summaryrefslogtreecommitdiffstats
path: root/libdvdread-embedded
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /libdvdread-embedded
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libdvdread-embedded')
-rw-r--r--libdvdread-embedded/.gitignore36
-rw-r--r--libdvdread-embedded/.gitlab-ci.yml56
-rw-r--r--libdvdread-embedded/AUTHORS49
-rw-r--r--libdvdread-embedded/COPYING340
-rw-r--r--libdvdread-embedded/Makefile.am80
-rw-r--r--libdvdread-embedded/NEWS220
-rw-r--r--libdvdread-embedded/README.md56
-rw-r--r--libdvdread-embedded/TODO12
-rw-r--r--libdvdread-embedded/configure.ac137
-rw-r--r--libdvdread-embedded/doc/doxygen.cfg.in1332
-rw-r--r--libdvdread-embedded/doc/footer.html2
-rw-r--r--libdvdread-embedded/doc/header.html9
-rw-r--r--libdvdread-embedded/m4/attributes.m4296
-rw-r--r--libdvdread-embedded/misc/dvdread.pc.in13
-rw-r--r--libdvdread-embedded/msvc/config.h56
-rw-r--r--libdvdread-embedded/msvc/contrib/dirent/dirent.c135
-rw-r--r--libdvdread-embedded/msvc/contrib/dirent/dirent.h32
-rw-r--r--libdvdread-embedded/msvc/contrib/dlfcn.c96
-rw-r--r--libdvdread-embedded/msvc/contrib/win32_cs.h40
-rw-r--r--libdvdread-embedded/msvc/ifo_dump.dsp110
-rw-r--r--libdvdread-embedded/msvc/include/dlfcn.h18
-rw-r--r--libdvdread-embedded/msvc/include/inttypes.h32
-rw-r--r--libdvdread-embedded/msvc/include/os_types.h24
-rw-r--r--libdvdread-embedded/msvc/include/sys/time.h28
-rw-r--r--libdvdread-embedded/msvc/include/unistd.h69
-rw-r--r--libdvdread-embedded/msvc/install/README7
-rw-r--r--libdvdread-embedded/msvc/libdvdcss.def12
-rw-r--r--libdvdread-embedded/msvc/libdvdcss.dsp139
-rw-r--r--libdvdread-embedded/msvc/libdvdnav.def73
-rw-r--r--libdvdread-embedded/msvc/libdvdnav.dsp188
-rw-r--r--libdvdread-embedded/msvc/libdvdnav.dsw101
-rw-r--r--libdvdread-embedded/msvc/libwin32utils.def77
-rw-r--r--libdvdread-embedded/msvc/libwin32utils.dsp121
-rw-r--r--libdvdread-embedded/msvc/play_title.dsp101
-rwxr-xr-xlibdvdread-embedded/msvc/scripts/libdvdcss_install.bat8
-rwxr-xr-xlibdvdread-embedded/msvc/scripts/libdvdnav_install.bat21
-rw-r--r--libdvdread-embedded/msvc/title_info.dsp101
-rw-r--r--libdvdread-embedded/src/bitreader.c163
-rw-r--r--libdvdread-embedded/src/bswap.h118
-rw-r--r--libdvdread-embedded/src/dvd_input.c483
-rw-r--r--libdvdread-embedded/src/dvd_input.h71
-rw-r--r--libdvdread-embedded/src/dvd_reader.c1690
-rw-r--r--libdvdread-embedded/src/dvd_udf.c1033
-rw-r--r--libdvdread-embedded/src/dvdread/bitreader.h40
-rw-r--r--libdvdread-embedded/src/dvdread/dvd_reader.h365
-rw-r--r--libdvdread-embedded/src/dvdread/dvd_udf.h59
-rw-r--r--libdvdread-embedded/src/dvdread/ifo_print.h28
-rw-r--r--libdvdread-embedded/src/dvdread/ifo_read.h229
-rw-r--r--libdvdread-embedded/src/dvdread/ifo_types.h755
-rw-r--r--libdvdread-embedded/src/dvdread/nav_print.h52
-rw-r--r--libdvdread-embedded/src/dvdread/nav_read.h53
-rw-r--r--libdvdread-embedded/src/dvdread/nav_types.h248
-rw-r--r--libdvdread-embedded/src/dvdread/version.h.in37
-rw-r--r--libdvdread-embedded/src/dvdread_internal.h86
-rw-r--r--libdvdread-embedded/src/ifo_print.c1209
-rw-r--r--libdvdread-embedded/src/ifo_read.c2341
-rw-r--r--libdvdread-embedded/src/logger.c41
-rw-r--r--libdvdread-embedded/src/logger.h32
-rw-r--r--libdvdread-embedded/src/md5.c346
-rw-r--r--libdvdread-embedded/src/md5.h46
-rw-r--r--libdvdread-embedded/src/nav_print.c248
-rw-r--r--libdvdread-embedded/src/nav_read.c272
62 files changed, 14272 insertions, 0 deletions
diff --git a/libdvdread-embedded/.gitignore b/libdvdread-embedded/.gitignore
new file mode 100644
index 0000000..d24b27c
--- /dev/null
+++ b/libdvdread-embedded/.gitignore
@@ -0,0 +1,36 @@
+*.la
+*.lo
+*.o
+*~
+.deps/
+.dirstamp
+.libs/
+/aclocal.m4
+/autom4te.cache/
+/compile
+/config.guess
+/config.h
+/config.h.in
+/config.log
+/config.status
+/config.sub
+/configure
+/depcomp
+/install-sh
+/libdvdread*.tar.bz2
+/libtool
+/ltmain.sh
+/m4/libtool.m4
+/m4/lt*.m4
+/misc/*.pc
+/misc/libdvdread.spec
+/missing
+/stamp-h1
+/ChangeLog
+/Makefile
+/Makefile.in
+/doc/doxygen.cfg
+/doc/html
+/stamp-*
+/build
+/releases
diff --git a/libdvdread-embedded/.gitlab-ci.yml b/libdvdread-embedded/.gitlab-ci.yml
new file mode 100644
index 0000000..6f808c7
--- /dev/null
+++ b/libdvdread-embedded/.gitlab-ci.yml
@@ -0,0 +1,56 @@
+stages:
+ - build
+
+variables:
+ GIT_SUBMODULE_STRATEGY: normal
+
+build-debian:
+ image: registry.videolan.org/vlc-debian-unstable:20200529132440
+ stage: build
+ tags:
+ - docker
+ - amd64
+ script:
+ - autoreconf -fisv
+ - mkdir build
+ - cd build
+ - ../configure
+ - make -j $(getconf _NPROCESSORS_ONLN)
+
+build-macos:
+ stage: build
+ tags:
+ - mojave
+ - amd64
+ script:
+ - autoreconf -fisv
+ - mkdir build
+ - cd build
+ - ../configure
+ - make -j $(getconf _NPROCESSORS_ONLN)
+
+build-win64:
+ image: registry.videolan.org/vlc-debian-win64:20201106143728
+ stage: build
+ tags:
+ - docker
+ - amd64
+ script:
+ - autoreconf -fisv
+ - mkdir build
+ - cd build
+ - ../configure --host=x86_64-w64-mingw32
+ - make -j $(getconf _NPROCESSORS_ONLN)
+
+build-win32:
+ image: registry.videolan.org/vlc-debian-win32:20201106141924
+ stage: build
+ tags:
+ - docker
+ - amd64
+ script:
+ - autoreconf -fisv
+ - mkdir build
+ - cd build
+ - ../configure --host=i686-w64-mingw32
+ - make -j $(getconf _NPROCESSORS_ONLN)
diff --git a/libdvdread-embedded/AUTHORS b/libdvdread-embedded/AUTHORS
new file mode 100644
index 0000000..19c5ad5
--- /dev/null
+++ b/libdvdread-embedded/AUTHORS
@@ -0,0 +1,49 @@
+Jean-Baptiste Kempf
+Erik Hovland
+Diego Biurrun
+Nico Sabbi
+Dominik Mierzejewski
+Steve Dibb
+Diego Elio Pettenò
+Petri Hintukainen
+John Stebbins
+Andrew Clayton
+KO Myung-Hun
+Thomas Guillem
+Dan Nicholson
+Reimar Döffinger
+Frédéric Marchal
+Paul Menzel
+Pierre Lamot
+Rafaël Carré
+Evgeny Grin
+Lawrence D'Oliveiro
+Marcel Mol
+Alexander Roalter
+Benjamin Kerensa
+Brad Smith
+Dominik 'Rathann' Mierzejewski
+Doug Springer
+Erik Auerswald
+Ganael Laplanche
+Ingo Brückl
+Jindrich Makovicka
+Jorgen Lundman
+Konstantin Pavlov
+LRN
+Mike Castle
+Mike Gorchak
+Nicolas Porcel
+Reinhard Tartler
+Richard Hulme
+Roger Pack
+Rémi Duraffort
+Thomas Klausner
+Thomas Petazzoni
+Timothy Gu
+Daniel Caujolle-Bert
+Frantisek Dvorak
+Kees Cook
+Michael Roitzsch
+Rich Wareha
+Thomas Vander Stichele
diff --git a/libdvdread-embedded/COPYING b/libdvdread-embedded/COPYING
new file mode 100644
index 0000000..a3e9774
--- /dev/null
+++ b/libdvdread-embedded/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/libdvdread-embedded/Makefile.am b/libdvdread-embedded/Makefile.am
new file mode 100644
index 0000000..f2849b8
--- /dev/null
+++ b/libdvdread-embedded/Makefile.am
@@ -0,0 +1,80 @@
+ACLOCAL_AMFLAGS = -Im4
+
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src/dvdread $(CSS_CFLAGS)
+
+dist_doc_DATA = AUTHORS ChangeLog COPYING NEWS README.md TODO
+
+MAINTAINERCLEANFILES = ChangeLog
+
+ChangeLog: $(wildcard $(srcdir)/.git/logs/HEAD)
+ -cd $(srcdir) && git log > $(abs_builddir)/$(@)-tmp
+ -test -s $(@)-tmp && mv $(@)-tmp $(@)
+ -rm -f $(@)-tmp
+ test -e $(@) || touch $(@)
+
+EXTRA_DIST = msvc doc/footer.html doc/header.html
+
+lib_LTLIBRARIES = libdvdread.la
+
+libdvdread_la_SOURCES = \
+ src/bitreader.c \
+ src/bswap.h \
+ src/dvd_input.c \
+ src/dvd_input.h \
+ src/dvd_reader.c \
+ src/dvd_udf.c \
+ src/dvdread_internal.h \
+ src/ifo_print.c \
+ src/ifo_read.c \
+ src/logger.c \
+ src/logger.h \
+ src/md5.c \
+ src/md5.h \
+ src/nav_print.c \
+ src/nav_read.c \
+ msvc/contrib/win32_cs.h
+
+libdvdread_la_LIBADD = $(CSS_LIBS)
+
+libdvdread_la_LDFLAGS = -version-info $(DVDREAD_LTVERSION) \
+ -export-symbols-regex "(^dvdread.*|^nav.*|^ifo.*|^DVD.*|^UDF.*)"
+
+pkgincludedir = $(includedir)/dvdread
+pkginclude_HEADERS = \
+ src/dvdread/bitreader.h \
+ src/dvdread/dvd_reader.h \
+ src/dvdread/dvd_udf.h \
+ src/dvdread/ifo_print.h \
+ src/dvdread/ifo_read.h \
+ src/dvdread/ifo_types.h \
+ src/dvdread/nav_print.h \
+ src/dvdread/nav_read.h \
+ src/dvdread/nav_types.h \
+ src/dvdread/version.h
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = misc/dvdread.pc
+
+if APIDOC
+all-local: apidoc
+install-data-local: install-apidoc
+uninstall-hook: uninstall-apidoc
+endif
+
+apidoc: stamp-doxygen
+
+stamp-doxygen: doc/doxygen.cfg
+ cd doc && doxygen doxygen.cfg
+ touch $@
+
+clean-local:
+ -rm -Rf stamp-doxygen doc/html
+
+install-apidoc:
+ $(MKDIR_P) $(DESTDIR)$(htmldir)
+ for file in doc/html/*; do \
+ $(INSTALL_DATA) $$file "$(DESTDIR)$(htmldir)" || exit 1; \
+ done;
+
+uninstall-apidoc:
+ -rm -Rf $(DESTDIR)$(htmldir)/*.png $(DESTDIR)$(htmldir)/*.html $(DESTDIR)$(htmldir)/*.css $(DESTDIR)$(htmldir)/*.js
diff --git a/libdvdread-embedded/NEWS b/libdvdread-embedded/NEWS
new file mode 100644
index 0000000..7c6733a
--- /dev/null
+++ b/libdvdread-embedded/NEWS
@@ -0,0 +1,220 @@
+libdvdread (6.1.3)
+ * Improvements for OpenBSD
+ * Fix crashes on some DVD (0 PCGI SRP)
+ * Misc source fixes and cleanups, including fixes for recent toolchains
+
+libdvdread (6.1.2)
+ * Fix Win32 Unicode paths opening
+ * Fix warnings and build improvements
+
+libdvdread (6.1.1)
+ * Fix the soversion following ABI breakage
+
+libdvdread (6.1.0)
+ * Introduce DVDOpen2 and DVDOpenStream2 API to provide logger context
+ * fix playback of broken discs with broken ifo files
+ * improve getmntent_r detection
+ * fix crashes in some disc, with empty contiguous ShortAD
+ * clean obsolete code
+
+libdvdread (6.0.2)
+ * use reentrant getmntent_r
+ * fix playback of some discs, by fixing pgc_t structure
+
+libdvdread (6.0.1)
+ * fix a regression on some DVDs, like Resident Evil
+ * check InternalUDFReadBlocksRaw I/O issues
+ * fix issues with some DVD burnt by Nero
+
+libdvdread (6.0.0)
+ * restrict the number of symbols to be exposed to the shared-object
+ * remove dvdinput_error function
+ * improve compatibility with some DVDs (notably the eOne ones)
+ * fix write after free in ifoFree functions
+ * fix possible buffer overflow in open
+ * additional checks on DVDReadBytes arguments
+ * fix leaks
+
+libdvdread (5.0.3)
+ * cache IFO and BUP files (only for ImageFiles) in order to reduce the number
+ of read/seek access
+ * fix compilation warnings for OS/2
+
+libdvdread (5.0.2)
+ * add DVDOpenStream to open a dvd from a set of callbacks
+
+libdvdread (5.0.1)
+ * support DragonFly BSD bswap
+ * new md5 implementation, under LGPL
+ * fix partial reads
+ * fix warnings and code cleanup
+
+libdvdread (5.0.0)
+ * added support for Android
+ * fix build support for OS/2
+ * fix crashes in IFO parsing (Shark Week The Great Bites Collection)
+
+libdvdread (4.9.9)
+ * major cleanup
+ * rewritten build system, including removal of configure2 and dvdread-config
+ * fix numerous crashes
+ * added QNX support
+ * updated to support libdvdcss 1.3.0
+ * improved dlopen support for Win32
+ * simplify static linking of libdvdcss
+
+libdvdread (4.2.1)
+ * fix huge memory consumption when reading DVDs: "Up" and "Dark Knight"
+ * numerous crashes fixed, notably on ARCCOS protected DVDs,
+ like Transformers 3, Brave, The Express, Sherlock Holmes
+ and Cars 2,
+ * fix packed structures with mingw gcc >= 4.7
+
+libdvdread (4.2.0)
+ * added OS/2 support
+ * fixed ARM memory alignment issue
+ * portability fixes for *BSD, Mac OS X, MINGW and OS/2
+ * added a missing DVDFileStat function
+ * exposed the dvd_stat_t struct in header
+ * fixed libdvdread closing stdin if it can't open the file/device
+ * fixed a segfault in UDFFileEntry on some badly mastered DVDs
+ * fixed big-endian builds
+ * Win32: large file support under Mingw to go over 2G limits
+ * configure: remove the special case test for uid=0
+ * provide BUP file support for more issues
+ * move UDFReadBlocksRaw declaration to dvdread_internal.h
+ * fixed double free of parental management information
+ * configure2: Use a single = instead of ==
+ * fixed crash when PTT is too short
+ * fixed segfault when reading certain DVDs, for example "Thor"
+
+libdvdread (4.1.3)
+ * an embarrassing amount of fixes regarding potential memory and resource leaks
+ (patches contributed by Erik Hovland)
+ * added dvdread-config (dvdnav-config's younger brother)
+ * added pkgconfig support
+ * split dvdread to a separate tree
+
+libdvdnav (4.1.2)
+ * multiple build system fixes
+ * added dvdnav_describe_title_chapters(title) to get title and chapters
+ duration
+
+libdvdnav (4.1.1)
+ * added dvdnav_audio_stream_channels() to return number of channels
+ * fixed dvdnav_time_search() in multi-angle dvds (but it still needs
+ improvements)
+ * added dvdnav_audio_stream_format() to identify the codec used
+ in audio streams
+ * starting DVD playback at specific title/part positions with
+ dvdnav_{title,part}_play() works again
+ * removed wrong SPU stream change event filter
+ (fixes unwanted subtitles in the trailer of "Girl, interrupted", RC2)
+ * fixed error "Expected NAV packet but none found." occurring sometimes
+ on resume from menu
+
+libdvdnav (0.1.10)
+ * filter the symbols that we export.
+ * fix LinkNextC assertion failure (fixes LotR-SEE bonus disc image gallery)
+ * detect zero stilltime still cells inside PGCs, not only at the end
+ (fixes "Red Dragon" RC2 scene selection)
+ * PGC stills seem to work, assertion removed
+ * fix rare race condition after Exit commands
+ * fix wrong JumpSS_VTSM execution in German RC2 "Anatomie"
+ (fix ported from Ogle)
+
+libdvdnav (0.1.9)
+ * libdvdnav does not depend on libdvdread any more. It has it's own version.
+ * fix some situations where an unlucky user could trigger assertions
+
+libdvdnav (0.1.8)
+ * more timing info in cell change event struct
+ * documentation review
+
+libdvdnav (0.1.7)
+ * fixed a bug in title jumping, where the title number would not be
+ converted from TTN to VTS_TTN properly
+ * some minor sanity checks added to prevent segfaults
+
+libdvdnav (0.1.6) unstable; urgency=low
+ * new event DVDNAV_WAIT to fix consistency problems in applications with fifos
+ where libdvdnav is always a bit ahead in the stream, the event forces
+ the application to wait for its fifos to get empty
+ * correct HIGHLIGHT reporting when a button is activated
+ * method to try-run VM operations, now used for safer chapter skipping and menu jumps
+ * fixed detection of current PTT to not assume a 1:1 mapping between PTTs and PGs
+ * releasing stills when jumping to menu fixes some state inconsistencies
+ * do not assume PGs to be physically laid out in sequence on the disc
+ * optional PGC based seeking
+ * new event on cell changes for timing info
+
+libdvdnav (0.1.5) unstable; urgency=low
+ * some bugfixes
+ * code cleanup
+ * build process polishing
+ * more sensible event order in get_next_block to ensure useful event delivery
+ * VOBU level resume
+ * fixed: seeking in a multiangle feature briefly showed the wrong angle
+
+libdvdnav (0.1.4) unstable; urgency=low
+ * more read cache improvements
+ * minor fixes for some problematic DVDs
+
+libdvdnav (0.1.3-1) unstable; urgency=low
+ * Zero-copy read cache.
+ * More support for alternative Menu languages.
+
+ -- Rich Wareham <richwareham@users.sourceforge.net> Fri, 2 Aug 2002 08:52:24 +0100
+
+libdvdnav (0.1.2-1) unstable; urgency=low
+ * Read Cache changes. Recommended setting for read_cache is OFF.
+ Unless one's DVD drive has too small a buffer.
+ * Should work with xine 0.9.10 or above.
+
+ -- James Courtier-Dutton <jcdutton@users.sourceforge.net> Sun, 3 Jul 2002 15:30:00 +0000
+
+libdvdnav (0.1.1-1) unstable; urgency=low
+
+ * New upstream version. (closes: #148495)
+ * Include TODO
+ * Fix config.h problem
+ * Threaded cache
+
+ -- Philipp Matthias Hahn <pmhahn@titan.lahn.de> Sat, 1 Jun 2002 17:47:59 +0200
+
+libdvdnav (0.1.0-2) unstable; urgency=low
+
+ * Add manual page dvdnav-config.1
+ * Add bug-presubj on Daniel's request
+ * Get dvdnav.c:1.17 from CVS to fix angle support
+ * Merge patch from Jamie Wilkinson (#146699)
+ * Rerun automake to fix dependencies
+ * Ack NMU from siggi
+ * Fix include in examples/menus.c
+
+ -- Philipp Hahn <pmhahn@titan.lahn.de> Thu, 23 May 2002 09:41:15 +0200
+
+libdvdnav (0.1.0-1.1) unstable; urgency=low
+
+ * Prepared for first 'real' release.
+ * Bug fixes
+ * Changes to allow apps to 'roll-their-own' dvdnav_get_next_block functions.
+ * NMU in order to get xine-dvdnav running again
+ - changed package name to libdvdnav0
+ (see patch from Jamie Wilkinson for a better solution)
+
+ -- Siggi Langauf <siggi@debian.org> Mon, 20 May 2002 15:57:40 +0200
+
+libdvdnav (0.0.1-1) unstable; urgency=low
+
+ * Repackaged using dh-make.
+
+ -- Philipp Matthias Hahn <pmhahn@titan.lahn.de> Sun, 7 Apr 2002 16:29:35 +0200
+
+libdvdnav (0.0.1) unstable; urgency=low
+
+ * Initial release.
+ * Split from xine-dvdnav
+
+ -- rjw57 <rjw57@hermes.cam.ac.uk> Tue, 12 Mar 2002 19:41:13 +0000
+
diff --git a/libdvdread-embedded/README.md b/libdvdread-embedded/README.md
new file mode 100644
index 0000000..6227d8d
--- /dev/null
+++ b/libdvdread-embedded/README.md
@@ -0,0 +1,56 @@
+# Goals and features
+
+**libdvdread** is a library for simple navigation of DVD (DVDs without menus).
+
+Written in C, cross-platform, it gives low-level access to DVD structures.
+
+It works well in conjunction with ***libdvdnav*** *(menus)* and ***libdvdcss*** *(cipher)*.
+
+## Where does it come from?
+
+This library is based on a lot of code and expertise from the **Ogle project**.
+**Ogle** was the first DVD player who implemented **free DVD navigation**. The
+**libdvdread** developers wish to express their gratitude to the Ogle people
+for all the valuable research work they have done.
+
+Initially, the dvdnav code was part of a plugin to the xine media player
+called xine-dvdnav. Later on, the DVD VM specific code was split
+from xine-dvdnav and went into the first version of libdvdnav.
+
+Then, it was forked, and forked again on MPlayer repositories.
+libdvdnav and libdvdread were merged, and then split again.
+
+## Where is it now?
+
+Libdvdread is hosted [here](https://code.videolan.org/videolan/libdvdread).
+
+Libdvdnav is hosted [here](https://code.videolan.org/videolan/libdvdnav).
+
+You can find more information [here](https://www.videolan.org/developers/libdvdnav.html)
+
+Please report bugs to the developers mailinglist at
+[dvdnav mailing list](https://mailman.videolan.org/listinfo/libdvdnav-devel)
+
+## License
+
+**Libdvdread** is completely licensed under GPLv2/v3. You may use it at wish within the
+bounds of this license. See the file [COPYING](https://code.videolan.org/videolan/libdvdread/-/blob/master/COPYING) for a copy of the GPL.
+
+## Using libdvdread
+
+A detailed description of DVD structures is available [here](http://www.mpucoder.com/dvd/)
+
+All documentation is also accessible [here](http://dvdnav.mplayerhq.hu/#docs)
+
+## Deciphering disks
+
+**Libdvdread** does not do any decryption of the CSS algorithm. This task can be delegated to **libdvdcss**.
+
+Install *libdvdcss* from source or from your distribution (*libdvd-pkg*) to play full DVDs,
+if your country allows this to work legally.
+
+Note that *libdvdnav* is useful for interactive DVD menus.
+
+## CoC
+
+The [VideoLAN Code of Conduct](https://wiki.videolan.org/Code_of_Conduct/) applies to this project.
diff --git a/libdvdread-embedded/TODO b/libdvdread-embedded/TODO
new file mode 100644
index 0000000..6f624ae
--- /dev/null
+++ b/libdvdread-embedded/TODO
@@ -0,0 +1,12 @@
+* Support DVDs with errors on them. So we can recover from corrupt sectors in
+ the .VOB. Be able to read the VAT ICB at the last sector of the UDF disk (see
+ function UDFGetAVDP)
+* Support Random and Shuffle Titles. Only sequential Titles are currently supported.
+* rework documentation
+* implement restriction levels:
+ 0 - execute everything as the app commands
+ 1 - do some sensible sanity checking
+ 2 - be more careful, when operations are prohibited (like not seeking/jumping in the presence of stills or cell commands)
+ 3 - fully respect user prohibitions
+* cleanup public API and fix libtool versioning
+* Update decoder.c with some of the more rare commands. Update already done to vmcmd.c
diff --git a/libdvdread-embedded/configure.ac b/libdvdread-embedded/configure.ac
new file mode 100644
index 0000000..a60ef0c
--- /dev/null
+++ b/libdvdread-embedded/configure.ac
@@ -0,0 +1,137 @@
+dnl library version number
+m4_define([dvdread_major], 6)
+m4_define([dvdread_minor], 1)
+m4_define([dvdread_micro], 3)
+m4_define([dvdread_version],[dvdread_major.dvdread_minor.dvdread_micro])
+
+AC_INIT(libdvdread, dvdread_version)
+
+AC_CONFIG_SRCDIR([src/dvd_reader.c])
+
+AC_PREREQ(2.53)
+AC_CANONICAL_HOST
+
+AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip subdir-objects])
+AM_MAINTAINER_MODE([enable])
+dnl Enable silent rules only when available (automake 1.11 or later).
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+LT_INIT
+
+AC_CONFIG_HEADER(config.h)
+AC_CONFIG_MACRO_DIR([m4])
+
+dnl The libtool version numbers (DVDREAD_LT_*); Don't even think about faking this!
+dnl
+dnl immediately before every release do:
+dnl ===================================
+dnl if (the interface is totally unchanged from previous release)
+dnl DVDREAD_LT_REVISION ++;
+dnl else { /* interfaces have been added, removed or changed */
+dnl DVDREAD_LT_REVISION = 0;
+dnl DVDREAD_LT_CURRENT ++;
+dnl if (any interfaces have been _added_ since last release)
+dnl AGE ++;
+dnl if (any interfaces have been _removed_ or _incompatibly changed_)
+dnl AGE = 0;
+dnl }
+dnl
+dnl If you want to know more about what you are doing, here are some details:
+dnl * DVDREAD_LT_CURRENT is the current API version
+dnl * DVDREAD_LT_REVISION is an internal revision number which is increased when the API
+dnl itself did not change
+dnl * DVDREAD_LT_AGE is the number of previous API versions still supported by this library
+dnl * libtool has its own numbering scheme, because local library numbering schemes
+dnl are platform dependent
+dnl * in Linux, the library will be named
+dnl libname.so.(DVDREAD_LT_CURRENT - DVDREAD_LT_AGE).DVDREAD_LT_REVISION.DVDREAD_LT_AGE
+
+DVDREAD_LT_CURRENT=8
+DVDREAD_LT_AGE=0
+DVDREAD_LT_REVISION=0
+
+AC_SUBST([DVDREAD_LTVERSION], [$DVDREAD_LT_CURRENT:$DVDREAD_LT_REVISION:$DVDREAD_LT_AGE])
+
+AC_PROG_CC_C99
+
+AC_CHECK_HEADERS_ONCE([sys/param.h limits.h dlfcn.h])
+
+AC_SYS_LARGEFILE
+AC_C_BIGENDIAN
+
+AS_CASE([$host],
+ [*mingw32* | *cygwin*], [AC_CHECK_FUNCS(gettimeofday)])
+
+AS_CASE([$host],
+ [*-os2-*], LDFLAGS="-no-undefined -Zbin-files $LDFLAGS")
+
+AS_CASE([$host],
+ [*-linux-*], [AC_CHECK_FUNCS([getmntent_r])])
+
+AC_ARG_WITH([libdvdcss],
+ AS_HELP_STRING([--with-libdvdcss], [Link directly against libdvdcss @<:@default=no@:>@]))
+
+AC_ARG_ENABLE([dlfcn],
+ [AS_HELP_STRING([--enable-dlfcn],
+ [use builtin dlfcn for mingw (default is auto)])],
+ [use_builtin_dlfcn=$enableval],
+ [use_builtin_dlfcn=no])
+
+AS_IF([test x"$with_libdvdcss" = "xyes"], [
+ CSS_REQUIRES="libdvdcss >= 1.2"
+ PKG_CHECK_MODULES([CSS], [$CSS_REQUIRES])
+ AC_CHECK_HEADERS(dvdcss/dvdcss.h,, AC_MSG_ERROR(You need libdvdcss (dvdcss.h)))
+], [
+ AS_CASE([$host],
+ [*mingw32*], [],
+ [use_builtin_dlfcn=no])
+
+ AS_IF([test $use_builtin_dlfcn = "yes"], [
+ AC_DEFINE([USING_BUILTIN_DLFCN], [1], ["Define to 1 to use builtin dlfcn"])
+ ], [
+ AC_SEARCH_LIBS([dlopen], [dl])
+ ])
+])
+AC_SUBST([CSS_REQUIRES])
+
+CC_CHECK_CFLAGS_APPEND([-Wall -Wsign-compare -Wextra])
+
+
+AC_ARG_ENABLE([apidoc],
+ AS_HELP_STRING([--disable-apidoc], [Disable building (with Doxygen) and installing API documentation @<:@default=auto@:@>]))
+
+AC_PATH_PROG([DOXYGEN], [doxygen])
+
+AS_IF([test "x$DOXYGEN" = "x"], [
+ AS_IF([test "x$enable_apidoc" = "xyes"], [
+ AC_MSG_ERROR([You need Doxygen to build API documentation])
+ ])
+])
+AM_CONDITIONAL([APIDOC], [test "x$DOXYGEN" != "x" && test "x$enable_apidoc" = "xyes"])
+
+AS_IF([test "x$ac_cv_c_compiler_gnu" = "xyes"], [
+ AC_DEFINE([UNUSED], [__attribute__((unused))], [Unused parameter annotation])
+], [
+ AC_DEFINE([UNUSED], [], [Unused parameter annotation])
+])
+
+AC_FUNC_STRERROR_R
+AS_IF([test "x$ac_cv_func_strerror_r" = "xyes"], [], [AC_CHECK_FUNCS([strerror_s])])
+
+dnl export library version number
+DVDREAD_VERSION_MAJOR=dvdread_major()
+DVDREAD_VERSION_MINOR=dvdread_minor()
+DVDREAD_VERSION_MICRO=dvdread_micro()
+AC_SUBST(DVDREAD_VERSION_MAJOR)
+AC_SUBST(DVDREAD_VERSION_MINOR)
+AC_SUBST(DVDREAD_VERSION_MICRO)
+
+dnl ---------------------------------------------
+dnl Output configuration files
+dnl ---------------------------------------------
+AC_OUTPUT([
+Makefile
+doc/doxygen.cfg
+misc/dvdread.pc
+src/dvdread/version.h
+])
diff --git a/libdvdread-embedded/doc/doxygen.cfg.in b/libdvdread-embedded/doc/doxygen.cfg.in
new file mode 100644
index 0000000..8a89a30
--- /dev/null
+++ b/libdvdread-embedded/doc/doxygen.cfg.in
@@ -0,0 +1,1332 @@
+# Doxyfile 1.5.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = @PACKAGE_NAME
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @PACKAGE_VERSION@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = @top_builddir@/doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
+# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish,
+# and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page. This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = @top_srcdir@/src
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.c \
+ *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE = ../src/config.h
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS = *_private.h
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER = @top_srcdir@/doc/header.html
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER = @top_srcdir@/doc/footer.html
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = YES
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to FRAME, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature. Other possible values
+# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
+# and Class Hiererachy pages using a tree view instead of an ordered list;
+# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
+# disables this behavior completely. For backwards compatibility with previous
+# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
+# respectively.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = _DOXYGEN_SKIP_ME
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS = .
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is enabled by default, which results in a transparent
+# background. Warning: Depending on the platform used, enabling this option
+# may lead to badly anti-aliased labels on the edges of a graph (i.e. they
+# become hard to read).
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/libdvdread-embedded/doc/footer.html b/libdvdread-embedded/doc/footer.html
new file mode 100644
index 0000000..b605728
--- /dev/null
+++ b/libdvdread-embedded/doc/footer.html
@@ -0,0 +1,2 @@
+ </body>
+</html>
diff --git a/libdvdread-embedded/doc/header.html b/libdvdread-embedded/doc/header.html
new file mode 100644
index 0000000..4b43b61
--- /dev/null
+++ b/libdvdread-embedded/doc/header.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+ <title>libdvdread documentation</title>
+ <link href="/main.css" rel="stylesheet" type="text/css">
+ <link href="/doxygen.css" rel="stylesheet" type="text/css">
+ </head>
+ <body>
diff --git a/libdvdread-embedded/m4/attributes.m4 b/libdvdread-embedded/m4/attributes.m4
new file mode 100644
index 0000000..39d02e3
--- /dev/null
+++ b/libdvdread-embedded/m4/attributes.m4
@@ -0,0 +1,296 @@
+dnl Macros to check the presence of generic (non-typed) symbols.
+dnl Copyright (c) 2006-2007 Diego Pettenò <flameeyes@gmail.com>
+dnl Copyright (c) 2006-2007 xine project
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2, or (at your option)
+dnl any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+dnl 02110-1301, USA.
+dnl
+dnl As a special exception, the copyright owners of the
+dnl macro gives unlimited permission to copy, distribute and modify the
+dnl configure scripts that are the output of Autoconf when processing the
+dnl Macro. You need not follow the terms of the GNU General Public
+dnl License when using or distributing such scripts, even though portions
+dnl of the text of the Macro appear in them. The GNU General Public
+dnl License (GPL) does govern all other use of the material that
+dnl constitutes the Autoconf Macro.
+dnl
+dnl This special exception to the GPL applies to versions of the
+dnl Autoconf Macro released by this project. When you make and
+dnl distribute a modified version of the Autoconf Macro, you may extend
+dnl this special exception to the GPL to apply to your modified version as
+dnl well.
+
+dnl Check if the flag is supported by compiler
+dnl CC_CHECK_CFLAGS_SILENT([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
+
+AC_DEFUN([CC_CHECK_CFLAGS_SILENT], [
+ AC_CACHE_VAL(AS_TR_SH([cc_cv_cflags_$1]),
+ [ac_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $1"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([int main() { return 0; }])],
+ [eval "AS_TR_SH([cc_cv_cflags_$1])='yes'"],
+ [eval "AS_TR_SH([cc_cv_cflags_$1])='no'"])
+ CFLAGS="$ac_save_CFLAGS"
+ ])
+
+ AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes],
+ [$2], [$3])
+])
+
+dnl Check if the flag is supported by compiler (cacheable)
+dnl CC_CHECK_CFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
+
+AC_DEFUN([CC_CHECK_CFLAGS], [
+ AC_CACHE_CHECK([if $CC supports $1 flag],
+ AS_TR_SH([cc_cv_cflags_$1]),
+ CC_CHECK_CFLAGS_SILENT([$1]) dnl Don't execute actions here!
+ )
+
+ AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes],
+ [$2], [$3])
+])
+
+dnl CC_CHECK_CFLAG_APPEND(FLAG, [action-if-found], [action-if-not-found])
+dnl Check for CFLAG and appends them to CFLAGS if supported
+AC_DEFUN([CC_CHECK_CFLAG_APPEND], [
+ AC_CACHE_CHECK([if $CC supports $1 flag],
+ AS_TR_SH([cc_cv_cflags_$1]),
+ CC_CHECK_CFLAGS_SILENT([$1]) dnl Don't execute actions here!
+ )
+
+ AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes],
+ [CFLAGS="$CFLAGS $1"; $2], [$3])
+])
+
+dnl CC_CHECK_CFLAGS_APPEND([FLAG1 FLAG2], [action-if-found], [action-if-not])
+AC_DEFUN([CC_CHECK_CFLAGS_APPEND], [
+ for flag in $1; do
+ CC_CHECK_CFLAG_APPEND($flag, [$2], [$3])
+ done
+])
+
+dnl Check if the flag is supported by linker (cacheable)
+dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
+
+AC_DEFUN([CC_CHECK_LDFLAGS], [
+ AC_CACHE_CHECK([if $CC supports $1 flag],
+ AS_TR_SH([cc_cv_ldflags_$1]),
+ [ac_save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $1"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([int main() { return 1; }])],
+ [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"],
+ [eval "AS_TR_SH([cc_cv_ldflags_$1])="])
+ LDFLAGS="$ac_save_LDFLAGS"
+ ])
+
+ AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes],
+ [$2], [$3])
+])
+
+dnl Check for a -Werror flag or equivalent. -Werror is the GCC
+dnl and ICC flag that tells the compiler to treat all the warnings
+dnl as fatal. We usually need this option to make sure that some
+dnl constructs (like attributes) are not simply ignored.
+dnl
+dnl Other compilers don't support -Werror per se, but they support
+dnl an equivalent flag:
+dnl - Sun Studio compiler supports -errwarn=%all
+AC_DEFUN([CC_CHECK_WERROR], [
+ AC_CACHE_CHECK(
+ [for $CC way to treat warnings as errors],
+ [cc_cv_werror],
+ [CC_CHECK_CFLAGS_SILENT([-Werror], [cc_cv_werror=-Werror],
+ [CC_CHECK_CFLAGS_SILENT([-errwarn=%all], [cc_cv_werror=-errwarn=%all])])
+ ])
+])
+
+AC_DEFUN([CC_CHECK_ATTRIBUTE], [
+ AC_REQUIRE([CC_CHECK_WERROR])
+ AC_CACHE_CHECK([if $CC supports __attribute__(( ifelse([$2], , [$1], [$2]) ))],
+ AS_TR_SH([cc_cv_attribute_$1]),
+ [ac_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $cc_cv_werror"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])],
+ [eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"],
+ [eval "AS_TR_SH([cc_cv_attribute_$1])='no'"])
+ CFLAGS="$ac_save_CFLAGS"
+ ])
+
+ AS_IF([eval test x$]AS_TR_SH([cc_cv_attribute_$1])[ = xyes],
+ [AC_DEFINE(
+ AS_TR_CPP([SUPPORT_ATTRIBUTE_$1]), 1,
+ [Define this if the compiler supports __attribute__(( ifelse([$2], , [$1], [$2]) ))]
+ )
+ $4],
+ [$5])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [
+ CC_CHECK_ATTRIBUTE(
+ [constructor],,
+ [extern void foo();
+ void __attribute__((constructor)) ctor() { foo(); }],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_DESTRUCTOR], [
+ CC_CHECK_ATTRIBUTE(
+ [destructor],,
+ [extern void foo();
+ void __attribute__((destructor)) dtor() { foo(); }],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_FORMAT], [
+ CC_CHECK_ATTRIBUTE(
+ [format], [format(printf, n, n)],
+ [void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { fmt = (void *)0; }],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_FORMAT_ARG], [
+ CC_CHECK_ATTRIBUTE(
+ [format_arg], [format_arg(printf)],
+ [char *__attribute__((format_arg(1))) gettextlike(const char *fmt) { fmt = (void *)0; }],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [
+ CC_CHECK_ATTRIBUTE(
+ [visibility_$1], [visibility("$1")],
+ [void __attribute__((visibility("$1"))) $1_function() { }],
+ [$2], [$3])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_NONNULL], [
+ CC_CHECK_ATTRIBUTE(
+ [nonnull], [nonnull()],
+ [void __attribute__((nonnull())) some_function(void *foo, void *bar) { foo = (void*)0; bar = (void*)0; }],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_UNUSED], [
+ CC_CHECK_ATTRIBUTE(
+ [unused], ,
+ [void some_function(void *foo, __attribute__((unused)) void *bar);],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_SENTINEL], [
+ CC_CHECK_ATTRIBUTE(
+ [sentinel], ,
+ [void some_function(void *foo, ...) __attribute__((sentinel));],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_DEPRECATED], [
+ CC_CHECK_ATTRIBUTE(
+ [deprecated], ,
+ [void some_function(void *foo, ...) __attribute__((deprecated));],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_ALIAS], [
+ CC_CHECK_ATTRIBUTE(
+ [alias], [weak, alias],
+ [void other_function(void *foo) { }
+ void some_function(void *foo) __attribute__((weak, alias("other_function")));],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_MALLOC], [
+ CC_CHECK_ATTRIBUTE(
+ [malloc], ,
+ [void * __attribute__((malloc)) my_alloc(int n);],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_PACKED], [
+ CC_CHECK_ATTRIBUTE(
+ [packed], ,
+ [struct astructure { char a; int b; long c; void *d; } __attribute__((packed));
+ char assert@<:@(sizeof(struct astructure) == (sizeof(char)+sizeof(int)+sizeof(long)+sizeof(void*)))-1@:>@;],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_CONST], [
+ CC_CHECK_ATTRIBUTE(
+ [const], ,
+ [int __attribute__((const)) twopow(int n) { return 1 << n; } ],
+ [$1], [$2])
+])
+
+AC_DEFUN([CC_FLAG_VISIBILITY], [
+ AC_REQUIRE([CC_CHECK_WERROR])
+ AC_CACHE_CHECK([if $CC supports -fvisibility=hidden],
+ [cc_cv_flag_visibility],
+ [cc_flag_visibility_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $cc_cv_werror"
+ CC_CHECK_CFLAGS_SILENT([-fvisibility=hidden],
+ cc_cv_flag_visibility='yes',
+ cc_cv_flag_visibility='no')
+ CFLAGS="$cc_flag_visibility_save_CFLAGS"])
+
+ AS_IF([test "x$cc_cv_flag_visibility" = "xyes"],
+ [AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1,
+ [Define this if the compiler supports the -fvisibility flag])
+ $1],
+ [$2])
+])
+
+AC_DEFUN([CC_FUNC_EXPECT], [
+ AC_REQUIRE([CC_CHECK_WERROR])
+ AC_CACHE_CHECK([if compiler has __builtin_expect function],
+ [cc_cv_func_expect],
+ [ac_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $cc_cv_werror"
+ AC_COMPILE_IFELSE(
+ [int some_function() {
+ int a = 3;
+ return (int)__builtin_expect(a, 3);
+ }],
+ [cc_cv_func_expect=yes],
+ [cc_cv_func_expect=no])
+ CFLAGS="$ac_save_CFLAGS"
+ ])
+
+ AS_IF([test "x$cc_cv_func_expect" = "xyes"],
+ [AC_DEFINE([SUPPORT__BUILTIN_EXPECT], 1,
+ [Define this if the compiler supports __builtin_expect() function])
+ $1],
+ [$2])
+])
+
+AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [
+ AC_REQUIRE([CC_CHECK_WERROR])
+ AC_CACHE_CHECK([highest __attribute__ ((aligned ())) supported],
+ [cc_cv_attribute_aligned],
+ [ac_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $cc_cv_werror"
+ for cc_attribute_align_try in 64 32 16 8 4 2; do
+ AC_COMPILE_IFELSE([
+ int main() {
+ static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0;
+ return c;
+ }], [cc_cv_attribute_aligned=$cc_attribute_align_try; break])
+ done
+ CFLAGS="$ac_save_CFLAGS"
+ ])
+
+ if test "x$cc_cv_attribute_aligned" != "x"; then
+ AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX], [$cc_cv_attribute_aligned],
+ [Define the highest alignment supported])
+ fi
+])
diff --git a/libdvdread-embedded/misc/dvdread.pc.in b/libdvdread-embedded/misc/dvdread.pc.in
new file mode 100644
index 0000000..2e8f66a
--- /dev/null
+++ b/libdvdread-embedded/misc/dvdread.pc.in
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdvdread
+Description: Low level DVD access library
+Version: @VERSION@
+
+Requires.private: @CSS_REQUIRES@
+
+Cflags: -I${includedir}
+Libs: -L${libdir} -ldvdread
diff --git a/libdvdread-embedded/msvc/config.h b/libdvdread-embedded/msvc/config.h
new file mode 100644
index 0000000..4e9b60e
--- /dev/null
+++ b/libdvdread-embedded/msvc/config.h
@@ -0,0 +1,56 @@
+/* config.h. Generated by hand. */
+
+#ifndef LIBDVDREAD_CONFIG_H
+#define LIBDVDREAD_CONFIG_H
+
+#define HAVE_DLFCN_H 1
+/* #undef HAVE_DVDCSS_DVDCSS_H*/
+/* #undef HAVE_INTTYPES_H */
+#define HAVE_MEMORY_H 1
+/* #undef HAVE_STDINT_H */
+#define HAVE_STDLIB_H 1
+#define HAVE_STRINGS_H 1
+#define HAVE_STRING_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TYPES_H 1
+/* #undef HAVE_UNISTD_H */
+#define PACKAGE "libdvdread"
+#define PACKAGE_BUGREPORT ""
+#define PACKAGE_NAME ""
+#define PACKAGE_STRING ""
+#define PACKAGE_TARNAME ""
+#define PACKAGE_VERSION ""
+#define STDC_HEADERS 1
+#define VERSION "1.2.6"
+/* #undef WORDS_BIGENDIAN */
+/* #undef __DARWIN__ */
+/* #undef const */
+#define inline __inline
+/* #undef size_t */
+
+#define ssize_t __int64
+
+#ifndef PATH_MAX
+#define PATH_MAX MAX_PATH
+#endif
+
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+
+#define S_ISDIR(m) ((m) & _S_IFDIR)
+#define S_ISREG(m) ((m) & _S_IFREG)
+#define S_ISBLK(m) 0
+#define S_ISCHR(m) 0
+
+/* Fallback types (very x86-centric, sorry) */
+typedef unsigned char uint8_t;
+typedef signed char int8_t;
+typedef unsigned short uint16_t;
+typedef signed short int16_t;
+typedef unsigned int uint32_t;
+typedef signed int int32_t;
+typedef unsigned __int64 uint64_t;
+typedef signed __int64 int64_t;
+typedef unsigned int uintptr_t;
+
+#endif /* LIBDVDREAD_CONFIG_H */
diff --git a/libdvdread-embedded/msvc/contrib/dirent/dirent.c b/libdvdread-embedded/msvc/contrib/dirent/dirent.c
new file mode 100644
index 0000000..0eddd90
--- /dev/null
+++ b/libdvdread-embedded/msvc/contrib/dirent/dirent.c
@@ -0,0 +1,135 @@
+/*
+
+ Implementation of POSIX directory browsing functions and types for Win32.
+
+ Kevlin Henney (mailto:kevlin@acm.org), March 1997.
+
+ Copyright Kevlin Henney, 1997. All rights reserved.
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation for any purpose is hereby granted without fee, provided
+ that this copyright and permissions notice appear in all copies and
+ derivatives, and that no charge may be made for the software and its
+ documentation except to cover cost of distribution.
+
+ This software is supplied "as is" without express or implied warranty.
+
+ But that said, if there are any problems please get in touch.
+
+*/
+
+#include <dirent.h>
+#include <errno.h>
+#include <io.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef DIR
+
+struct DIR
+{
+ long handle; /* -1 for failed rewind */
+ struct _finddata_t info;
+ struct dirent result; /* d_name null iff first time */
+ char *name; /* NTBS */
+};
+
+#endif
+
+DIR *opendir(const char *name)
+{
+ DIR *dir = 0;
+
+ if(name && name[0])
+ {
+ size_t base_length = strlen(name);
+ const char *all = /* the root directory is a special case... */
+ strchr("/\\", name[base_length - 1]) ? "*" : "/*";
+
+ if((dir = malloc(sizeof *dir)) != 0 &&
+ (dir->name = malloc(base_length + strlen(all) + 1)) != 0)
+ {
+ strcat(strcpy(dir->name, name), all);
+
+ if((dir->handle = _findfirst(dir->name, &dir->info)) != -1)
+ {
+ dir->result.d_name = 0;
+ }
+ else /* rollback */
+ {
+ free(dir->name);
+ free(dir);
+ dir = 0;
+ }
+ }
+ else /* rollback */
+ {
+ free(dir);
+ dir = 0;
+ errno = ENOMEM;
+ }
+ }
+ else
+ {
+ errno = EINVAL;
+ }
+
+ return dir;
+}
+
+int closedir(DIR *dir)
+{
+ int result = -1;
+
+ if(dir)
+ {
+ if(dir->handle != -1)
+ {
+ result = _findclose(dir->handle);
+ }
+
+ free(dir->name);
+ free(dir);
+ }
+
+ if(result == -1) /* map all errors to EBADF */
+ {
+ errno = EBADF;
+ }
+
+ return result;
+}
+
+struct dirent *readdir(DIR *dir)
+{
+ struct dirent *result = 0;
+
+ if(dir && dir->handle != -1)
+ {
+ if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1)
+ {
+ result = &dir->result;
+ result->d_name = dir->info.name;
+ }
+ }
+ else
+ {
+ errno = EBADF;
+ }
+
+ return result;
+}
+
+void rewinddir(DIR *dir)
+{
+ if(dir && dir->handle != -1)
+ {
+ _findclose(dir->handle);
+ dir->handle = _findfirst(dir->name, &dir->info);
+ dir->result.d_name = 0;
+ }
+ else
+ {
+ errno = EBADF;
+ }
+}
diff --git a/libdvdread-embedded/msvc/contrib/dirent/dirent.h b/libdvdread-embedded/msvc/contrib/dirent/dirent.h
new file mode 100644
index 0000000..28a1773
--- /dev/null
+++ b/libdvdread-embedded/msvc/contrib/dirent/dirent.h
@@ -0,0 +1,32 @@
+/*
+
+ Declaration of POSIX directory browsing functions and types for Win32.
+
+ Kevlin Henney (mailto:kevlin@acm.org), March 1997.
+
+ Copyright Kevlin Henney, 1997. All rights reserved.
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation for any purpose is hereby granted without fee, provided
+ that this copyright and permissions notice appear in all copies and
+ derivatives, and that no charge may be made for the software and its
+ documentation except to cover cost of distribution.
+
+*/
+
+#ifndef DIRENT_INCLUDED
+#define DIRENT_INCLUDED
+
+typedef struct DIR DIR;
+
+struct dirent
+{
+ char *d_name;
+};
+
+DIR *opendir(const char *);
+int closedir(DIR *);
+struct dirent *readdir(DIR *);
+void rewinddir(DIR *);
+
+#endif
diff --git a/libdvdread-embedded/msvc/contrib/dlfcn.c b/libdvdread-embedded/msvc/contrib/dlfcn.c
new file mode 100644
index 0000000..01b5781
--- /dev/null
+++ b/libdvdread-embedded/msvc/contrib/dlfcn.c
@@ -0,0 +1,96 @@
+/*
+ * Adopted from Apache DSO code.
+ * Portions copyright Apache Software Foundation
+ *
+ * Structures and types used to implement dlopen, dlsym, etc.
+ * on Windows 95/NT.
+ */
+#include <windows.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../include/dlfcn.h"
+#include "../include/os_types.h"
+
+void *dlopen(const char *module_name, int mode)
+{
+ UINT em;
+ HINSTANCE dsoh;
+ char path[MAX_PATH], *p;
+ (void)mode;
+ /* Load the module...
+ * per PR2555, the LoadLibraryEx function is very picky about slashes.
+ * Debugging on NT 4 SP 6a reveals First Chance Exception within NTDLL.
+ * LoadLibrary in the MS PSDK also reveals that it -explicitly- states
+ * that backslashes must be used.
+ *
+ * Transpose '\' for '/' in the filename.
+ */
+ (void)strncpy(path, module_name, MAX_PATH);
+ path[MAX_PATH - 1] = 0;
+ p = path;
+ while ((p = strchr(p, '/')))
+ *p = '\\';
+
+ /* First assume the dso/dll's required by -this- dso are sitting in the
+ * same path or can be found in the usual places. Failing that, let's
+ * let that dso look in the apache root.
+ */
+ em = SetErrorMode(SEM_FAILCRITICALERRORS);
+ dsoh = LoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+ if (!dsoh)
+ {
+ SetLastError(0); // clear the last error
+ dsoh = LoadLibraryEx(path, NULL, 0);
+ }
+ SetErrorMode(em);
+ SetLastError(0); // clear the last error
+ return (void *)dsoh;
+}
+
+char *dlerror(void)
+{
+ int len, nErrorCode;
+ static char errstr[120];
+ /* This is -not- threadsafe code, but it's about the best we can do.
+ * mostly a potential problem for isapi modules, since LoadModule
+ * errors are handled within a single config thread.
+ */
+
+ if((nErrorCode = GetLastError()) == 0)
+ return((char *)0);
+
+ SetLastError(0); // clear the last error
+ len = snprintf(errstr, sizeof(errstr), "(%d) ", nErrorCode);
+
+ len += FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ nErrorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
+ (LPTSTR) errstr + len,
+ sizeof(errstr) - len,
+ NULL
+ );
+ /* FormatMessage may have appended a newline (\r\n). So remove it
+ * and use ": " instead like the Unix errors. The error may also
+ * end with a . before the return - if so, trash it.
+ */
+ if (len > 1 && errstr[len-2] == '\r' && errstr[len-1] == '\n') {
+ if (len > 2 && errstr[len-3] == '.')
+ len--;
+ errstr[len-2] = ':';
+ errstr[len-1] = ' ';
+ }
+ return errstr;
+}
+
+int dlclose(void *handle)
+{
+ return FreeLibrary(handle);
+}
+
+void *dlsym(void *handle, const char *name)
+{
+ return GetProcAddress(handle, name);
+}
diff --git a/libdvdread-embedded/msvc/contrib/win32_cs.h b/libdvdread-embedded/msvc/contrib/win32_cs.h
new file mode 100644
index 0000000..cb29bac
--- /dev/null
+++ b/libdvdread-embedded/msvc/contrib/win32_cs.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <windows.h>
+
+static inline wchar_t *_utf8_to_wchar(const char *utf8)
+{
+ wchar_t *wstr;
+ int wlen;
+
+ wlen = MultiByteToWideChar (CP_UTF8, 0, utf8, -1, NULL, 0);
+ if (wlen < 1) {
+ return NULL;
+ }
+ wstr = (wchar_t*)malloc(sizeof(wchar_t) * wlen);
+ if (!wstr ) {
+ return NULL;
+ }
+ if (!MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, wlen)) {
+ free(wstr);
+ return NULL;
+ }
+ return wstr;
+}
diff --git a/libdvdread-embedded/msvc/ifo_dump.dsp b/libdvdread-embedded/msvc/ifo_dump.dsp
new file mode 100644
index 0000000..48e718b
--- /dev/null
+++ b/libdvdread-embedded/msvc/ifo_dump.dsp
@@ -0,0 +1,110 @@
+# Microsoft Developer Studio Project File - Name="ifo_dump" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=ifo_dump - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "ifo_dump.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "ifo_dump.mak" CFG="ifo_dump - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "ifo_dump - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "ifo_dump - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "ifo_dump - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "." /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF "$(CFG)" == "ifo_dump - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "ifo_dump___Win32_Debug"
+# PROP BASE Intermediate_Dir "ifo_dump___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug\ifo_dump"
+# PROP Intermediate_Dir "Debug\ifo_dump"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "include" /I "." /I "install/include" /I ".." /I "../src" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "DVDNAV_COMPILE" /FR /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug/bin/ifo_dump.exe" /pdbtype:sept
+# SUBTRACT LINK32 /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "ifo_dump - Win32 Release"
+# Name "ifo_dump - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\ifo_dump.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\ifo_print.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\vmcmd.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/libdvdread-embedded/msvc/include/dlfcn.h b/libdvdread-embedded/msvc/include/dlfcn.h
new file mode 100644
index 0000000..edd03a6
--- /dev/null
+++ b/libdvdread-embedded/msvc/include/dlfcn.h
@@ -0,0 +1,18 @@
+#ifndef __DLFCN_H__
+# define __DLFCN_H__
+
+extern void *dlopen (const char *file, int mode);
+extern int dlclose (void *handle);
+extern void *dlsym (void * handle, const char * name);
+extern char *dlerror (void);
+
+/* These don't mean anything on windows */
+#define RTLD_NEXT ((void *) -1l)
+#define RTLD_DEFAULT ((void *) 0)
+#define RTLD_LAZY -1
+#define RTLD_NOW -1
+#define RTLD_BINDING_MASK -1
+#define RTLD_NOLOAD -1
+#define RTLD_GLOBAL -1
+
+#endif /* __DLFCN_H__ */
diff --git a/libdvdread-embedded/msvc/include/inttypes.h b/libdvdread-embedded/msvc/include/inttypes.h
new file mode 100644
index 0000000..4e1cbe1
--- /dev/null
+++ b/libdvdread-embedded/msvc/include/inttypes.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2000-2001 the xine project
+ *
+ * This file is part of xine, a unix video player.
+ *
+ * xine is free software; you can 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.
+ *
+ * xine is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * WIN32 PORT,
+ * by Matthew Grooms <elon@altavista.com>
+ *
+ * inttypes.h - Standard integer definitions.
+ *
+ */
+
+#ifndef _SYS_INTTYPES_H_
+#define _SYS_INTTYPES_H_
+
+#include <config.h>
+
+#endif
diff --git a/libdvdread-embedded/msvc/include/os_types.h b/libdvdread-embedded/msvc/include/os_types.h
new file mode 100644
index 0000000..eb0f24f
--- /dev/null
+++ b/libdvdread-embedded/msvc/include/os_types.h
@@ -0,0 +1,24 @@
+#ifndef __OS_TYPES_H__
+#define __OS_TYPES_H__
+/*
+ * win32 types
+ * 04 Sept 2001 - Chris Wolf create.
+ */
+
+typedef unsigned char uint_8;
+typedef unsigned short uint_16;
+typedef unsigned int uint_32;
+typedef signed char sint_32;
+typedef signed short sint_16;
+typedef signed int sint_8;
+
+#define snprintf _snprintf
+#define M_PI 3.14159265358979323846 /* pi */
+#define DLLENTRY __declspec(dllexport)
+
+ // Temporarily hardcode this location
+#define AO_PLUGIN_PATH "c:\\Program Files\\Common Files\\Xiphophorus\\ao"
+
+#define SHARED_LIB_EXT ".dll"
+
+#endif /* __OS_TYPES_H__ */
diff --git a/libdvdread-embedded/msvc/include/sys/time.h b/libdvdread-embedded/msvc/include/sys/time.h
new file mode 100644
index 0000000..f7973c1
--- /dev/null
+++ b/libdvdread-embedded/msvc/include/sys/time.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2000-2001 the xine project
+ *
+ * This file is part of xine, a unix video player.
+ *
+ * xine is free software; you can 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.
+ *
+ * xine is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * WIN32 PORT,
+ * by Matthew Grooms <elon@altavista.com>
+ *
+ * sys/time.h - There is no separate sys/time.h for win32 so we simply
+ * include the standard time header as well as our xine
+ * timer functions.
+ */
+
+#include <time.h>
diff --git a/libdvdread-embedded/msvc/include/unistd.h b/libdvdread-embedded/msvc/include/unistd.h
new file mode 100644
index 0000000..620017c
--- /dev/null
+++ b/libdvdread-embedded/msvc/include/unistd.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2000-2001 the xine project
+ *
+ * This file is part of xine, a unix video player.
+ *
+ * xine is free software; you can 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.
+ *
+ * xine is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * WIN32 PORT,
+ * by Matthew Grooms <elon@altavista.com>
+ *
+ * unistd.h - This is mostly a catch all header that maps standard unix
+ * libc calls to the equivalent win32 functions.
+ *
+ */
+
+#include <windows.h>
+#include <malloc.h>
+#include <errno.h>
+#include <direct.h>
+
+#include <config.h>
+
+#ifndef _SYS_UNISTD_H_
+#define _SYS_UNISTD_H_
+
+#define inline __inline
+
+#define mkdir( A, B ) _mkdir( A )
+#define lstat stat
+
+#ifndef S_ISDIR
+#define S_ISDIR(A) ( S_IFDIR & A )
+#endif
+
+#define S_IXUSR S_IEXEC
+#define S_IXGRP S_IEXEC
+#define S_IXOTH S_IEXEC
+
+#define M_PI 3.14159265358979323846 /* pi */
+
+#define bzero( A, B ) memset( A, 0, B )
+
+#ifndef strcasecmp
+#define strcasecmp _stricmp
+#endif
+
+#ifndef strncasecmp
+#define strncasecmp _strnicmp
+#endif
+
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+
+// FIXME : I don't remember why this is here
+#define readlink
+
+#endif
diff --git a/libdvdread-embedded/msvc/install/README b/libdvdread-embedded/msvc/install/README
new file mode 100644
index 0000000..5e6ba4a
--- /dev/null
+++ b/libdvdread-embedded/msvc/install/README
@@ -0,0 +1,7 @@
+MSVC Help
+---------
+
+In order to build using any application that requires libdvdnav using MSVC the
+following directories (lib and include) must be copied to the msvc directory of
+the particular application.
+
diff --git a/libdvdread-embedded/msvc/libdvdcss.def b/libdvdread-embedded/msvc/libdvdcss.def
new file mode 100644
index 0000000..0a6e2fd
--- /dev/null
+++ b/libdvdread-embedded/msvc/libdvdcss.def
@@ -0,0 +1,12 @@
+;------------------------------------------------------------
+; LIBDVDCSS DLL DEFINITIONS FILE
+
+EXPORTS
+
+dvdcss_interface_2
+dvdcss_open
+dvdcss_error
+dvdcss_seek
+dvdcss_read
+dvdcss_close
+dvdcss_title \ No newline at end of file
diff --git a/libdvdread-embedded/msvc/libdvdcss.dsp b/libdvdread-embedded/msvc/libdvdcss.dsp
new file mode 100644
index 0000000..bb91b6a
--- /dev/null
+++ b/libdvdread-embedded/msvc/libdvdcss.dsp
@@ -0,0 +1,139 @@
+# Microsoft Developer Studio Project File - Name="libdvdcss" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=libdvdcss - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "libdvdcss.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "libdvdcss.mak" CFG="libdvdcss - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "libdvdcss - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "libdvdcss - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "libdvdcss - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_USRDLL" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "." /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_USRDLL" /D PATH_MAX=2048 /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /machine:IX86
+# ADD LINK32 /machine:IX86
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PostBuild_Desc=Create libdvdcss Install
+PostBuild_Cmds=scripts\libdvdcss_intstall.bat Release
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "libdvdcss - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "." /I "../libdvdcss/dvdcss" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_USRDLL" /D MAX_PATH=2048 /YX /FD /GZ ./ "../libdvdcss" /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /machine:IX86
+# ADD LINK32 /dll /machine:IX86 /out:"Debug/bin/libdvdcss.dll"
+# SUBTRACT LINK32 /pdb:none
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PostBuild_Desc=Create libdvdcss Install
+PostBuild_Cmds=scripts\libdvdcss_intstall.bat Debug
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "libdvdcss - Win32 Release"
+# Name "libdvdcss - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\..\libdvdcss\css.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\libdvdcss\device.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\libdvdcss\error.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\libdvdcss\ioctl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\libdvdcss\libdvdcss.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "DLL Defs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\libdvdcss.def
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/libdvdread-embedded/msvc/libdvdnav.def b/libdvdread-embedded/msvc/libdvdnav.def
new file mode 100644
index 0000000..a483cfd
--- /dev/null
+++ b/libdvdread-embedded/msvc/libdvdnav.def
@@ -0,0 +1,73 @@
+;------------------------------------------------------------
+; LIBDVDNAV DLL DEFINITIONS FILE
+
+EXPORTS
+
+DVDOpen
+DVDClose
+DVDOpenFile
+DVDCloseFile
+DVDReadBlocks
+
+navRead_DSI
+navRead_PCI
+
+ifoClose
+ifoRead_VOBU_ADMAP
+ifoRead_VTS_ATRT
+ifoRead_PTL_MAIT
+ifoRead_PGCI_UT
+ifoRead_TT_SRPT
+ifoRead_FP_PGC
+ifoOpenVMGI
+ifoRead_TITLE_VOBU_ADMAP
+ifoRead_PGCIT
+ifoRead_VTS_PTT_SRPT
+ifoOpenVTSI
+ifoPrint
+
+dvdnav_set_readahead_flag
+dvdnav_set_region_mask
+dvdnav_spu_language_select
+dvdnav_audio_language_select
+dvdnav_menu_language_select
+dvdnav_get_angle_info
+dvdnav_current_title_info
+dvdnav_title_play
+dvdnav_part_play
+dvdnav_get_number_of_titles
+dvdnav_get_title_string
+dvdnav_open
+dvdnav_close
+dvdnav_wait_skip
+dvdnav_get_video_scale_permission
+dvdnav_get_video_aspect
+dvdnav_still_skip
+dvdnav_err_to_string
+dvdnav_get_next_cache_block
+dvdnav_free_cache_block
+dvdnav_get_position
+dvdnav_sector_search
+dvdnav_get_current_highlight
+dvdnav_button_select_and_activate
+dvdnav_right_button_select
+dvdnav_left_button_select
+dvdnav_lower_button_select
+dvdnav_upper_button_select
+dvdnav_mouse_select
+dvdnav_button_select
+dvdnav_mouse_activate
+dvdnav_button_activate
+dvdnav_angle_change
+dvdnav_prev_pg_search
+dvdnav_next_pg_search
+dvdnav_menu_call
+dvdnav_spu_stream_to_lang
+dvdnav_get_spu_logical_stream
+dvdnav_audio_stream_to_lang
+dvdnav_get_audio_logical_stream
+dvdnav_is_domain_vts
+
+dvdnav_set_PGC_positioning_flag
+dvdnav_get_number_of_parts
+dvdnav_reset
diff --git a/libdvdread-embedded/msvc/libdvdnav.dsp b/libdvdread-embedded/msvc/libdvdnav.dsp
new file mode 100644
index 0000000..9d8196e
--- /dev/null
+++ b/libdvdread-embedded/msvc/libdvdnav.dsp
@@ -0,0 +1,188 @@
+# Microsoft Developer Studio Project File - Name="libdvdnav" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=libdvdnav - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "libdvdnav.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "libdvdnav.mak" CFG="libdvdnav - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "libdvdnav - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "libdvdnav - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "libdvdnav - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+LINK32=link.exe
+# ADD BASE LINK32 /machine:IX86
+# ADD LINK32 /machine:IX86
+MTL=midl.exe
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "..\..\libdvdcss\src" /I "." /I "include" /I "contrib/dirent" /I "../../libdvdcss" /I ".." /I "../src" /I "../src/dvdread" /I "../src/vm" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "DVDNAV_COMPILE" /D "HAVE_CONFIG_H" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"Release\libdvdnav\libdvdnav.lib"
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PostBuild_Desc=Create libdvdnav Install Files
+PostBuild_Cmds=scripts\libdvdnav_install.bat Release
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "libdvdnav - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+LINK32=link.exe
+# ADD BASE LINK32 /machine:IX86
+# ADD LINK32 /debug /machine:IX86 /out:"Debug/libdvdnav.lib" /implib:"Debug/libdvdnav.lib"
+# SUBTRACT LINK32 /pdb:none /nodefaultlib
+MTL=midl.exe
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "include" /I "contrib/dirent" /I "../../libdvdcss" /I "../src" /I "." /I ".." /I "../src/dvdread" /I "../src/vm" /D "WIN32" /D "_DEBUG" /D "_LIB" /D "DVDNAV_COMPILE" /D "HAVE_CONFIG_H" /FR"Debug/libdvdnav/" /Fp"Debug/libdvdnav/libdvdnav.pch" /YX /Fo"Debug/libdvdnav/" /Fd"Debug/libdvdnav/" /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 libwin32utils.lib /nologo /out:"Debug\libdvdnav\libdvdnav.lib"
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PostBuild_Desc=Create libdvdnav Install Files
+PostBuild_Cmds=scripts\libdvdnav_install.bat Debug
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "libdvdnav - Win32 Release"
+# Name "libdvdnav - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\vm\decoder.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\dvdread\dvd_input.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\dvdread\dvd_reader.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\dvdread\dvd_udf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\dvdnav.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\highlight.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\dvdread\ifo_print.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\dvdread\ifo_read.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\dvdread\md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\dvdread\nav_print.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\dvdread\nav_read.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\navigation.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\read_cache.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\remap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\searching.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\settings.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\vm\vm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\vm\vmcmd.c
+# End Source File
+# End Group
+# Begin Group "DLL Defs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\libdvdnav.def
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/libdvdread-embedded/msvc/libdvdnav.dsw b/libdvdread-embedded/msvc/libdvdnav.dsw
new file mode 100644
index 0000000..8974efb
--- /dev/null
+++ b/libdvdread-embedded/msvc/libdvdnav.dsw
@@ -0,0 +1,101 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "ifo_dump"=.\ifo_dump.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name libdvdnav
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "libdvdcss"=.\libdvdcss.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "libdvdnav"=.\libdvdnav.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name libwin32utils
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "libwin32utils"=.\libwin32utils.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "play_title"=.\play_title.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name libdvdnav
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "title_info"=.\title_info.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name libdvdnav
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/libdvdread-embedded/msvc/libwin32utils.def b/libdvdread-embedded/msvc/libwin32utils.def
new file mode 100644
index 0000000..02472a5
--- /dev/null
+++ b/libdvdread-embedded/msvc/libwin32utils.def
@@ -0,0 +1,77 @@
+;------------------------------------------------------------
+; LIBWIN32UTILS DLL DEFINITIONS FILE
+
+EXPORTS
+
+;------------------------------------------------------------
+; dirent exports
+
+opendir
+closedir
+readdir
+rewinddir
+
+;
+; Needed if !defined(_MSC_VER) && !defined(__cplusplus)
+;
+ptw32_push_cleanup
+ptw32_pop_cleanup
+;
+; Not for use directly. Needed by macros in pthread.h
+; to return internal SEH code.
+;
+ptw32_get_exception_services_code
+
+;------------------------------------------------------------
+; timer exports
+
+adler32
+compress
+crc32
+deflate
+deflateCopy
+deflateEnd
+deflateInit2_
+deflateInit_
+deflateParams
+deflateReset
+deflateSetDictionary
+gzclose
+gzdopen
+gzerror
+gzflush
+gzopen
+gzread
+gzwrite
+inflate
+inflateEnd
+inflateInit2_
+inflateInit_
+inflateReset
+inflateSetDictionary
+inflateSync
+uncompress
+zlibVersion
+gzprintf
+gzputc
+gzgetc
+gzseek
+gzrewind
+gztell
+gzeof
+gzsetparams
+zError
+inflateSyncPoint
+get_crc_table
+compress2
+gzputs
+gzgets
+
+;------------------------------------------------------------
+; other exports
+dlclose
+dlsym
+dlopen
+dlerror
+
+optind
diff --git a/libdvdread-embedded/msvc/libwin32utils.dsp b/libdvdread-embedded/msvc/libwin32utils.dsp
new file mode 100644
index 0000000..712b24f
--- /dev/null
+++ b/libdvdread-embedded/msvc/libwin32utils.dsp
@@ -0,0 +1,121 @@
+# Microsoft Developer Studio Project File - Name="libwin32utils" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=libwin32utils - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "libwin32utils.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "libwin32utils.mak" CFG="libwin32utils - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "libwin32utils - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "libwin32utils - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "libwin32utils - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release/libwin32utils"
+# PROP Intermediate_Dir "Release/libwin32utils"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /machine:I386
+# ADD LINK32 winmm.lib /nologo /machine:I386 /out:"Release/libwin32utils.lib"
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_LIB" /D "LIBWIN32UTILS_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "include" /I "contrib/dirent" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_LIB" /D "LIBWIN32UTILS_EXPORTS" /D "__CLEANUP_C" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD LIB32 /out:"libwin32utils.lib"
+
+!ELSEIF "$(CFG)" == "libwin32utils - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug/libwin32utils"
+# PROP Intermediate_Dir "Debug/libwin32utils"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 winmm.lib /nologo /debug /machine:I386 /out:"Debug/libwin32utils.lib" /pdbtype:sept
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_LIB" /D "LIBWIN32UTILS_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "include" /I "contrib/dirent" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_LIB" /D "LIBWIN32UTILS_EXPORTS" /D "__CLEANUP_C" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD LIB32 winmm.lib /out:"libwin32utils.lib"
+
+!ENDIF
+
+# Begin Target
+
+# Name "libwin32utils - Win32 Release"
+# Name "libwin32utils - Win32 Debug"
+# Begin Group "Source Files ( dirent )"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\contrib\dirent\dirent.c
+# End Source File
+# End Group
+#
+# Begin Group "DLL Defs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\libwin32utils.def
+# End Source File
+# End Group
+# Begin Group "Source Files ( other )"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\contrib\dlfcn.c
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/libdvdread-embedded/msvc/play_title.dsp b/libdvdread-embedded/msvc/play_title.dsp
new file mode 100644
index 0000000..8a3efdf
--- /dev/null
+++ b/libdvdread-embedded/msvc/play_title.dsp
@@ -0,0 +1,101 @@
+# Microsoft Developer Studio Project File - Name="play_title" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=play_title - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "play_title.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "play_title.mak" CFG="play_title - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "play_title - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "play_title - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "play_title - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "." /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF "$(CFG)" == "play_title - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "play_title___Win32_Debug"
+# PROP BASE Intermediate_Dir "play_title___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug\play_title"
+# PROP Intermediate_Dir "Debug\play_title"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "include" /I "contrib/dirent" /I "../../libdvdcss" /I "install/include" /I "." /I ".." /I "../src" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug/bin/play_title.exe" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "play_title - Win32 Release"
+# Name "play_title - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\play_title.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/libdvdread-embedded/msvc/scripts/libdvdcss_install.bat b/libdvdread-embedded/msvc/scripts/libdvdcss_install.bat
new file mode 100755
index 0000000..84712b1
--- /dev/null
+++ b/libdvdread-embedded/msvc/scripts/libdvdcss_install.bat
@@ -0,0 +1,8 @@
+
+ECHO mkdir dll ...
+rmdir /s install\dll
+mkdir install\dll
+
+ECHO libvdvcss dll ...
+xcopy /Y %1\bin\libdvdcss.dll install\dll
+
diff --git a/libdvdread-embedded/msvc/scripts/libdvdnav_install.bat b/libdvdread-embedded/msvc/scripts/libdvdnav_install.bat
new file mode 100755
index 0000000..f49f2f2
--- /dev/null
+++ b/libdvdread-embedded/msvc/scripts/libdvdnav_install.bat
@@ -0,0 +1,21 @@
+
+ECHO mkdir install ...
+rmdir /s install\include
+rmdir /s install\lib
+mkdir install\include\dvdnav
+mkdir install\lib
+
+ECHO includes ...
+xcopy /Y ..\src\dvdnav.h install\include\dvdnav
+xcopy /Y ..\src\dvdnav_events.h install\include\dvdnav
+xcopy /Y ..\src\dvd_types.h install\include\dvdnav
+xcopy /Y ..\src\dvdread\dvd_reader.h install\include\dvdnav
+xcopy /Y ..\src\dvdread\nav_read.h install\include\dvdnav
+xcopy /Y ..\src\dvdread\ifo_read.h install\include\dvdnav
+xcopy /Y ..\src\dvdread\nav_print.h install\include\dvdnav
+xcopy /Y ..\src\dvdread\ifo_print.h install\include\dvdnav
+xcopy /Y ..\src\dvdread\ifo_types.h install\include\dvdnav
+xcopy /Y ..\src\dvdread\nav_types.h install\include\dvdnav
+
+ECHO lib ...
+xcopy /Y %1\libdvdnav\libdvdnav.lib install\lib
diff --git a/libdvdread-embedded/msvc/title_info.dsp b/libdvdread-embedded/msvc/title_info.dsp
new file mode 100644
index 0000000..39d3768
--- /dev/null
+++ b/libdvdread-embedded/msvc/title_info.dsp
@@ -0,0 +1,101 @@
+# Microsoft Developer Studio Project File - Name="title_info" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=title_info - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "title_info.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "title_info.mak" CFG="title_info - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "title_info - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "title_info - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "title_info - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "." /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF "$(CFG)" == "title_info - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "title_info___Win32_Debug"
+# PROP BASE Intermediate_Dir "title_info___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "title_info___Win32_Debug"
+# PROP Intermediate_Dir "title_info___Win32_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "include" /I "contrib/dirent" /I "../../libdvdcss" /I "install/include" /I "." /I ".." /I "../src" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"Debug/title_info/title_info.pch" /YX /Fo"Debug/title_info/" /Fd"Debug/title_info/" /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug/bin/title_info.exe" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "title_info - Win32 Release"
+# Name "title_info - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\title_info.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/libdvdread-embedded/src/bitreader.c b/libdvdread-embedded/src/bitreader.c
new file mode 100644
index 0000000..f6a2183
--- /dev/null
+++ b/libdvdread-embedded/src/bitreader.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "dvdread/bitreader.h"
+
+int dvdread_getbits_init(getbits_state_t *state, const uint8_t *start) {
+ if ((state == NULL) || (start == NULL)) return 0;
+ state->start = start;
+ state->bit_position = 0;
+ state->byte_position = 0;
+ return 1;
+}
+
+/* Non-optimized getbits. */
+/* This can easily be optimized for particular platforms. */
+uint32_t dvdread_getbits(getbits_state_t *state, uint32_t number_of_bits) {
+ uint32_t result=0;
+ uint8_t byte=0;
+ if (number_of_bits > 32) {
+ printf("Number of bits > 32 in getbits\n");
+ abort();
+ }
+
+ if ((state->bit_position) > 0) { /* Last getbits left us in the middle of a byte. */
+ if (number_of_bits > (8-state->bit_position)) { /* this getbits will span 2 or more bytes. */
+ byte = state->start[state->byte_position] << state->bit_position;
+ byte = byte >> (state->bit_position);
+ result = byte;
+ number_of_bits -= (8-state->bit_position);
+ state->bit_position = 0;
+ state->byte_position++;
+ } else {
+ byte = state->start[state->byte_position] << state->bit_position;
+ byte = byte >> (8 - number_of_bits);
+ result = byte;
+ state->bit_position += number_of_bits; /* Here it is impossible for bit_position > 8 */
+ if (state->bit_position == 8) {
+ state->bit_position = 0;
+ state->byte_position++;
+ }
+ number_of_bits = 0;
+ }
+ }
+ if ((state->bit_position) == 0) {
+ while (number_of_bits > 7) {
+ result = (result << 8) + state->start[state->byte_position];
+ state->byte_position++;
+ number_of_bits -= 8;
+ }
+ if (number_of_bits > 0) { /* number_of_bits < 8 */
+ byte = state->start[state->byte_position] << state->bit_position;
+ state->bit_position += number_of_bits; /* Here it is impossible for bit_position > 7 */
+ byte = byte >> (8 - number_of_bits);
+ result = (result << number_of_bits) + byte;
+ number_of_bits = 0;
+ }
+ }
+
+ return result;
+}
+
+#if 0 /* TODO: optimized versions not yet used */
+
+/* WARNING: This function can only be used on a byte boundary.
+ No checks are made that we are in fact on a byte boundary.
+ */
+uint16_t dvdread_get16bits(getbits_state_t *state) {
+ uint16_t result;
+ state->byte_position++;
+ result = (state->byte << 8) + state->start[state->byte_position++];
+ state->byte = state->start[state->byte_position];
+ return result;
+}
+
+/* WARNING: This function can only be used on a byte boundary.
+ No checks are made that we are in fact on a byte boundary.
+ */
+uint32_t dvdread_get32bits(getbits_state_t *state) {
+ uint32_t result;
+ state->byte_position++;
+ result = (state->byte << 8) + state->start[state->byte_position++];
+ result = (result << 8) + state->start[state->byte_position++];
+ result = (result << 8) + state->start[state->byte_position++];
+ state->byte = state->start[state->byte_position];
+ return result;
+}
+
+#endif
+
+#ifdef BITREADER_TESTS
+
+int main()
+{
+ uint8_t buff[2] = {
+ 0x6E, 0xC2
+ // 0b 01101110 11000010
+ };
+ getbits_state_t state;
+ dvdread_getbits_init(&state, buff);
+
+ uint32_t bits = dvdread_getbits(&state, 3);
+ assert(bits == 3);
+
+ bits = dvdread_getbits(&state, 3);
+ assert(bits == 3);
+
+ bits = dvdread_getbits(&state, 4);
+ assert(bits == 11);
+
+ bits = dvdread_getbits(&state, 6);
+ assert(bits == 2);
+
+ dvdread_getbits_init(&state, buff);
+ bits = dvdread_getbits(&state, 10);
+ assert(bits == 443);
+
+ bits = dvdread_getbits(&state, 6);
+ assert(bits == 2);
+
+ dvdread_getbits_init(&state, buff);
+ bits = dvdread_getbits(&state, 16);
+ assert(bits == 28354);
+
+ buff[0] = buff[1] = 0xFF;
+ dvdread_getbits_init(&state, buff);
+ bits = dvdread_getbits(&state, 16);
+ assert(bits == 0xFFFF);
+
+ uint8_t large[5] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ dvdread_getbits_init(&state, large);
+ bits = dvdread_getbits(&state, 8);
+ assert(bits == 0xFF);
+ bits = dvdread_getbits(&state, 32);
+ assert(bits == 0xFFFFFFFF);
+
+ return 0;
+}
+
+#endif
diff --git a/libdvdread-embedded/src/bswap.h b/libdvdread-embedded/src/bswap.h
new file mode 100644
index 0000000..d3b2383
--- /dev/null
+++ b/libdvdread-embedded/src/bswap.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2000, 2001 Billy Biggs <vektor@dumbterm.net>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDREAD_BSWAP_H
+#define LIBDVDREAD_BSWAP_H
+
+#include <config.h>
+
+#if defined(WORDS_BIGENDIAN)
+/* All bigendian systems are fine, just ignore the swaps. */
+#define B2N_16(x) (void)(x)
+#define B2N_32(x) (void)(x)
+#define B2N_64(x) (void)(x)
+
+#else
+
+/* For __FreeBSD_version */
+#if defined(HAVE_SYS_PARAM_H)
+#include <sys/param.h>
+#endif
+
+#if defined(__linux__) || defined(__GLIBC__)
+#include <byteswap.h>
+#define B2N_16(x) x = bswap_16(x)
+#define B2N_32(x) x = bswap_32(x)
+#define B2N_64(x) x = bswap_64(x)
+
+#elif defined(__APPLE__)
+#include <libkern/OSByteOrder.h>
+#define B2N_16(x) x = OSSwapBigToHostInt16(x)
+#define B2N_32(x) x = OSSwapBigToHostInt32(x)
+#define B2N_64(x) x = OSSwapBigToHostInt64(x)
+
+#elif defined(__NetBSD__)
+#include <sys/endian.h>
+#define B2N_16(x) BE16TOH(x)
+#define B2N_32(x) BE32TOH(x)
+#define B2N_64(x) BE64TOH(x)
+
+#elif defined(__OpenBSD__)
+#include <sys/endian.h>
+#define B2N_16(x) x = swap16(x)
+#define B2N_32(x) x = swap32(x)
+#define B2N_64(x) x = swap64(x)
+
+#elif defined(__FreeBSD__) && __FreeBSD_version >= 470000
+#include <sys/endian.h>
+#define B2N_16(x) x = be16toh(x)
+#define B2N_32(x) x = be32toh(x)
+#define B2N_64(x) x = be64toh(x)
+
+#elif defined(__QNXNTO__)
+#include <gulliver.h>
+#define B2N_16(x) x = ENDIAN_RET16(x)
+#define B2N_32(x) x = ENDIAN_RET32(x)
+#define B2N_64(x) x = ENDIAN_RET64(x)
+
+#elif defined(__DragonFly__)
+#include <sys/endian.h>
+#define B2N_16(x) x = bswap16(x)
+#define B2N_32(x) x = bswap32(x)
+#define B2N_64(x) x = bswap64(x)
+
+/* This is a slow but portable implementation, it has multiple evaluation
+ * problems so beware.
+ * Old FreeBSD's and Solaris don't have <byteswap.h> or any other such
+ * functionality!
+ */
+
+#elif defined(__FreeBSD__) || defined(__sun) || defined(__bsdi__) || defined(_WIN32) || defined(__CYGWIN__) || defined(__BEOS__) || defined(__OS2__)
+#define B2N_16(x) \
+ x = ((((x) & 0xff00) >> 8) | \
+ (((x) & 0x00ff) << 8))
+#define B2N_32(x) \
+ x = ((((x) & 0xff000000) >> 24) | \
+ (((x) & 0x00ff0000) >> 8) | \
+ (((x) & 0x0000ff00) << 8) | \
+ (((x) & 0x000000ff) << 24))
+#define B2N_64(x) \
+ x = ((((x) & 0xff00000000000000ULL) >> 56) | \
+ (((x) & 0x00ff000000000000ULL) >> 40) | \
+ (((x) & 0x0000ff0000000000ULL) >> 24) | \
+ (((x) & 0x000000ff00000000ULL) >> 8) | \
+ (((x) & 0x00000000ff000000ULL) << 8) | \
+ (((x) & 0x0000000000ff0000ULL) << 24) | \
+ (((x) & 0x000000000000ff00ULL) << 40) | \
+ (((x) & 0x00000000000000ffULL) << 56))
+
+#else
+
+/* If there isn't a header provided with your system with this functionality
+ * add the relevant || define( ) to the portable implementation above.
+ */
+#error "You need to add endian swap macros for your system"
+
+#endif
+
+#endif /* WORDS_BIGENDIAN */
+
+#endif /* LIBDVDREAD_BSWAP_H */
diff --git a/libdvdread-embedded/src/dvd_input.c b/libdvdread-embedded/src/dvd_input.c
new file mode 100644
index 0000000..26b6928
--- /dev/null
+++ b/libdvdread-embedded/src/dvd_input.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2002 Samuel Hocevar <sam@zoy.org>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h" /* Required for HAVE_DVDCSS_DVDCSS_H */
+#include <stdio.h> /* fprintf */
+#include <stdlib.h> /* free */
+#include <fcntl.h> /* open */
+#include <unistd.h> /* lseek */
+#include <string.h> /* strerror */
+#include <errno.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include "../msvc/contrib/win32_cs.h"
+#endif
+
+#include "dvdread/dvd_reader.h" /* DVD_VIDEO_LB_LEN */
+#include "dvdread_internal.h"
+#include "dvd_input.h"
+#include "logger.h"
+
+
+/* The function pointers that is the exported interface of this file. */
+dvd_input_t (*dvdinput_open) (void *, dvd_logger_cb *,
+ const char *,dvd_reader_stream_cb *);
+int (*dvdinput_close) (dvd_input_t);
+int (*dvdinput_seek) (dvd_input_t, int);
+int (*dvdinput_title) (dvd_input_t, int);
+int (*dvdinput_read) (dvd_input_t, void *, int, int);
+
+#ifdef HAVE_DVDCSS_DVDCSS_H
+/* linking to libdvdcss */
+# include <dvdcss/dvdcss.h>
+# define DVDcss_open_stream(a, b) \
+ dvdcss_open_stream((void*)(a), (dvdcss_stream_cb*)(b))
+# define DVDcss_open(a) dvdcss_open((char*)(a))
+# define DVDcss_close dvdcss_close
+# define DVDcss_seek dvdcss_seek
+# define DVDcss_read dvdcss_read
+#else
+
+/* dlopening libdvdcss */
+# if defined(HAVE_DLFCN_H) && !defined(USING_BUILTIN_DLFCN)
+# include <dlfcn.h>
+# else
+# if defined(_WIN32)
+/* Only needed on MINGW at the moment */
+# include "../msvc/contrib/dlfcn.c"
+# endif
+# endif
+
+typedef struct dvdcss_s *dvdcss_t;
+typedef struct dvdcss_stream_cb dvdcss_stream_cb;
+static dvdcss_t (*DVDcss_open_stream) (void *, dvdcss_stream_cb *);
+static dvdcss_t (*DVDcss_open) (const char *);
+static int (*DVDcss_close) (dvdcss_t);
+static int (*DVDcss_seek) (dvdcss_t, int, int);
+static int (*DVDcss_read) (dvdcss_t, void *, int, int);
+#define DVDCSS_SEEK_KEY (1 << 1)
+#endif
+
+#ifdef _WIN32
+static int open_win32(const char *path, int flags)
+{
+ wchar_t *wpath;
+ int fd;
+
+ wpath = _utf8_to_wchar(path);
+ if (!wpath) {
+ return -1;
+ }
+ fd = _wopen(wpath, flags);
+ free(wpath);
+ return fd;
+}
+#endif
+
+/* The DVDinput handle, add stuff here for new input methods.
+ * NOTE: All members of this structure must be initialized in dvd_input_New
+ */
+struct dvd_input_s {
+ /* libdvdcss handle */
+ dvdcss_t dvdcss;
+ /* */
+ void *priv;
+ dvd_logger_cb *logcb;
+ off_t ipos;
+
+ /* dummy file input */
+ int fd;
+ /* stream input */
+ dvd_reader_stream_cb *stream_cb;
+};
+
+static dvd_input_t dvd_input_New(void *priv, dvd_logger_cb *logcb)
+{
+ dvd_input_t dev = calloc(1, sizeof(*dev));
+ if(dev)
+ {
+ dev->priv = priv;
+ dev->logcb = logcb;
+ dev->ipos = 0;
+
+ /* Initialize all inputs to safe defaults */
+ dev->dvdcss = NULL;
+ dev->fd = -1;
+ dev->stream_cb = NULL;
+ }
+ return dev;
+}
+
+/**
+ * initialize and open a DVD (device or file or stream_cb)
+ */
+static dvd_input_t css_open(void *priv, dvd_logger_cb *logcb,
+ const char *target,
+ dvd_reader_stream_cb *stream_cb)
+{
+ dvd_input_t dev;
+
+ /* Allocate the handle structure */
+ dev = dvd_input_New(priv, logcb);
+ if(dev == NULL) {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Could not allocate memory.");
+ return NULL;
+ }
+
+ /* Really open it with libdvdcss */
+ if(target)
+ dev->dvdcss = DVDcss_open(target);
+ else if(priv && stream_cb) {
+#ifdef HAVE_DVDCSS_DVDCSS_H
+ dev->dvdcss = DVDcss_open_stream(priv, (dvdcss_stream_cb *)stream_cb);
+#else
+ dev->dvdcss = DVDcss_open_stream ?
+ DVDcss_open_stream(priv, (dvdcss_stream_cb *)stream_cb) :
+ NULL;
+#endif
+ }
+ if(dev->dvdcss == NULL) {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Could not open %s with libdvdcss.", target);
+ free(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+/**
+ * seek into the device.
+ */
+static int css_seek(dvd_input_t dev, int blocks)
+{
+ /* DVDINPUT_NOFLAGS should match the DVDCSS_NOFLAGS value. */
+ return DVDcss_seek(dev->dvdcss, blocks, DVDINPUT_NOFLAGS);
+}
+
+/**
+ * set the block for the beginning of a new title (key).
+ */
+static int css_title(dvd_input_t dev, int block)
+{
+ return DVDcss_seek(dev->dvdcss, block, DVDCSS_SEEK_KEY);
+}
+
+/**
+ * read data from the device.
+ */
+static int css_read(dvd_input_t dev, void *buffer, int blocks, int flags)
+{
+ return DVDcss_read(dev->dvdcss, buffer, blocks, flags);
+}
+
+/**
+ * close the DVD device and clean up the library.
+ */
+static int css_close(dvd_input_t dev)
+{
+ int ret;
+
+ ret = DVDcss_close(dev->dvdcss);
+
+ free(dev);
+
+ return ret;
+}
+
+/**
+ * initialize and open a DVD device or file.
+ */
+static dvd_input_t file_open(void *priv, dvd_logger_cb *logcb,
+ const char *target,
+ dvd_reader_stream_cb *stream_cb)
+{
+ dvd_input_t dev;
+
+ /* Allocate the library structure */
+ dev = dvd_input_New(priv, logcb);
+ if(dev == NULL) {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Could not allocate memory.");
+ return NULL;
+ }
+
+ /* Initialize with stream callback if it is specified */
+ if (stream_cb) {
+ if (!stream_cb->pf_read || !stream_cb->pf_seek) {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Stream callback provided but lacks of pf_read or pf_seek methods.");
+ free(dev);
+ return NULL;
+ }
+ dev->stream_cb = stream_cb;
+ return dev;
+ }
+
+ /* Open the device */
+ if(target == NULL) {
+ free(dev);
+ return NULL;
+ }
+#if defined(_WIN32)
+ dev->fd = open_win32(target, O_RDONLY | O_BINARY);
+#elif defined(__OS2__)
+ dev->fd = open(target, O_RDONLY | O_BINARY);
+#else
+ dev->fd = open(target, O_RDONLY);
+#endif
+ if(dev->fd < 0) {
+ char buf[256];
+#if defined(HAVE_STRERROR_R) && defined(HAVE_DECL_STRERROR_R)
+ #ifdef STRERROR_R_CHAR_P
+ *buf=0;
+ if(strerror_r(errno, buf, 256) == NULL)
+ *buf=0;
+ #else
+ if(strerror_r(errno, buf, 256) != 0)
+ *buf=0;
+ #endif
+#else
+ #if defined(HAVE_STRERR_S)
+ if(strerror_s(buf, 256, errno) != 0)
+ *buf=0;
+ #else
+ *buf=0;
+ #endif
+#endif
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Could not open input: %s", buf);
+ free(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+/**
+ * seek into the device.
+ */
+static int file_seek(dvd_input_t dev, int blocks)
+{
+ off_t pos = -1;
+
+ if(dev->ipos == blocks)
+ {
+ /* We are already in position */
+ return blocks;
+ }
+
+ if (dev->stream_cb) {
+ /* Returns 0 on successful completion and -1 on error */
+ pos = dev->stream_cb->pf_seek(dev->priv, (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN);
+
+ if (!pos) {
+ dev->ipos = blocks;
+ }
+ } else {
+ /* Returns position as the number of bytes from beginning of file
+ * or -1 on error
+ */
+ pos = lseek(dev->fd, (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN, SEEK_SET);
+
+ if (pos >= 0) {
+ dev->ipos = pos / DVD_VIDEO_LB_LEN;
+ }
+ }
+
+ if(pos < 0) {
+ return pos;
+ }
+
+ /* assert pos % DVD_VIDEO_LB_LEN == 0 */
+ return (int) dev->ipos;
+}
+
+/**
+ * set the block for the beginning of a new title (key).
+ */
+static int file_title(dvd_input_t dev UNUSED, int block UNUSED)
+{
+ return -1;
+}
+
+/**
+ * read data from the device.
+ */
+static int file_read(dvd_input_t dev, void *buffer, int blocks,
+ int flags UNUSED)
+{
+ size_t len, bytes, blocks_read;
+
+ len = (size_t)blocks * DVD_VIDEO_LB_LEN;
+ bytes = 0;
+ blocks_read = 0;
+
+ while(len > 0) {
+ ssize_t ret = -1;
+
+ /* Perform read based on the input type */
+ if (dev->stream_cb) {
+ /* Returns the number of bytes read or -1 on error */
+ ret = dev->stream_cb->pf_read(dev->priv, ((char*)buffer) + bytes, len);
+ } else {
+ /* Returns the number of bytes read or -1 on error */
+ ret = read(dev->fd, ((char*)buffer) + bytes, len);
+ }
+
+ if(ret < 0) {
+ /* One of the reads failed, too bad. We won't even bother
+ * returning the reads that went OK, and as in the POSIX spec
+ * the file position is left unspecified after a failure. */
+ dev->ipos = -1;
+ return ret;
+ }
+
+ if(ret == 0) {
+ /* Nothing more to read. Return all of the whole blocks, if any.
+ * Adjust the file position back to the previous block boundary. */
+ ret = file_seek(dev, dev->ipos + blocks_read);
+ if(ret < 0)
+ return ret;
+
+ return (int) blocks_read;
+ }
+
+ len -= ret;
+ bytes += ret;
+ blocks_read = bytes / DVD_VIDEO_LB_LEN;
+ }
+
+ dev->ipos += blocks_read;
+ return blocks;
+}
+
+/**
+ * close the DVD device and clean up.
+ */
+static int file_close(dvd_input_t dev)
+{
+ int ret = 0;
+
+ /* close file if it was open */
+
+ if (dev->fd >= 0) {
+ ret = close(dev->fd);
+ }
+
+ free(dev);
+
+ return ret;
+}
+
+/**
+ * Setup read functions with either libdvdcss or minimal DVD access.
+ */
+int dvdinput_setup(void *priv, dvd_logger_cb *logcb)
+{
+ void *dvdcss_library = NULL;
+
+#ifdef HAVE_DVDCSS_DVDCSS_H
+ /* linking to libdvdcss */
+ dvdcss_library = &dvdcss_library; /* Give it some value != NULL */
+
+#else
+ /* dlopening libdvdcss */
+
+#ifdef __APPLE__
+ #define CSS_LIB "libdvdcss.2.dylib"
+#elif defined(_WIN32)
+ #define CSS_LIB "libdvdcss-2.dll"
+#elif defined(__OS2__)
+ #define CSS_LIB "dvdcss2.dll"
+#elif defined(__OpenBSD__)
+ #define CSS_LIB "libdvdcss.so"
+#else
+ #define CSS_LIB "libdvdcss.so.2"
+#endif
+ dvdcss_library = dlopen(CSS_LIB, RTLD_LAZY);
+
+ if(dvdcss_library != NULL) {
+#ifdef __OS2__
+#define U_S "_"
+#else
+#define U_S
+#endif
+ DVDcss_open_stream = (dvdcss_t (*)(void *, dvdcss_stream_cb *))
+ dlsym(dvdcss_library, U_S "dvdcss_open_stream");
+ DVDcss_open = (dvdcss_t (*)(const char*))
+ dlsym(dvdcss_library, U_S "dvdcss_open");
+ DVDcss_close = (int (*)(dvdcss_t))
+ dlsym(dvdcss_library, U_S "dvdcss_close");
+ DVDcss_seek = (int (*)(dvdcss_t, int, int))
+ dlsym(dvdcss_library, U_S "dvdcss_seek");
+ DVDcss_read = (int (*)(dvdcss_t, void*, int, int))
+ dlsym(dvdcss_library, U_S "dvdcss_read");
+
+ if(dlsym(dvdcss_library, U_S "dvdcss_crack")) {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Old (pre-0.0.2) version of libdvdcss found. "
+ "libdvdread: You should get the latest version from "
+ "http://www.videolan.org/" );
+ dlclose(dvdcss_library);
+ dvdcss_library = NULL;
+ } else if(!DVDcss_open || !DVDcss_close || !DVDcss_seek
+ || !DVDcss_read) {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_ERROR,
+ "Missing symbols in %s, "
+ "this shouldn't happen !", CSS_LIB);
+ dlclose(dvdcss_library);
+ dvdcss_library = NULL;
+ }
+ }
+#endif /* HAVE_DVDCSS_DVDCSS_H */
+
+ if(dvdcss_library != NULL) {
+ /*
+ char *psz_method = getenv( "DVDCSS_METHOD" );
+ char *psz_verbose = getenv( "DVDCSS_VERBOSE" );
+ fprintf(stderr, "DVDCSS_METHOD %s\n", psz_method);
+ fprintf(stderr, "DVDCSS_VERBOSE %s\n", psz_verbose);
+ */
+
+ /* libdvdcss wrapper functions */
+ dvdinput_open = css_open;
+ dvdinput_close = css_close;
+ dvdinput_seek = css_seek;
+ dvdinput_title = css_title;
+ dvdinput_read = css_read;
+ return 1;
+
+ } else {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_WARN,
+ "Encrypted DVD support unavailable.");
+
+ /* libdvdcss replacement functions */
+ dvdinput_open = file_open;
+ dvdinput_close = file_close;
+ dvdinput_seek = file_seek;
+ dvdinput_title = file_title;
+ dvdinput_read = file_read;
+ return 0;
+ }
+}
diff --git a/libdvdread-embedded/src/dvd_input.h b/libdvdread-embedded/src/dvd_input.h
new file mode 100644
index 0000000..470cfa4
--- /dev/null
+++ b/libdvdread-embedded/src/dvd_input.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2001, 2002 Samuel Hocevar <sam@zoy.org>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDREAD_DVD_INPUT_H
+#define LIBDVDREAD_DVD_INPUT_H
+
+/**
+ * Defines and flags. Make sure they fit the libdvdcss API!
+ */
+#define DVDINPUT_NOFLAGS 0
+
+#define DVDINPUT_READ_DECRYPT (1 << 0)
+
+typedef struct dvd_input_s *dvd_input_t;
+
+#if defined( __MINGW32__ )
+# undef lseek
+# define lseek _lseeki64
+# undef off_t
+# define off_t off64_t
+# undef stat
+# define stat _stati64
+# undef fstat
+# define fstat _fstati64
+# undef wstat
+# define wstat _wstati64
+#endif
+
+#ifdef __ANDROID__
+# undef lseek
+# define lseek lseek64
+# undef off_t
+# define off_t off64_t
+#endif
+
+/**
+ * Function pointers that will be filled in by the input implementation.
+ * These functions provide the main API.
+ */
+extern dvd_input_t (*dvdinput_open) (void *, dvd_logger_cb *,
+ const char *,
+ dvd_reader_stream_cb *);
+extern int (*dvdinput_close) (dvd_input_t);
+extern int (*dvdinput_seek) (dvd_input_t, int);
+extern int (*dvdinput_title) (dvd_input_t, int);
+extern int (*dvdinput_read) (dvd_input_t, void *, int, int);
+
+/**
+ * Setup function accessed by dvd_reader.c. Returns 1 if there is CSS support.
+ */
+int dvdinput_setup(void *, dvd_logger_cb *);
+
+#endif /* LIBDVDREAD_DVD_INPUT_H */
diff --git a/libdvdread-embedded/src/dvd_reader.c b/libdvdread-embedded/src/dvd_reader.c
new file mode 100644
index 0000000..c4d9641
--- /dev/null
+++ b/libdvdread-embedded/src/dvd_reader.c
@@ -0,0 +1,1690 @@
+/*
+ * Copyright (C) 2001-2004 Billy Biggs <vektor@dumbterm.net>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>,
+ * Björn Englund <d4bjorn@dtek.chalmers.se>
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include <sys/types.h> /* off_t */
+#include <sys/stat.h> /* stat */
+#include <sys/time.h> /* For the timing of dvdcss_title crack. */
+#include <fcntl.h> /* open */
+#include <stdlib.h> /* free */
+#include <stdio.h> /* fprintf */
+#include <errno.h> /* errno, EIN* */
+#include <string.h> /* memcpy, strlen */
+#include <unistd.h> /* pclose */
+#include <limits.h> /* PATH_MAX */
+#include <dirent.h> /* opendir, readdir */
+#include <ctype.h> /* isalpha */
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__) || defined(__APPLE__)
+# define SYS_BSD 1
+#endif
+
+#if defined(__sun)
+# include <sys/mnttab.h>
+#elif defined(__APPLE__)
+# include <sys/param.h>
+# include <sys/ucred.h>
+# include <sys/mount.h>
+#elif defined(SYS_BSD)
+# include <fstab.h>
+#elif defined(__linux__)
+# include <mntent.h>
+# include <paths.h>
+#endif
+
+#include "dvdread/dvd_udf.h"
+#include "dvdread/dvd_reader.h"
+#include "dvd_input.h"
+#include "dvdread_internal.h"
+#include "md5.h"
+#include "dvdread/ifo_read.h"
+
+#if defined(_WIN32)
+# include <windows.h>
+# include "msvc/contrib/win32_cs.h"
+#endif
+
+/* misc win32 helpers */
+
+#ifdef _WIN32
+# ifndef HAVE_GETTIMEOFDAY
+ /* replacement gettimeofday implementation */
+# include <sys/timeb.h>
+static inline int _private_gettimeofday( struct timeval *tv, void *tz )
+{
+ struct timeb t;
+ ftime( &t );
+ tv->tv_sec = t.time;
+ tv->tv_usec = t.millitm * 1000;
+ return 0;
+}
+# define gettimeofday(TV, TZ) _private_gettimeofday((TV), (TZ))
+# endif
+#endif /* _WIN32 */
+
+/* Compat wrapper for stat() */
+
+#if defined(_WIN32)
+/* can't re-define stat (used in both function name and struct name) */
+typedef struct _stat64 dvdstat_t;
+static inline int dvdstat(const char *path, dvdstat_t *st)
+{
+ wchar_t *wpath, *it;
+ int ret;
+
+ wpath = _utf8_to_wchar(path);
+ if (!wpath) {
+ return -1;
+ }
+
+ /* need to strip possible trailing \\ */
+ for (it = wpath; *it; it++)
+ if ((*it == '\\' || *it == '/') && *(it+1) == 0)
+ *it = 0;
+
+ ret = _wstat64(wpath, st);
+ free(wpath);
+ return ret;
+}
+#else
+typedef struct stat dvdstat_t;
+static inline int dvdstat(const char *file, dvdstat_t *st) {
+ return stat(file, st);
+}
+#endif
+
+#if defined(_WIN32)
+/* UTF-8 aware version of opendir()/readdir() */
+
+#include <io.h>
+
+typedef struct {
+ intptr_t handle;
+ struct _wfinddata_t went;
+ struct dirent ent;
+} win32_dir_t;
+
+win32_dir_t *win32_opendir(const char *path)
+{
+ char *filespec;
+ wchar_t *wfilespec;
+ win32_dir_t *d;
+
+ d = calloc(1, sizeof(*d));
+ if (!d)
+ return NULL;
+
+ filespec = malloc(strlen(path) + 3);
+ if (!filespec) {
+ goto fail;
+ }
+ sprintf(filespec, "%s\\*", path);
+
+ wfilespec = _utf8_to_wchar(filespec);
+ free(filespec);
+ if (!wfilespec) {
+ goto fail;
+ }
+
+ d->handle = _wfindfirst(wfilespec, &d->went);
+ free(wfilespec);
+ if (d->handle != -1) {
+ return d;
+ }
+
+ fail:
+ free(d);
+ return NULL;
+}
+
+static struct dirent *win32_readdir(win32_dir_t *dir)
+{
+ if (dir->went.name[0]) {
+ if (!WideCharToMultiByte(CP_UTF8, 0, dir->went.name, -1, dir->ent.d_name, sizeof(dir->ent.d_name), NULL, NULL))
+ dir->ent.d_name[0] = 0; /* allow reading next */
+ dir->went.name[0] = 0;
+ _wfindnext(dir->handle, &dir->went);
+ return &dir->ent;
+ }
+
+ return NULL;
+}
+
+static void win32_closedir(win32_dir_t *dir)
+{
+ _findclose(dir->handle);
+ free(dir);
+}
+
+#define DIR win32_dir_t
+#define opendir win32_opendir
+#define readdir win32_readdir
+#define closedir win32_closedir
+
+#endif /* _WIN32 */
+
+#define DEFAULT_UDF_CACHE_LEVEL 1
+
+struct dvd_reader_device_s {
+ /* Basic information. */
+ int isImageFile;
+
+ /* Hack for keeping track of the css status.
+ * 0: no css, 1: perhaps (need init of keys), 2: have done init */
+ int css_state;
+ int css_title; /* Last title that we have called dvdinpute_title for. */
+
+ /* Information required for an image file. */
+ dvd_input_t dev;
+
+ /* Information required for a directory path drive. */
+ char *path_root;
+
+ /* Filesystem cache */
+ int udfcache_level; /* 0 - turned off, 1 - on */
+ void *udfcache;
+};
+
+#define TITLES_MAX 9
+
+struct dvd_file_s {
+ /* Basic information. */
+ dvd_reader_t *ctx;
+
+ /* Hack for selecting the right css title. */
+ int css_title;
+
+ /* Information required for an image file. */
+ uint32_t lb_start;
+ uint32_t seek_pos;
+
+ /* Information required for a directory path drive. */
+ size_t title_sizes[ TITLES_MAX ];
+ dvd_input_t title_devs[ TITLES_MAX ];
+
+ /* Calculated at open-time, size in blocks. */
+ ssize_t filesize;
+
+ /* Cache of the dvd_file. If not NULL, the cache corresponds to the whole
+ * dvd_file. Used only for IFO and BUP. */
+ unsigned char *cache;
+};
+
+/**
+ * Set the level of caching on udf
+ * level = 0 (no caching)
+ * level = 1 (caching filesystem info)
+ */
+int DVDUDFCacheLevel(dvd_reader_t *reader, int level)
+{
+ dvd_reader_device_t *dev = reader->rd;
+
+ if(level > 0) {
+ level = 1;
+ } else if(level < 0) {
+ return dev->udfcache_level;
+ }
+
+ dev->udfcache_level = level;
+
+ return level;
+}
+
+void *GetUDFCacheHandle(dvd_reader_t *reader)
+{
+ dvd_reader_device_t *dev = reader->rd;
+
+ return dev->udfcache;
+}
+
+void SetUDFCacheHandle(dvd_reader_t *reader, void *cache)
+{
+ dvd_reader_device_t *dev = reader->rd;
+
+ dev->udfcache = cache;
+}
+
+
+
+/* Loop over all titles and call dvdcss_title to crack the keys. */
+static int initAllCSSKeys( dvd_reader_t *ctx )
+{
+ dvd_reader_device_t *dvd = ctx->rd;
+ struct timeval all_s, all_e;
+ struct timeval t_s, t_e;
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ uint32_t start, len;
+ int title;
+
+ const char *nokeys_str = getenv("DVDREAD_NOKEYS");
+ if(nokeys_str != NULL)
+ return 0;
+
+ Log2(ctx,"Attempting to retrieve all CSS keys" );
+ Log2(ctx,"This can take a _long_ time, please be patient" );
+ gettimeofday(&all_s, NULL);
+
+ for( title = 0; title < 100; title++ ) {
+ gettimeofday( &t_s, NULL );
+ if( title == 0 ) {
+ strcpy( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
+ } else {
+ sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
+ }
+ start = UDFFindFile( ctx, filename, &len );
+ if( start != 0 && len != 0 ) {
+ /* Perform CSS key cracking for this title. */
+ Log3(ctx,"Get key for %s at 0x%08x",filename, start );
+ if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
+ Log1(ctx,"Error cracking CSS key for %s (0x%08x)", filename, start);
+ }
+ gettimeofday( &t_e, NULL );
+ Log3(ctx,"Elapsed time %ld", (long int) t_e.tv_sec - t_s.tv_sec );
+ }
+
+ if( title == 0 ) continue;
+
+ gettimeofday( &t_s, NULL );
+ sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 );
+ start = UDFFindFile( ctx, filename, &len );
+ if( start == 0 || len == 0 ) break;
+
+ /* Perform CSS key cracking for this title. */
+ Log3(ctx,"Get key for %s at 0x%08x",filename, start );
+ if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
+ Log1(ctx,"Error cracking CSS key for %s (0x%08x)", filename, start);
+ }
+ gettimeofday( &t_e, NULL );
+ Log3(ctx,"Elapsed time %ld", (long int) t_e.tv_sec - t_s.tv_sec );
+ }
+ title--;
+
+ Log3(ctx,"Found %d VTS's", title );
+ gettimeofday(&all_e, NULL);
+ Log3(ctx,"Elapsed time %ld", (long int) all_e.tv_sec - all_s.tv_sec );
+
+ return 0;
+}
+
+
+
+/**
+ * Open a DVD image or block device file or use stream_cb functions.
+ */
+static dvd_reader_device_t *DVDOpenImageFile( dvd_reader_t *ctx,
+ const char *location,
+ dvd_reader_stream_cb *stream_cb,
+ int have_css )
+{
+ dvd_reader_device_t *dvd;
+ dvd_input_t dev;
+
+ dev = dvdinput_open( ctx->priv, &ctx->logcb, location, stream_cb );
+ if( !dev ) {
+ Log0(ctx,"Can't open %s for reading", location );
+ return NULL;
+ }
+
+ dvd = calloc( 1, sizeof( dvd_reader_device_t ) );
+ if( !dvd ) {
+ dvdinput_close(dev);
+ return NULL;
+ }
+ dvd->isImageFile = 1;
+ dvd->dev = dev;
+
+ dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
+
+ if( have_css ) {
+ /* Only if DVDCSS_METHOD = title, a bit if it's disc or if
+ * DVDCSS_METHOD = key but region mismatch. Unfortunately we
+ * don't have that information. */
+
+ dvd->css_state = 1; /* Need key init. */
+ }
+
+ return dvd;
+}
+
+static dvd_reader_device_t *DVDOpenPath( const char *path_root )
+{
+ dvd_reader_device_t *dvd;
+
+ dvd = calloc( 1, sizeof( dvd_reader_device_t ) );
+ if( !dvd ) return NULL;
+ dvd->path_root = strdup( path_root );
+ if(!dvd->path_root) {
+ free(dvd);
+ return NULL;
+ }
+ dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
+
+ return dvd;
+}
+
+#if defined(__sun)
+/* /dev/rdsk/c0t6d0s0 (link to /devices/...)
+ /vol/dev/rdsk/c0t6d0/??
+ /vol/rdsk/<name> */
+static char *sun_block2char( const char *path )
+{
+ char *new_path;
+
+ /* Must contain "/dsk/" */
+ if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
+
+ /* Replace "/dsk/" with "/rdsk/" */
+ new_path = malloc( strlen(path) + 2 );
+ if(!new_path) return NULL;
+ strcpy( new_path, path );
+ strcpy( strstr( new_path, "/dsk/" ), "" );
+ strcat( new_path, "/rdsk/" );
+ strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
+
+ return new_path;
+}
+#endif
+
+#if defined(SYS_BSD)
+/* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recommended to _not_ use r
+ update: FreeBSD and DragonFly no longer uses the prefix so don't add it.
+ OpenBSD /dev/rcd0c, it needs to be the raw device
+ NetBSD /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others
+ Darwin /dev/rdisk0, it needs to be the raw device
+ BSD/OS /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do)
+ returns a string allocated with strdup. It should be freed when no longer
+ used. */
+static char *bsd_block2char( const char *path )
+{
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+ return (char *) strdup( path );
+#else
+ char *new_path;
+
+ /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */
+ if( strncmp( path, "/dev/", 5 ) || !strncmp( path, "/dev/r", 6 ) )
+ return (char *) strdup( path );
+
+ /* Replace "/dev/" with "/dev/r" */
+ new_path = malloc( strlen(path) + 2 );
+ if(!new_path) return NULL;
+ strcpy( new_path, "/dev/r" );
+ strcat( new_path, path + strlen( "/dev/" ) );
+
+ return new_path;
+#endif /* __FreeBSD__ || __DragonFly__ */
+}
+#endif
+
+static dvd_reader_t *DVDOpenCommon( void *priv,
+ const dvd_logger_cb *logcb,
+ const char *ppath,
+ dvd_reader_stream_cb *stream_cb )
+{
+ dvdstat_t fileinfo;
+ int ret, have_css, cdir = -1;
+ char *dev_name = NULL;
+ char *path = NULL, *new_path = NULL, *path_copy = NULL;
+ dvd_reader_t *ctx = calloc(1, sizeof(*ctx));
+ if(!ctx)
+ return NULL;
+
+ ctx->priv = priv;
+ if(logcb)
+ ctx->logcb = *logcb;
+
+#if defined(_WIN32) || defined(__OS2__)
+ int len;
+#endif
+
+ /* Try to open DVD using stream_cb functions */
+ if( priv != NULL && stream_cb != NULL )
+ {
+ have_css = dvdinput_setup( ctx->priv, &ctx->logcb );
+ ctx->rd = DVDOpenImageFile( ctx, NULL, stream_cb, have_css );
+ if(!ctx->rd)
+ {
+ free(ctx);
+ return NULL;
+ }
+ return ctx;
+ }
+
+ if( ppath == NULL )
+ goto DVDOpen_error;
+
+ path = strdup(ppath);
+ if( path == NULL )
+ goto DVDOpen_error;
+
+ /* Try to open libdvdcss or fall back to standard functions */
+ have_css = dvdinput_setup( ctx->priv, &ctx->logcb );
+
+#if defined(_WIN32) || defined(__OS2__)
+ /* Strip off the trailing \ if it is not a drive */
+ len = strlen(path);
+ if ((len > 1) &&
+ (path[len - 1] == '\\') &&
+ (path[len - 2] != ':'))
+ {
+ path[len-1] = '\0';
+ }
+#endif
+
+ ret = dvdstat( path, &fileinfo );
+
+ if( ret < 0 ) {
+
+ /* maybe "host:port" url? try opening it with acCeSS library */
+ if( strchr(path,':') ) {
+ ctx->rd = DVDOpenImageFile( ctx, path, NULL, have_css );
+ free(path);
+ if(!ctx->rd)
+ {
+ free(ctx);
+ return NULL;
+ }
+ return ctx;
+ }
+
+ /* If we can't stat the file, give up */
+ Log0(ctx, "Can't stat %s", path );
+ perror("");
+ goto DVDOpen_error;
+ }
+
+ /* First check if this is a block/char device or a file*/
+ if( S_ISBLK( fileinfo.st_mode ) ||
+ S_ISCHR( fileinfo.st_mode ) ||
+ S_ISREG( fileinfo.st_mode ) ) {
+
+ /**
+ * Block devices and regular files are assumed to be DVD-Video images.
+ */
+#if defined(__sun)
+ dev_name = sun_block2char( path );
+#elif defined(SYS_BSD)
+ dev_name = bsd_block2char( path );
+#else
+ dev_name = strdup( path );
+#endif
+ if(!dev_name)
+ goto DVDOpen_error;
+ ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
+ free( dev_name );
+ free(path);
+ if(!ctx->rd)
+ {
+ free(ctx);
+ return NULL;
+ }
+ return ctx;
+ } else if( S_ISDIR( fileinfo.st_mode ) ) {
+#if defined(SYS_BSD)
+ struct fstab* fe;
+#elif defined(__sun) || defined(__linux__)
+ FILE *mntfile;
+#endif
+
+ /* XXX: We should scream real loud here. */
+ if( !(path_copy = strdup( path ) ) )
+ goto DVDOpen_error;
+
+#ifndef _WIN32 /* win32 doesn't have realpath */
+ /* Also WIN32 does not have symlinks, so we don't need this bit of code. */
+
+ /* Resolve any symlinks and get the absolute dir name. */
+ {
+ new_path = realpath( path_copy, NULL );
+ if( new_path == NULL ) {
+ goto DVDOpen_error;
+ }
+ free(path_copy);
+ path_copy = new_path;
+ new_path = NULL;
+ }
+#endif
+
+ /**
+ * If we're being asked to open a directory, check if that directory
+ * is the mount point for a DVD-ROM which we can use instead.
+ */
+
+ if( strlen( path_copy ) > 1 ) {
+ if( path_copy[ strlen( path_copy ) - 1 ] == '/' ) {
+ path_copy[ strlen( path_copy ) - 1 ] = '\0';
+ }
+ }
+
+#if defined(_WIN32) || defined(__OS2__)
+ if( strlen( path_copy ) > 9 ) {
+ if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]),
+ "\\video_ts"))
+ path_copy[ strlen( path_copy ) - (9-1) ] = '\0';
+ }
+#endif
+ if( strlen( path_copy ) > 9 ) {
+ if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]),
+ "/video_ts" ) ) {
+ path_copy[ strlen( path_copy ) - 9 ] = '\0';
+ }
+ }
+
+ if(path_copy[0] == '\0') {
+ free( path_copy );
+ if( !(path_copy = strdup( "/" ) ) )
+ goto DVDOpen_error;
+ }
+
+#if defined(__APPLE__)
+ struct statfs s[128];
+ int r = getfsstat(NULL, 0, MNT_NOWAIT);
+ if (r > 0) {
+ if (r > 128)
+ r = 128;
+ r = getfsstat(s, r * sizeof(s[0]), MNT_NOWAIT);
+ int i;
+ for (i=0; i<r; i++) {
+ if (!strcmp(path_copy, s[i].f_mntonname)) {
+ dev_name = bsd_block2char(s[i].f_mntfromname);
+ Log3(ctx, "Attempting to use device %s"
+ " mounted on %s for CSS authentication",
+ dev_name,
+ s[i].f_mntonname);
+ ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
+ break;
+ }
+ }
+ }
+#elif defined(SYS_BSD)
+ if( ( fe = getfsfile( path_copy ) ) ) {
+ dev_name = bsd_block2char( fe->fs_spec );
+ Log3(ctx, "Attempting to use device %s"
+ " mounted on %s for CSS authentication",
+ dev_name,
+ fe->fs_file );
+ ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
+ }
+#elif defined(__sun)
+ mntfile = fopen( MNTTAB, "r" );
+ if( mntfile ) {
+ struct mnttab mp;
+ int res;
+
+ while( ( res = getmntent( mntfile, &mp ) ) != -1 ) {
+ if( res == 0 && !strcmp( mp.mnt_mountp, path_copy ) ) {
+ dev_name = sun_block2char( mp.mnt_special );
+ Log3(ctx, "Attempting to use device %s"
+ " mounted on %s for CSS authentication",
+ dev_name,
+ mp.mnt_mountp );
+ ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
+ break;
+ }
+ }
+ fclose( mntfile );
+ }
+#elif defined(__linux__)
+ mntfile = fopen( _PATH_MOUNTED, "r" );
+ if( mntfile ) {
+
+#ifdef HAVE_GETMNTENT_R
+ struct mntent *me, mbuf;
+ char buf [8192];
+ while( ( me = getmntent_r( mntfile, &mbuf, buf, sizeof(buf) ) ) ) {
+#else
+ struct mntent *me;
+ while( ( me = getmntent( mntfile ) ) ) {
+#endif
+ if( !strcmp( me->mnt_dir, path_copy ) ) {
+ Log3(ctx, "Attempting to use device %s"
+ " mounted on %s for CSS authentication",
+ me->mnt_fsname,
+ me->mnt_dir );
+ ctx->rd = DVDOpenImageFile( ctx, me->mnt_fsname, NULL, have_css );
+ dev_name = strdup(me->mnt_fsname);
+ break;
+ }
+ }
+ fclose( mntfile );
+ }
+#elif defined(_WIN32) || defined(__OS2__)
+#ifdef __OS2__
+ /* Use DVDOpenImageFile() only if it is a drive */
+ if(isalpha(path[0]) && path[1] == ':' &&
+ ( !path[2] ||
+ ((path[2] == '\\' || path[2] == '/') && !path[3])))
+#endif
+ ctx->rd = DVDOpenImageFile( ctx, path, NULL, have_css );
+#endif
+
+#if !defined(_WIN32) && !defined(__OS2__)
+ if( !dev_name ) {
+ Log0(ctx, "Couldn't find device name." );
+ } else if( !ctx->rd ) {
+ Log0(ctx, "Device %s inaccessible, "
+ "CSS authentication not available.", dev_name );
+ }
+#else
+ if( !ctx->rd ) {
+ Log0(ctx, "Device %s inaccessible, "
+ "CSS authentication not available.", path );
+ }
+#endif
+
+ free( dev_name );
+ dev_name = NULL;
+ free( path_copy );
+ path_copy = NULL;
+
+ /**
+ * If we've opened a drive, just use that.
+ */
+ if(ctx->rd)
+ {
+ free(path);
+ return ctx;
+ }
+ /**
+ * Otherwise, we now try to open the directory tree instead.
+ */
+ ctx->rd = DVDOpenPath( path );
+ free( path );
+ if(!ctx->rd)
+ {
+ free(ctx);
+ return NULL;
+ }
+ return ctx;
+ }
+
+DVDOpen_error:
+ /* If it's none of the above, screw it. */
+ Log0(ctx, "Could not open %s", path );
+ free( path );
+ free( path_copy );
+ if ( cdir >= 0 )
+ close( cdir );
+ free( new_path );
+ return NULL;
+}
+
+dvd_reader_t *DVDOpen( const char *ppath )
+{
+ return DVDOpenCommon( NULL, NULL, ppath, NULL );
+}
+
+dvd_reader_t *DVDOpenStream( void *stream,
+ dvd_reader_stream_cb *stream_cb )
+{
+ return DVDOpenCommon( stream, NULL, NULL, stream_cb );
+}
+
+dvd_reader_t *DVDOpen2( void *priv, const dvd_logger_cb *logcb,
+ const char *ppath )
+{
+ return DVDOpenCommon( priv, logcb, ppath, NULL );
+}
+
+dvd_reader_t *DVDOpenStream2( void *priv, const dvd_logger_cb *logcb,
+ dvd_reader_stream_cb *stream_cb )
+{
+ return DVDOpenCommon( priv, logcb, NULL, stream_cb );
+}
+
+void DVDClose( dvd_reader_t *dvd )
+{
+ if( dvd ) {
+ if( dvd->rd->dev ) dvdinput_close( dvd->rd->dev );
+ if( dvd->rd->path_root ) free( dvd->rd->path_root );
+ if( dvd->rd->udfcache ) FreeUDFCache( dvd->rd->udfcache );
+ free( dvd->rd );
+ free( dvd );
+ }
+}
+
+/**
+ * Open an unencrypted file on a DVD image file.
+ */
+static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *ctx, const char *filename,
+ int do_cache )
+{
+ uint32_t start, len;
+ dvd_file_t *dvd_file;
+
+ start = UDFFindFile( ctx, filename, &len );
+ if( !start ) {
+ Log0(ctx, "DVDOpenFileUDF:UDFFindFile %s failed", filename );
+ return NULL;
+ }
+
+ dvd_file = calloc( 1, sizeof( dvd_file_t ) );
+ if( !dvd_file ) {
+ Log0(ctx, "DVDOpenFileUDF:malloc failed" );
+ return NULL;
+ }
+ dvd_file->ctx = ctx;
+ dvd_file->lb_start = start;
+ dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
+
+ /* Read the whole file in cache (unencrypted) if asked and if it doesn't
+ * exceed 128KB */
+ if( do_cache && len < 64 * DVD_VIDEO_LB_LEN ) {
+ int ret;
+
+ dvd_file->cache = malloc( len );
+ if( !dvd_file->cache )
+ return dvd_file;
+
+ ret = InternalUDFReadBlocksRaw( ctx, dvd_file->lb_start,
+ dvd_file->filesize, dvd_file->cache,
+ DVDINPUT_NOFLAGS );
+ if( ret != dvd_file->filesize ) {
+ free( dvd_file->cache );
+ dvd_file->cache = NULL;
+ }
+ }
+
+ return dvd_file;
+}
+
+/**
+ * Searches for <file> in directory <path>, ignoring case.
+ * Returns 0 and full filename in <filename>.
+ * or -1 on file not found.
+ * or -2 on path not found.
+ */
+static int findDirFile( const char *path, const char *file, char *filename )
+{
+ DIR *dir;
+ struct dirent *ent;
+
+ dir = opendir( path );
+ if( !dir ) return -2;
+
+ while( ( ent = readdir( dir ) ) != NULL ) {
+ if( !strcasecmp( ent->d_name, file ) ) {
+ sprintf( filename, "%s%s%s", path,
+ ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
+ ent->d_name );
+ closedir(dir);
+ return 0;
+ }
+ }
+ closedir(dir);
+ return -1;
+}
+
+static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
+{
+ const char *nodirfile;
+ int ret;
+
+ /* Strip off the directory for our search */
+ if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
+ nodirfile = &(file[ 10 ]);
+ } else {
+ nodirfile = file;
+ }
+
+ ret = findDirFile( dvd->rd->path_root, nodirfile, filename );
+ if( ret < 0 ) {
+ char video_path[ PATH_MAX + 1 ];
+
+ /* Try also with adding the path, just in case. */
+ sprintf( video_path, "%s/VIDEO_TS/", dvd->rd->path_root );
+ ret = findDirFile( video_path, nodirfile, filename );
+ if( ret < 0 ) {
+ /* Try with the path, but in lower case. */
+ sprintf( video_path, "%s/video_ts/", dvd->rd->path_root );
+ ret = findDirFile( video_path, nodirfile, filename );
+ if( ret < 0 ) {
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * Open an unencrypted file from a DVD directory tree.
+ */
+static dvd_file_t *DVDOpenFilePath( dvd_reader_t *ctx, const char *filename )
+{
+ char full_path[ PATH_MAX + 1 ];
+ dvd_file_t *dvd_file;
+ dvdstat_t fileinfo;
+ dvd_input_t dev;
+
+ /* Get the full path of the file. */
+ if( !findDVDFile( ctx, filename, full_path ) ) {
+ Log0(ctx, "DVDOpenFilePath:findDVDFile %s failed", filename );
+ return NULL;
+ }
+
+ dev = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
+ if( !dev ) {
+ Log0(ctx, "DVDOpenFilePath:dvdinput_open %s failed", full_path );
+ return NULL;
+ }
+
+ dvd_file = calloc( 1, sizeof( dvd_file_t ) );
+ if( !dvd_file ) {
+ Log0(ctx, "DVDOpenFilePath:dvd_file malloc failed" );
+ dvdinput_close(dev);
+ return NULL;
+ }
+ dvd_file->ctx = ctx;
+
+ if( dvdstat( full_path, &fileinfo ) < 0 ) {
+ Log0(ctx, "Can't stat() %s.", filename );
+ free( dvd_file );
+ dvdinput_close( dev );
+ return NULL;
+ }
+ dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+ dvd_file->title_devs[ 0 ] = dev;
+ dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+
+ return dvd_file;
+}
+
+static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *ctx, int title, int menu )
+{
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ uint32_t start, len;
+ dvd_file_t *dvd_file;
+
+ if( title == 0 ) {
+ strcpy( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
+ } else {
+ sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
+ }
+ start = UDFFindFile( ctx, filename, &len );
+ if( start == 0 ) return NULL;
+
+ dvd_file = calloc( 1, sizeof( dvd_file_t ) );
+ if( !dvd_file ) return NULL;
+ dvd_file->ctx = ctx;
+ /*Hack*/ dvd_file->css_title = title << 1 | menu;
+ dvd_file->lb_start = start;
+ dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
+
+ /* Calculate the complete file size for every file in the VOBS */
+ if( !menu ) {
+ int cur;
+
+ for( cur = 2; cur < 10; cur++ ) {
+ sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
+ if( !UDFFindFile( ctx, filename, &len ) ) break;
+ dvd_file->filesize += len / DVD_VIDEO_LB_LEN;
+ }
+ }
+
+ if( ctx->rd->css_state == 1 /* Need key init */ ) {
+ initAllCSSKeys( ctx );
+ ctx->rd->css_state = 2;
+ }
+ /*
+ if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
+ Log0(ctx, "Error cracking CSS key for %s", filename );
+ }
+ */
+
+ return dvd_file;
+}
+
+static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *ctx, int title, int menu )
+{
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ char full_path[ PATH_MAX + 1 ];
+ dvdstat_t fileinfo;
+ dvd_file_t *dvd_file;
+
+ dvd_file = calloc( 1, sizeof( dvd_file_t ) );
+ if( !dvd_file ) return NULL;
+ dvd_file->ctx = ctx;
+ /*Hack*/ dvd_file->css_title = title << 1 | menu;
+
+ if( menu ) {
+ dvd_input_t dev;
+
+ if( title == 0 ) {
+ strcpy( filename, "VIDEO_TS.VOB" );
+ } else {
+ sprintf( filename, "VTS_%02i_0.VOB", title );
+ }
+ if( !findDVDFile( ctx, filename, full_path ) ) {
+ free( dvd_file );
+ return NULL;
+ }
+
+ dev = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
+ if( dev == NULL ) {
+ free( dvd_file );
+ return NULL;
+ }
+
+ if( dvdstat( full_path, &fileinfo ) < 0 ) {
+ Log0(ctx, "Can't stat() %s.", filename );
+ dvdinput_close(dev);
+ free( dvd_file );
+ return NULL;
+ }
+ dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+ dvd_file->title_devs[ 0 ] = dev;
+ dvdinput_title( dvd_file->title_devs[0], 0);
+ dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+
+ } else {
+ int i;
+
+ for( i = 0; i < TITLES_MAX; ++i ) {
+
+ sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
+ if( !findDVDFile( ctx, filename, full_path ) ) {
+ break;
+ }
+
+ if( dvdstat( full_path, &fileinfo ) < 0 ) {
+ Log0(ctx, "Can't stat() %s.", filename );
+ break;
+ }
+
+ dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+ dvd_file->title_devs[ i ] = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
+ dvdinput_title( dvd_file->title_devs[ i ], 0 );
+ dvd_file->filesize += dvd_file->title_sizes[ i ];
+ }
+ if( !dvd_file->title_devs[ 0 ] ) {
+ free( dvd_file );
+ return NULL;
+ }
+ }
+
+ return dvd_file;
+}
+
+dvd_file_t *DVDOpenFile( dvd_reader_t *ctx, int titlenum,
+ dvd_read_domain_t domain )
+{
+ dvd_reader_device_t *dvd = ctx->rd;
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ int do_cache = 0;
+
+ /* Check arguments. */
+ if( dvd == NULL || titlenum < 0 )
+ return NULL;
+
+ switch( domain ) {
+ case DVD_READ_INFO_FILE:
+ if( titlenum == 0 ) {
+ strcpy( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
+ } else {
+ sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
+ }
+ do_cache = 1;
+ break;
+ case DVD_READ_INFO_BACKUP_FILE:
+ if( titlenum == 0 ) {
+ strcpy( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
+ } else {
+ sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
+ }
+ do_cache = 1;
+ break;
+ case DVD_READ_MENU_VOBS:
+ if( dvd->isImageFile ) {
+ return DVDOpenVOBUDF( ctx, titlenum, 1 );
+ } else {
+ return DVDOpenVOBPath( ctx, titlenum, 1 );
+ }
+ break;
+ case DVD_READ_TITLE_VOBS:
+ if( titlenum == 0 ) return NULL;
+ if( dvd->isImageFile ) {
+ return DVDOpenVOBUDF( ctx, titlenum, 0 );
+ } else {
+ return DVDOpenVOBPath( ctx, titlenum, 0 );
+ }
+ break;
+ default:
+ Log1(ctx, "Invalid domain for file open." );
+ return NULL;
+ }
+
+ if( dvd->isImageFile ) {
+ return DVDOpenFileUDF( ctx, filename, do_cache );
+ } else {
+ return DVDOpenFilePath( ctx, filename );
+ }
+}
+
+void DVDCloseFile( dvd_file_t *dvd_file )
+{
+ dvd_reader_device_t *dvd = dvd_file->ctx->rd;
+ if( dvd_file && dvd ) {
+ if( !dvd->isImageFile ) {
+ int i;
+
+ for( i = 0; i < TITLES_MAX; ++i ) {
+ if( dvd_file->title_devs[ i ] ) {
+ dvdinput_close( dvd_file->title_devs[i] );
+ }
+ }
+ }
+
+ free( dvd_file->cache );
+ free( dvd_file );
+ dvd_file = NULL;
+ }
+}
+
+static int DVDFileStatVOBUDF( dvd_reader_t *dvd, int title,
+ int menu, dvd_stat_t *statbuf )
+{
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ uint32_t size;
+ off_t tot_size;
+ off_t parts_size[ 9 ];
+ int nr_parts = 0;
+ int n;
+
+ if( title == 0 )
+ strcpy( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
+ else
+ sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
+
+ if( !UDFFindFile( dvd, filename, &size ) )
+ return -1;
+
+ tot_size = size;
+ nr_parts = 1;
+ parts_size[ 0 ] = size;
+
+ if( !menu ) {
+ int cur;
+
+ for( cur = 2; cur < 10; cur++ ) {
+ sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
+ if( !UDFFindFile( dvd, filename, &size ) )
+ break;
+
+ parts_size[ nr_parts ] = size;
+ tot_size += size;
+ nr_parts++;
+ }
+ }
+
+ statbuf->size = tot_size;
+ statbuf->nr_parts = nr_parts;
+ for( n = 0; n < nr_parts; n++ )
+ statbuf->parts_size[ n ] = parts_size[ n ];
+
+ return 0;
+}
+
+
+static int DVDFileStatVOBPath( dvd_reader_t *dvd, int title,
+ int menu, dvd_stat_t *statbuf )
+{
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ char full_path[ PATH_MAX + 1 ];
+ dvdstat_t fileinfo;
+ off_t tot_size;
+ off_t parts_size[ 9 ];
+ int nr_parts = 0;
+ int n;
+
+ if( title == 0 )
+ strcpy( filename, "VIDEO_TS.VOB" );
+ else
+ sprintf( filename, "VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
+
+ if( !findDVDFile( dvd, filename, full_path ) )
+ return -1;
+
+ if( dvdstat( full_path, &fileinfo ) < 0 ) {
+ Log1(dvd, "Can't stat() %s.", filename );
+ return -1;
+ }
+
+ tot_size = fileinfo.st_size;
+ nr_parts = 1;
+ parts_size[ 0 ] = fileinfo.st_size;
+
+ if( !menu ) {
+ int cur;
+ for( cur = 2; cur < 10; cur++ ) {
+ sprintf( filename, "VTS_%02d_%d.VOB", title, cur );
+ if( !findDVDFile( dvd, filename, full_path ) )
+ break;
+
+ if( dvdstat( full_path, &fileinfo ) < 0 ) {
+ Log1(dvd, "Can't stat() %s.", filename );
+ break;
+ }
+
+ parts_size[ nr_parts ] = fileinfo.st_size;
+ tot_size += parts_size[ nr_parts ];
+ nr_parts++;
+ }
+ }
+
+ statbuf->size = tot_size;
+ statbuf->nr_parts = nr_parts;
+ for( n = 0; n < nr_parts; n++ )
+ statbuf->parts_size[ n ] = parts_size[ n ];
+
+ return 0;
+}
+
+
+int DVDFileStat( dvd_reader_t *reader, int titlenum,
+ dvd_read_domain_t domain, dvd_stat_t *statbuf )
+{
+ dvd_reader_device_t *dvd = reader->rd;
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ dvdstat_t fileinfo;
+ uint32_t size;
+
+ /* Check arguments. */
+ if( dvd == NULL || titlenum < 0 ) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ switch( domain ) {
+ case DVD_READ_INFO_FILE:
+ if( titlenum == 0 )
+ strcpy( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
+ else
+ sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
+
+ break;
+ case DVD_READ_INFO_BACKUP_FILE:
+ if( titlenum == 0 )
+ strcpy( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
+ else
+ sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
+
+ break;
+ case DVD_READ_MENU_VOBS:
+ if( dvd->isImageFile )
+ return DVDFileStatVOBUDF( reader, titlenum, 1, statbuf );
+ else
+ return DVDFileStatVOBPath( reader, titlenum, 1, statbuf );
+
+ break;
+ case DVD_READ_TITLE_VOBS:
+ if( titlenum == 0 )
+ return -1;
+
+ if( dvd->isImageFile )
+ return DVDFileStatVOBUDF( reader, titlenum, 0, statbuf );
+ else
+ return DVDFileStatVOBPath( reader, titlenum, 0, statbuf );
+
+ break;
+ default:
+ Log1(reader, "Invalid domain for file stat." );
+ errno = EINVAL;
+ return -1;
+ }
+
+ if( dvd->isImageFile ) {
+ if( UDFFindFile( reader, filename, &size ) ) {
+ statbuf->size = size;
+ statbuf->nr_parts = 1;
+ statbuf->parts_size[ 0 ] = size;
+ return 0;
+ }
+ } else {
+ char full_path[ PATH_MAX + 1 ];
+
+ if( findDVDFile( reader, filename, full_path ) ) {
+ if( dvdstat( full_path, &fileinfo ) < 0 )
+ Log1(reader, "Can't stat() %s.", filename );
+ else {
+ statbuf->size = fileinfo.st_size;
+ statbuf->nr_parts = 1;
+ statbuf->parts_size[ 0 ] = statbuf->size;
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
+/* Internal, but used from dvd_udf.c */
+int InternalUDFReadBlocksRaw( const dvd_reader_t *ctx, uint32_t lb_number,
+ size_t block_count, unsigned char *data,
+ int encrypted )
+{
+ int ret;
+
+ if( !ctx->rd->dev ) {
+ Log0(ctx, "Fatal error in block read." );
+ return -1;
+ }
+
+ ret = dvdinput_seek( ctx->rd->dev, (int) lb_number );
+ if( ret != (int) lb_number ) {
+ Log1(ctx, "Can't seek to block %u", lb_number );
+ return ret;
+ }
+
+ ret = dvdinput_read( ctx->rd->dev, (char *) data,
+ (int) block_count, encrypted );
+ return ret;
+}
+
+/* This is using a single input and starting from 'dvd_file->lb_start' offset.
+ *
+ * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
+ * into the buffer located at 'data' and if 'encrypted' is set
+ * descramble the data if it's encrypted. Returning either an
+ * negative error or the number of blocks read. */
+static int DVDReadBlocksUDF( const dvd_file_t *dvd_file, uint32_t offset,
+ size_t block_count, unsigned char *data,
+ int encrypted )
+{
+ /* If the cache is present and we don't need to decrypt, use the cache to
+ * feed the data */
+ if( dvd_file->cache && (encrypted & DVDINPUT_READ_DECRYPT) == 0 ) {
+ /* Check if we don't exceed the cache (or file) size */
+ if( block_count + offset > (size_t) dvd_file->filesize )
+ return 0;
+
+ /* Copy the cache at a specified offset into data. offset and block_count
+ * must be converted into bytes */
+ memcpy( data, dvd_file->cache + (off_t)offset * (off_t)DVD_VIDEO_LB_LEN,
+ (off_t)block_count * (off_t)DVD_VIDEO_LB_LEN );
+
+ /* return the amount of blocks copied */
+ return block_count;
+ } else {
+ /* use dvdinput access */
+ return InternalUDFReadBlocksRaw( dvd_file->ctx, dvd_file->lb_start + offset,
+ block_count, data, encrypted );
+ }
+}
+
+/* This is using possibly several inputs and starting from an offset of '0'.
+ *
+ * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
+ * into the buffer located at 'data' and if 'encrypted' is set
+ * descramble the data if it's encrypted. Returning either an
+ * negative error or the number of blocks read. */
+static int DVDReadBlocksPath( const dvd_file_t *dvd_file, unsigned int offset,
+ size_t block_count, unsigned char *data,
+ int encrypted )
+{
+ const dvd_reader_t *ctx = dvd_file->ctx;
+ int i;
+ int ret, ret2, off;
+
+ ret = 0;
+ ret2 = 0;
+ for( i = 0; i < TITLES_MAX; ++i ) {
+ if( !dvd_file->title_sizes[ i ] ) return 0; /* Past end of file */
+
+ if( offset < dvd_file->title_sizes[ i ] ) {
+ if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
+ off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
+ if( off < 0 || off != (int)offset ) {
+ Log1(ctx, "Can't seek to block %u", offset );
+ return off < 0 ? off : 0;
+ }
+ ret = dvdinput_read( dvd_file->title_devs[ i ], data,
+ (int)block_count, encrypted );
+ break;
+ } else {
+ size_t part1_size = dvd_file->title_sizes[ i ] - offset;
+ /* FIXME: Really needs to be a while loop.
+ * (This is only true if you try and read >1GB at a time) */
+
+ /* Read part 1 */
+ off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
+ if( off < 0 || off != (int)offset ) {
+ Log1(ctx, "Can't seek to block %u", offset );
+ return off < 0 ? off : 0;
+ }
+ ret = dvdinput_read( dvd_file->title_devs[ i ], data,
+ (int)part1_size, encrypted );
+ if( ret < 0 ) return ret;
+ /* FIXME: This is wrong if i is the last file in the set.
+ * also error from this read will not show in ret. */
+
+ /* Does the next part exist? If not then return now. */
+ if( i + 1 >= TITLES_MAX || !dvd_file->title_devs[ i + 1 ] )
+ return ret;
+
+ /* Read part 2 */
+ off = dvdinput_seek( dvd_file->title_devs[ i + 1 ], 0 );
+ if( off < 0 || off != 0 ) {
+ Log1(ctx, "Can't seek to block %d", 0 );
+ return off < 0 ? off : 0;
+ }
+ ret2 = dvdinput_read( dvd_file->title_devs[ i + 1 ],
+ data + ( part1_size
+ * (int64_t)DVD_VIDEO_LB_LEN ),
+ (int)(block_count - part1_size),
+ encrypted );
+ if( ret2 < 0 ) return ret2;
+ break;
+ }
+ } else {
+ offset -= dvd_file->title_sizes[ i ];
+ }
+ }
+
+ return ret + ret2;
+}
+
+/* This is broken reading more than 2Gb at a time is ssize_t is 32-bit. */
+ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset,
+ size_t block_count, unsigned char *data )
+{
+ dvd_reader_t *ctx = dvd_file->ctx;
+ dvd_reader_device_t *dvd = ctx->rd;
+ int ret;
+
+ /* Check arguments. */
+ if( dvd_file == NULL || offset < 0 || data == NULL )
+ return -1;
+
+ /* Hack, and it will still fail for multiple opens in a threaded app ! */
+ if( dvd->css_title != dvd_file->css_title ) {
+ dvd->css_title = dvd_file->css_title;
+ if( dvd->isImageFile ) {
+ dvdinput_title( dvd->dev, (int)dvd_file->lb_start );
+ }
+ /* Here each vobu has it's own dvdcss handle, so no need to update
+ else {
+ dvdinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start );
+ }*/
+ }
+
+ if( dvd->isImageFile ) {
+ ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset,
+ block_count, data, DVDINPUT_READ_DECRYPT );
+ } else {
+ ret = DVDReadBlocksPath( dvd_file, (unsigned int)offset,
+ block_count, data, DVDINPUT_READ_DECRYPT );
+ }
+
+ return (ssize_t)ret;
+}
+
+int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset )
+{
+ /* Check arguments. */
+ if( dvd_file == NULL || offset < 0 )
+ return -1;
+
+ if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN ) {
+ return -1;
+ }
+ dvd_file->seek_pos = (uint32_t) offset;
+ return offset;
+}
+
+int DVDFileSeekForce(dvd_file_t *dvd_file, int offset, int force_size)
+{
+ dvd_reader_t *ctx = dvd_file->ctx;
+ dvd_reader_device_t *dvd = ctx->rd;
+ /* Check arguments. */
+ if( dvd_file == NULL || offset <= 0 )
+ return -1;
+
+ if( dvd->isImageFile ) {
+ if( force_size < 0 )
+ force_size = (offset - 1) / DVD_VIDEO_LB_LEN + 1;
+ if( dvd_file->filesize < force_size ) {
+ dvd_file->filesize = force_size;
+ free(dvd_file->cache);
+ dvd_file->cache = NULL;
+ Log2(ctx, "Ignored size of file indicated in UDF.");
+ }
+ }
+
+ if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN )
+ return -1;
+
+ dvd_file->seek_pos = (uint32_t) offset;
+ return offset;
+}
+
+ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
+{
+ dvd_reader_t *ctx = dvd_file->ctx;
+ dvd_reader_device_t *dvd = ctx->rd;
+ unsigned char *secbuf_base, *secbuf;
+ unsigned int numsec, seek_sector, seek_byte;
+ int ret;
+
+ /* Check arguments. */
+ if( dvd_file == NULL || data == NULL || (ssize_t)byte_size < 0 )
+ return -1;
+
+ seek_sector = dvd_file->seek_pos / DVD_VIDEO_LB_LEN;
+ seek_byte = dvd_file->seek_pos % DVD_VIDEO_LB_LEN;
+
+ numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) +
+ ( ( ( seek_byte + byte_size ) % DVD_VIDEO_LB_LEN ) ? 1 : 0 );
+
+ secbuf_base = malloc( numsec * DVD_VIDEO_LB_LEN + 2048 );
+ if( !secbuf_base ) {
+ Log0(ctx, "Can't allocate memory for file read" );
+ return 0;
+ }
+ secbuf = (unsigned char *)(((uintptr_t)secbuf_base & ~((uintptr_t)2047)) + 2048);
+
+ if( dvd->isImageFile ) {
+ ret = DVDReadBlocksUDF( dvd_file, (uint32_t) seek_sector,
+ (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
+ } else {
+ ret = DVDReadBlocksPath( dvd_file, seek_sector,
+ (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
+ }
+
+ if( ret != (int) numsec ) {
+ free( secbuf_base );
+ return ret < 0 ? ret : 0;
+ }
+
+ memcpy( data, &(secbuf[ seek_byte ]), byte_size );
+ free( secbuf_base );
+
+ DVDFileSeekForce(dvd_file, dvd_file->seek_pos + byte_size, -1);
+ return byte_size;
+}
+
+ssize_t DVDFileSize( dvd_file_t *dvd_file )
+{
+ /* Check arguments. */
+ if( dvd_file == NULL )
+ return -1;
+
+ return dvd_file->filesize;
+}
+
+int DVDDiscID( dvd_reader_t *dvd, unsigned char *discid )
+{
+ struct md5_s ctx;
+ int title;
+ int title_sets;
+ int nr_of_files = 0;
+ ifo_handle_t *vmg_ifo;
+
+ /* Check arguments. */
+ if( dvd == NULL || discid == NULL )
+ return 0;
+
+ vmg_ifo = ifoOpen( dvd, 0 );
+ if( !vmg_ifo ) {
+ Log0(dvd, "DVDDiscId, failed to open VMG IFO" );
+ return -1;
+ }
+
+ title_sets = vmg_ifo->vmgi_mat->vmg_nr_of_title_sets + 1;
+ ifoClose( vmg_ifo );
+
+ if( title_sets > 10 )
+ title_sets = 10;
+
+ /* Go through the first IFO:s, in order, up until the tenth,
+ * and md5sum them, i.e VIDEO_TS.IFO and VTS_0?_0.IFO */
+ InitMD5( &ctx );
+ for( title = 0; title < title_sets; title++ ) {
+ dvd_file_t *dvd_file = DVDOpenFile( dvd, title, DVD_READ_INFO_FILE );
+ if( dvd_file != NULL ) {
+ ssize_t bytes_read;
+ ssize_t file_size = dvd_file->filesize * DVD_VIDEO_LB_LEN;
+ char *buffer_base = malloc( file_size + 2048 );
+
+ if( buffer_base == NULL ) {
+ DVDCloseFile( dvd_file );
+ Log0(dvd, "DVDDiscId, failed to allocate memory for file read" );
+ return -1;
+ }
+
+ char *buffer = (char *)(((uintptr_t)buffer_base & ~((uintptr_t)2047)) + 2048);
+
+ bytes_read = DVDReadBytes( dvd_file, buffer, file_size );
+ if( bytes_read != file_size ) {
+ Log1(dvd, "DVDDiscId read returned %zd bytes"
+ ", wanted %zd", bytes_read, file_size );
+ DVDCloseFile( dvd_file );
+ free( buffer_base );
+ return -1;
+ }
+
+ AddMD5( &ctx, buffer, file_size );
+
+ DVDCloseFile( dvd_file );
+ free( buffer_base );
+ nr_of_files++;
+ }
+ }
+ EndMD5( &ctx );
+ memcpy( discid, ctx.buf, 16 );
+ if(!nr_of_files)
+ return -1;
+
+ return 0;
+}
+
+
+int DVDISOVolumeInfo( dvd_reader_t *ctx,
+ char *volid, unsigned int volid_size,
+ unsigned char *volsetid, unsigned int volsetid_size )
+{
+ dvd_reader_device_t *dvd = ctx->rd;
+ unsigned char *buffer, *buffer_base;
+ int ret;
+
+ /* Check arguments. */
+ if( dvd == NULL )
+ return 0;
+
+ if( dvd->dev == NULL ) {
+ /* No block access, so no ISO... */
+ return -1;
+ }
+
+ buffer_base = malloc( DVD_VIDEO_LB_LEN + 2048 );
+
+ if( buffer_base == NULL ) {
+ Log0(ctx, "DVDISOVolumeInfo, failed to "
+ "allocate memory for file read" );
+ return -1;
+ }
+
+ buffer = (unsigned char *)(((uintptr_t)buffer_base & ~((uintptr_t)2047)) + 2048);
+
+ ret = InternalUDFReadBlocksRaw( ctx, 16, 1, buffer, 0 );
+ if( ret != 1 ) {
+ Log0(ctx, "DVDISOVolumeInfo, failed to "
+ "read ISO9660 Primary Volume Descriptor" );
+ free( buffer_base );
+ return -1;
+ }
+
+ if( (volid != NULL) && (volid_size > 0) ) {
+ unsigned int n;
+ for(n = 0; n < 32; n++) {
+ if(buffer[40+n] == 0x20) {
+ break;
+ }
+ }
+
+ if(volid_size > n+1) {
+ volid_size = n+1;
+ }
+
+ memcpy(volid, &buffer[40], volid_size-1);
+ volid[volid_size-1] = '\0';
+ }
+
+ if( (volsetid != NULL) && (volsetid_size > 0) ) {
+ if(volsetid_size > 128) {
+ volsetid_size = 128;
+ }
+ memcpy(volsetid, &buffer[190], volsetid_size);
+ }
+ free( buffer_base );
+ return 0;
+}
+
+
+int DVDUDFVolumeInfo( dvd_reader_t *ctx,
+ char *volid, unsigned int volid_size,
+ unsigned char *volsetid, unsigned int volsetid_size )
+{
+ int ret;
+ /* Check arguments. */
+ if( ctx == NULL || ctx->rd == NULL )
+ return -1;
+
+ if( ctx->rd->dev == NULL ) {
+ /* No block access, so no UDF VolumeSet Identifier */
+ return -1;
+ }
+
+ if( (volid != NULL) && (volid_size > 0) ) {
+ ret = UDFGetVolumeIdentifier(ctx, volid, volid_size);
+ if(!ret) {
+ return -1;
+ }
+ }
+ if( (volsetid != NULL) && (volsetid_size > 0) ) {
+ ret = UDFGetVolumeSetIdentifier(ctx, volsetid, volsetid_size);
+ if(!ret) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/libdvdread-embedded/src/dvd_udf.c b/libdvdread-embedded/src/dvd_udf.c
new file mode 100644
index 0000000..4d90ba4
--- /dev/null
+++ b/libdvdread-embedded/src/dvd_udf.c
@@ -0,0 +1,1033 @@
+/*
+ * This code is based on dvdudf by:
+ * Christian Wolff <scarabaeus@convergence.de>.
+ *
+ * Modifications by:
+ * Billy Biggs <vektor@dumbterm.net>.
+ * Björn Englund <d4bjorn@dtek.chalmers.se>.
+ *
+ * dvdudf: parse and read the UDF volume information of a DVD Video
+ * Copyright (C) 1999 Christian Wolff for convergence integrated media
+ * GmbH The author can be reached at scarabaeus@convergence.de, the
+ * project's page is at http://linuxtv.org/dvd/
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "dvdread_internal.h"
+#include "dvdread/dvd_reader.h"
+#include "dvdread/dvd_udf.h"
+
+/* It's required to either fail or deliver all the blocks asked for. */
+static int DVDReadLBUDF( dvd_reader_t *ctx, uint32_t lb_number,
+ size_t block_count, unsigned char *data,
+ int encrypted )
+{
+ size_t count = block_count;
+
+ while(count > 0) {
+ int ret;
+
+ ret = InternalUDFReadBlocksRaw(ctx, lb_number, count, data + DVD_VIDEO_LB_LEN * (block_count - count), encrypted);
+
+ if(ret <= 0) {
+ /* One of the reads failed or nothing more to read, too bad.
+ * We won't even bother returning the reads that went ok. */
+ return ret;
+ }
+
+ count -= (size_t)ret;
+ lb_number += (uint32_t)ret;
+ }
+
+ return block_count;
+}
+
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+struct Partition {
+ int valid;
+ uint16_t Flags;
+ uint16_t Number;
+ char Contents[32];
+ uint32_t AccessType;
+ uint32_t Start;
+ uint32_t Length;
+};
+
+struct AD {
+ uint32_t Location;
+ uint32_t Length;
+ uint8_t Flags;
+ uint16_t Partition;
+};
+
+struct extent_ad {
+ uint32_t location;
+ uint32_t length;
+};
+
+struct avdp_t {
+ struct extent_ad mvds;
+ struct extent_ad rvds;
+};
+
+struct pvd_t {
+ uint8_t VolumeIdentifier[32];
+ uint8_t VolumeSetIdentifier[128];
+};
+
+struct lbudf {
+ uint32_t lb;
+ uint8_t *data;
+ /* needed for proper freeing */
+ uint8_t *data_base;
+};
+
+struct icbmap {
+ uint32_t lbn;
+ struct AD file;
+ uint8_t filetype;
+};
+
+struct udf_cache {
+ int avdp_valid;
+ struct avdp_t avdp;
+ int pvd_valid;
+ struct pvd_t pvd;
+ int partition_valid;
+ struct Partition partition;
+ int rooticb_valid;
+ struct AD rooticb;
+ int lb_num;
+ struct lbudf *lbs;
+ int map_num;
+ struct icbmap *maps;
+};
+
+typedef enum {
+ PartitionCache, RootICBCache, LBUDFCache, MapCache, AVDPCache, PVDCache
+} UDFCacheType;
+
+void FreeUDFCache(void *cache)
+{
+ struct udf_cache *c = (struct udf_cache *)cache;
+ if(c == NULL)
+ return;
+
+ if(c->lbs) {
+ int n;
+ for(n = 0; n < c->lb_num; n++)
+ free(c->lbs[n].data_base);
+ free(c->lbs);
+ }
+ if(c->maps)
+ free(c->maps);
+ free(c);
+}
+
+
+static int GetUDFCache(dvd_reader_t *ctx, UDFCacheType type,
+ uint32_t nr, void *data)
+{
+ int n;
+ struct udf_cache *c;
+
+ if(DVDUDFCacheLevel(ctx, -1) <= 0)
+ return 0;
+
+ c = (struct udf_cache *)GetUDFCacheHandle(ctx);
+
+ if(c == NULL)
+ return 0;
+
+ switch(type) {
+ case AVDPCache:
+ if(c->avdp_valid) {
+ *(struct avdp_t *)data = c->avdp;
+ return 1;
+ }
+ break;
+ case PVDCache:
+ if(c->pvd_valid) {
+ *(struct pvd_t *)data = c->pvd;
+ return 1;
+ }
+ break;
+ case PartitionCache:
+ if(c->partition_valid) {
+ *(struct Partition *)data = c->partition;
+ return 1;
+ }
+ break;
+ case RootICBCache:
+ if(c->rooticb_valid) {
+ *(struct AD *)data = c->rooticb;
+ return 1;
+ }
+ break;
+ case LBUDFCache:
+ for(n = 0; n < c->lb_num; n++) {
+ if(c->lbs[n].lb == nr) {
+ *(uint8_t **)data = c->lbs[n].data;
+ return 1;
+ }
+ }
+ break;
+ case MapCache:
+ for(n = 0; n < c->map_num; n++) {
+ if(c->maps[n].lbn == nr) {
+ *(struct icbmap *)data = c->maps[n];
+ return 1;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int SetUDFCache(dvd_reader_t *ctx, UDFCacheType type,
+ uint32_t nr, void *data)
+{
+ int n;
+ struct udf_cache *c;
+ void *tmp;
+
+ if(DVDUDFCacheLevel(ctx, -1) <= 0)
+ return 0;
+
+ c = (struct udf_cache *)GetUDFCacheHandle(ctx);
+
+ if(c == NULL) {
+ c = calloc(1, sizeof(struct udf_cache));
+ /* Log3(ctx, "calloc: %d", sizeof(struct udf_cache)); */
+ if(c == NULL)
+ return 0;
+ SetUDFCacheHandle(ctx, c);
+ }
+
+
+ switch(type) {
+ case AVDPCache:
+ c->avdp = *(struct avdp_t *)data;
+ c->avdp_valid = 1;
+ break;
+ case PVDCache:
+ c->pvd = *(struct pvd_t *)data;
+ c->pvd_valid = 1;
+ break;
+ case PartitionCache:
+ c->partition = *(struct Partition *)data;
+ c->partition_valid = 1;
+ break;
+ case RootICBCache:
+ c->rooticb = *(struct AD *)data;
+ c->rooticb_valid = 1;
+ break;
+ case LBUDFCache:
+ for(n = 0; n < c->lb_num; n++) {
+ if(c->lbs[n].lb == nr) {
+ /* replace with new data */
+ c->lbs[n].data_base = ((uint8_t **)data)[0];
+ c->lbs[n].data = ((uint8_t **)data)[1];
+ c->lbs[n].lb = nr;
+ return 1;
+ }
+ }
+ c->lb_num++;
+ tmp = realloc(c->lbs, c->lb_num * sizeof(struct lbudf));
+ /*
+ Log3(ctx, "realloc lb: %d * %d = %d",
+ c->lb_num, sizeof(struct lbudf),
+ c->lb_num * sizeof(struct lbudf));
+ */
+ if(tmp == NULL) {
+ if(c->lbs) free(c->lbs);
+ c->lb_num = 0;
+ return 0;
+ }
+ c->lbs = tmp;
+ c->lbs[n].data_base = ((uint8_t **)data)[0];
+ c->lbs[n].data = ((uint8_t **)data)[1];
+ c->lbs[n].lb = nr;
+ break;
+ case MapCache:
+ for(n = 0; n < c->map_num; n++) {
+ if(c->maps[n].lbn == nr) {
+ /* replace with new data */
+ c->maps[n] = *(struct icbmap *)data;
+ c->maps[n].lbn = nr;
+ return 1;
+ }
+ }
+ c->map_num++;
+ tmp = realloc(c->maps, c->map_num * sizeof(struct icbmap));
+ /*
+ Log3(ctx, "realloc maps: %d * %d = %d\n",
+ c->map_num, sizeof(struct icbmap),
+ c->map_num * sizeof(struct icbmap));
+ */
+ if(tmp == NULL) {
+ if(c->maps) free(c->maps);
+ c->map_num = 0;
+ return 0;
+ }
+ c->maps = tmp;
+ c->maps[n] = *(struct icbmap *)data;
+ c->maps[n].lbn = nr;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* For direct data access, LSB first */
+#define GETN1(p) ((uint8_t)data[p])
+#define GETN2(p) ((uint16_t)data[p] | ((uint16_t)data[(p) + 1] << 8))
+#define GETN3(p) ((uint32_t)data[p] | ((uint32_t)data[(p) + 1] << 8) \
+ | ((uint32_t)data[(p) + 2] << 16))
+#define GETN4(p) ((uint32_t)data[p] \
+ | ((uint32_t)data[(p) + 1] << 8) \
+ | ((uint32_t)data[(p) + 2] << 16) \
+ | ((uint32_t)data[(p) + 3] << 24))
+/* This is wrong with regard to endianness */
+#define GETN(p, n, target) memcpy(target, &data[p], n)
+
+static int Unicodedecode( uint8_t *data, int len, char *target )
+{
+ int p = 1, i = 0;
+ int err = 0;
+
+ if( ( data[ 0 ] == 8 ) || ( data[ 0 ] == 16 ) ) do {
+ if( data[ 0 ] == 16 ) err |= data[p++]; /* character cannot be converted to 8bit, return error */
+ if( p < len ) {
+ target[ i++ ] = data[ p++ ];
+ }
+ } while( p < len );
+
+ target[ i ] = '\0';
+ return !err;
+}
+
+static int UDFDescriptor( uint8_t *data, uint16_t *TagID )
+{
+ *TagID = GETN2(0);
+ /* TODO: check CRC 'n stuff */
+ return 0;
+}
+
+static int UDFExtentAD( uint8_t *data, uint32_t *Length, uint32_t *Location )
+{
+ *Length = GETN4(0);
+ *Location = GETN4(4);
+ return 0;
+}
+
+static int UDFShortAD( uint8_t *data, struct AD *ad,
+ struct Partition *partition )
+{
+ ad->Length = GETN4(0);
+ ad->Flags = ad->Length >> 30;
+ ad->Length &= 0x3FFFFFFF;
+ ad->Location = GETN4(4);
+ ad->Partition = partition->Number; /* use number of current partition */
+ return 0;
+}
+
+static int UDFLongAD( uint8_t *data, struct AD *ad )
+{
+ ad->Length = GETN4(0);
+ ad->Flags = ad->Length >> 30;
+ ad->Length &= 0x3FFFFFFF;
+ ad->Location = GETN4(4);
+ ad->Partition = GETN2(8);
+ /* GETN(10, 6, Use); */
+ return 0;
+}
+
+static int UDFExtAD( uint8_t *data, struct AD *ad )
+{
+ ad->Length = GETN4(0);
+ ad->Flags = ad->Length >> 30;
+ ad->Length &= 0x3FFFFFFF;
+ ad->Location = GETN4(12);
+ ad->Partition = GETN2(16);
+ /* GETN(10, 6, Use); */
+ return 0;
+}
+
+static int UDFICB( uint8_t *data, uint8_t *FileType, uint16_t *Flags )
+{
+ *FileType = GETN1(11);
+ *Flags = GETN2(18);
+ return 0;
+}
+
+
+static int UDFPartition( uint8_t *data, uint16_t *Flags, uint16_t *Number,
+ char *Contents, uint32_t *Start, uint32_t *Length )
+{
+ *Flags = GETN2(20);
+ *Number = GETN2(22);
+ GETN(24, 32, Contents);
+ *Start = GETN4(188);
+ *Length = GETN4(192);
+ return 0;
+}
+
+/**
+ * Reads the volume descriptor and checks the parameters. Returns 0 on OK, 1
+ * on error.
+ */
+static int UDFLogVolume( uint8_t *data )
+{
+ uint32_t lbsize;
+ lbsize = GETN4(212); /* should be 2048 */
+ /* MT_L = GETN4(264); */ /* should be 6 */
+ /* N_PM = GETN4(268); */ /* should be 1 */
+ if (lbsize != DVD_VIDEO_LB_LEN)
+ return 1;
+
+ return 0;
+}
+
+static int UDFFileEntry( uint8_t *data, uint8_t *FileType,
+ struct Partition *partition, struct AD *ad )
+{
+ uint16_t flags;
+ uint32_t L_EA, L_AD;
+ unsigned int p;
+ struct AD tmpad;
+
+ UDFICB( &data[ 16 ], FileType, &flags );
+
+ /* Init ad for an empty file (i.e. there isn't a AD, L_AD == 0 ) */
+ ad->Length = GETN4( 60 ); /* Really 8 bytes a 56 */
+ ad->Flags = 0;
+ ad->Location = 0; /* what should we put here? */
+ ad->Partition = partition->Number; /* use number of current partition */
+
+ L_EA = GETN4( 168 );
+ L_AD = GETN4( 172 );
+
+ if (176 + L_EA + L_AD > DVD_VIDEO_LB_LEN)
+ return 0;
+
+ p = 176 + L_EA;
+ while( p < 176 + L_EA + L_AD ) {
+ switch( flags & 0x0007 ) {
+ case 0:
+ UDFShortAD( &data[ p ], &tmpad, partition );
+ if( ad->Location == 0 ) /* We do not support more than one AD */
+ *ad = tmpad; /* This was exploited by copy protections adding empty AD */
+ p += 8;
+ break;
+ case 1: /* FIXME ? UDF 2.60 2.3.7.1 Only Short Allocation Descriptors shall be used */
+ UDFLongAD( &data[ p ], ad );
+ p += 16;
+ break;
+ case 2:
+ UDFExtAD( &data[ p ], ad );
+ p += 20;
+ break;
+ case 3:
+ switch( L_AD ) {
+ case 8:
+ UDFShortAD( &data[ p ], ad, partition );
+ break;
+ case 16:
+ UDFLongAD( &data[ p ], ad );
+ break;
+ case 20:
+ UDFExtAD( &data[ p ], ad );
+ break;
+ }
+ p += L_AD;
+ break;
+ default:
+ p += L_AD;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int UDFFileIdentifier( uint8_t *data, uint8_t *FileCharacteristics,
+ char *FileName, struct AD *FileICB )
+{
+ uint8_t L_FI;
+ uint16_t L_IU;
+
+ *FileCharacteristics = GETN1(18);
+ L_FI = GETN1(19);
+ UDFLongAD(&data[20], FileICB);
+ L_IU = GETN2(36);
+ if (L_FI) {
+ if (!Unicodedecode(&data[38 + L_IU], L_FI, FileName)) FileName[0] = 0;
+ } else FileName[0] = '\0';
+ return 4 * ((38 + L_FI + L_IU + 3) / 4);
+}
+
+/**
+ * Maps ICB to FileAD
+ * ICB: Location of ICB of directory to scan
+ * FileType: Type of the file
+ * File: Location of file the ICB is pointing to
+ * return 1 on success, 0 on error;
+ */
+static int UDFMapICB( dvd_reader_t *ctx, struct AD ICB, uint8_t *FileType,
+ struct Partition *partition, struct AD *File )
+{
+ uint8_t LogBlock_base[DVD_VIDEO_LB_LEN + 2048];
+ uint8_t *LogBlock = (uint8_t *)(((uintptr_t)LogBlock_base & ~((uintptr_t)2047)) + 2048);
+ uint32_t lbnum;
+ uint16_t TagID;
+ struct icbmap tmpmap;
+ int ret;
+
+ lbnum = partition->Start + ICB.Location;
+ tmpmap.lbn = lbnum;
+ if(GetUDFCache(ctx, MapCache, lbnum, &tmpmap)) {
+ *FileType = tmpmap.filetype;
+ memcpy(File, &tmpmap.file, sizeof(tmpmap.file));
+ return 1;
+ }
+
+ do {
+ ret = DVDReadLBUDF( ctx, lbnum++, 1, LogBlock, 0 );
+ if( ret < 0 ) {
+ return ret;
+ }
+ else if( ret == 0 ) {
+ TagID = 0;
+ }
+ else {
+ UDFDescriptor( LogBlock, &TagID );
+ }
+
+ if( TagID == FileEntry ) {
+ UDFFileEntry( LogBlock, FileType, partition, File );
+ memcpy(&tmpmap.file, File, sizeof(tmpmap.file));
+ tmpmap.filetype = *FileType;
+ SetUDFCache(ctx, MapCache, tmpmap.lbn, &tmpmap);
+ return 1;
+ };
+ } while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 )
+ / DVD_VIDEO_LB_LEN ) && ( TagID != FileEntry ) );
+
+ return 0;
+}
+
+/**
+ * Dir: Location of directory to scan
+ * FileName: Name of file to look for
+ * FileICB: Location of ICB of the found file
+ * return 1 on success, 0 on error;
+ */
+static int UDFScanDir( dvd_reader_t *ctx, struct AD Dir, char *FileName,
+ struct Partition *partition, struct AD *FileICB,
+ int cache_file_info)
+{
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ uint8_t directory_base[ 2 * DVD_VIDEO_LB_LEN + 2048];
+ uint8_t *directory = (uint8_t *)(((uintptr_t)directory_base & ~((uintptr_t)2047)) + 2048);
+ uint32_t lbnum;
+ uint16_t TagID;
+ uint8_t filechar;
+ unsigned int p;
+ uint8_t *cached_dir_base = NULL, *cached_dir;
+ uint32_t dir_lba;
+ struct AD tmpICB;
+ int ret;
+
+ /* Scan dir for ICB of file */
+ lbnum = partition->Start + Dir.Location;
+
+ if(DVDUDFCacheLevel(ctx, -1) > 0) {
+ int found = 0;
+ int in_cache = 0;
+
+ /* caching */
+
+ if(!GetUDFCache(ctx, LBUDFCache, lbnum, &cached_dir)) {
+ dir_lba = (Dir.Length + DVD_VIDEO_LB_LEN) / DVD_VIDEO_LB_LEN;
+ if((cached_dir_base = malloc(dir_lba * DVD_VIDEO_LB_LEN + 2048)) == NULL)
+ return 0;
+ cached_dir = (uint8_t *)(((uintptr_t)cached_dir_base & ~((uintptr_t)2047)) + 2048);
+ ret = DVDReadLBUDF( ctx, lbnum, dir_lba, cached_dir, 0);
+ if( ret <= 0 ) {
+ free(cached_dir_base);
+ cached_dir_base = NULL;
+ cached_dir = NULL;
+ if( ret < 0 )
+ return ret;
+ }
+ /*
+ if(cached_dir) {
+ Log3(ctx, "malloc dir: %d", dir_lba * DVD_VIDEO_LB_LEN);
+ }
+ */
+ {
+ uint8_t *data[2];
+ data[0] = cached_dir_base;
+ data[1] = cached_dir;
+ SetUDFCache(ctx, LBUDFCache, lbnum, data);
+ }
+ } else
+ in_cache = 1;
+
+ if(cached_dir == NULL) {
+ free(cached_dir_base);
+ return 0;
+ }
+
+ p = 0;
+
+ while( p < Dir.Length ) {
+
+ UDFDescriptor( &cached_dir[ p ], &TagID );
+ if( TagID == FileIdentifierDescriptor ) {
+ p += UDFFileIdentifier( &cached_dir[ p ], &filechar,
+ filename, &tmpICB );
+ if(cache_file_info && !in_cache) {
+ uint8_t tmpFiletype;
+ struct AD tmpFile;
+
+ if( !strcasecmp( FileName, filename ) ) {
+ memcpy(FileICB, &tmpICB, sizeof(tmpICB));
+ found = 1;
+ }
+ if(!UDFMapICB(ctx, tmpICB, &tmpFiletype, partition, &tmpFile))
+ return 0;
+
+ } else {
+ if( !strcasecmp( FileName, filename ) ) {
+ memcpy(FileICB, &tmpICB, sizeof(tmpICB));
+ return 1;
+ }
+ }
+ } else {
+ if(cache_file_info && (!in_cache) && found)
+ return 1;
+ return 0;
+ }
+ }
+ if(cache_file_info && (!in_cache) && found)
+ return 1;
+ return 0;
+ }
+
+ if( DVDReadLBUDF( ctx, lbnum, 2, directory, 0 ) <= 0 )
+ return 0;
+
+ p = 0;
+ while( p < Dir.Length ) {
+ if( p > DVD_VIDEO_LB_LEN ) {
+ ++lbnum;
+ p -= DVD_VIDEO_LB_LEN;
+ Dir.Length -= DVD_VIDEO_LB_LEN;
+ if( DVDReadLBUDF( ctx, lbnum, 2, directory, 0 ) <= 0 ) {
+ return 0;
+ }
+ }
+ UDFDescriptor( &directory[ p ], &TagID );
+ if( TagID == FileIdentifierDescriptor ) {
+ p += UDFFileIdentifier( &directory[ p ], &filechar,
+ filename, FileICB );
+ if( !strcasecmp( FileName, filename ) ) {
+ return 1;
+ }
+ } else
+ return 0;
+ }
+
+ return 0;
+}
+
+
+static int UDFGetAVDP( dvd_reader_t *ctx,
+ struct avdp_t *avdp)
+{
+ uint8_t Anchor_base[ DVD_VIDEO_LB_LEN + 2048 ];
+ uint8_t *Anchor = (uint8_t *)(((uintptr_t)Anchor_base & ~((uintptr_t)2047)) + 2048);
+ uint32_t lbnum, MVDS_location, MVDS_length;
+ uint16_t TagID;
+ uint32_t lastsector;
+ int terminate;
+ struct avdp_t;
+ int ret;
+
+ if(GetUDFCache(ctx, AVDPCache, 0, avdp))
+ return 1;
+
+ /* Find Anchor */
+ lastsector = 0;
+ lbnum = 256; /* Try #1, prime anchor */
+ terminate = 0;
+
+ for(;;) {
+ ret = DVDReadLBUDF( ctx, lbnum, 1, Anchor, 0 );
+ if( ret < 0 ) {
+ return ret;
+ }
+ else if( ret == 0 ) {
+ TagID = 0;
+ }
+ else {
+ UDFDescriptor( Anchor, &TagID );
+ }
+ if (TagID != AnchorVolumeDescriptorPointer) {
+ /* Not an anchor */
+ if( terminate ) return 0; /* Final try failed */
+
+ if( lastsector ) {
+ /* We already found the last sector. Try #3, alternative
+ * backup anchor. If that fails, don't try again.
+ */
+ lbnum = lastsector;
+ terminate = 1;
+ } else {
+ /* TODO: Find last sector of the disc (this is optional). */
+ if( lastsector )
+ /* Try #2, backup anchor */
+ lbnum = lastsector - 256;
+ else
+ /* Unable to find last sector */
+ return 0;
+ }
+ } else
+ /* It's an anchor! We can leave */
+ break;
+ }
+ /* Main volume descriptor */
+ UDFExtentAD( &Anchor[ 16 ], &MVDS_length, &MVDS_location );
+ avdp->mvds.location = MVDS_location;
+ avdp->mvds.length = MVDS_length;
+
+ /* Backup volume descriptor */
+ UDFExtentAD( &Anchor[ 24 ], &MVDS_length, &MVDS_location );
+ avdp->rvds.location = MVDS_location;
+ avdp->rvds.length = MVDS_length;
+
+ SetUDFCache(ctx, AVDPCache, 0, avdp);
+
+ return 1;
+}
+
+/**
+ * Looks for partition on the disc. Returns 1 if partition found, 0 on error.
+ * partnum: Number of the partition, starting at 0.
+ * part: structure to fill with the partition information
+ */
+static int UDFFindPartition( dvd_reader_t *ctx, int partnum,
+ struct Partition *part )
+{
+ uint8_t LogBlock_base[ DVD_VIDEO_LB_LEN + 2048 ];
+ uint8_t *LogBlock = (uint8_t *)(((uintptr_t)LogBlock_base & ~((uintptr_t)2047)) + 2048);
+ uint32_t lbnum, MVDS_location, MVDS_length;
+ uint16_t TagID;
+ int i, volvalid, ret;
+ struct avdp_t avdp;
+
+ if(!UDFGetAVDP(ctx, &avdp))
+ return 0;
+
+ /* Main volume descriptor */
+ MVDS_location = avdp.mvds.location;
+ MVDS_length = avdp.mvds.length;
+
+ part->valid = 0;
+ volvalid = 0;
+ i = 1;
+ do {
+ /* Find Volume Descriptor */
+ lbnum = MVDS_location;
+ do {
+
+ ret = DVDReadLBUDF( ctx, lbnum++, 1, LogBlock, 0 );
+ if( ret < 0 ) {
+ return ret;
+ }
+ else if( ret == 0 ) {
+ TagID = 0;
+ }
+ else {
+ UDFDescriptor( LogBlock, &TagID );
+ }
+
+ if( ( TagID == PartitionDescriptor ) && ( !part->valid ) ) {
+ /* Partition Descriptor */
+ UDFPartition( LogBlock, &part->Flags, &part->Number,
+ part->Contents, &part->Start, &part->Length );
+ part->valid = ( partnum == part->Number );
+ } else if( ( TagID == LogicalVolumeDescriptor ) && ( !volvalid ) ) {
+ /* Logical Volume Descriptor */
+ if( UDFLogVolume( LogBlock ) ) {
+ /* TODO: sector size wrong! */
+ } else
+ volvalid = 1;
+ }
+
+ } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
+ / DVD_VIDEO_LB_LEN ) && ( TagID != TerminatingDescriptor )
+ && ( ( !part->valid ) || ( !volvalid ) ) );
+
+ if( ( !part->valid) || ( !volvalid ) ) {
+ /* Backup volume descriptor */
+ MVDS_location = avdp.mvds.location;
+ MVDS_length = avdp.mvds.length;
+ }
+ } while( i-- && ( ( !part->valid ) || ( !volvalid ) ) );
+
+ /* We only care for the partition, not the volume */
+ return part->valid;
+}
+
+uint32_t UDFFindFile( dvd_reader_t *ctx, const char *filename,
+ uint32_t *filesize )
+{
+ uint8_t LogBlock_base[ DVD_VIDEO_LB_LEN + 2048 ];
+ uint8_t *LogBlock = (uint8_t *)(((uintptr_t)LogBlock_base & ~((uintptr_t)2047)) + 2048);
+ uint32_t lbnum;
+ uint16_t TagID;
+ struct Partition partition;
+ struct AD RootICB, File, ICB;
+ char tokenline[ MAX_UDF_FILE_NAME_LEN ];
+ uint8_t filetype;
+ int ret;
+
+ *filesize = 0;
+ tokenline[0] = '\0';
+ strncat(tokenline, filename, MAX_UDF_FILE_NAME_LEN - 1);
+ memset(&ICB, 0, sizeof(ICB));
+
+ if(!(GetUDFCache(ctx, PartitionCache, 0, &partition) &&
+ GetUDFCache(ctx, RootICBCache, 0, &RootICB))) {
+ /* Find partition, 0 is the standard location for DVD Video.*/
+ if( !UDFFindPartition( ctx, 0, &partition ) ) return 0;
+ SetUDFCache(ctx, PartitionCache, 0, &partition);
+
+ /* Find root dir ICB */
+ lbnum = partition.Start;
+ do {
+ ret = DVDReadLBUDF( ctx, lbnum++, 1, LogBlock, 0 );
+ if( ret < 0 ) {
+ return ret;
+ }
+ else if( ret == 0 ) {
+ TagID = 0;
+ }
+ else {
+ UDFDescriptor( LogBlock, &TagID );
+ }
+
+ /* File Set Descriptor */
+ if( TagID == FileSetDescriptor ) /* File Set Descriptor */
+ UDFLongAD( &LogBlock[ 400 ], &RootICB );
+ } while( ( lbnum < partition.Start + partition.Length )
+ && ( TagID != TerminatingDescriptor ) && ( TagID != FileSetDescriptor) );
+
+ /* Sanity checks. */
+ if( TagID != FileSetDescriptor )
+ return 0;
+ if( RootICB.Partition != 0 )
+ return 0;
+ SetUDFCache(ctx, RootICBCache, 0, &RootICB);
+ }
+
+ /* Find root dir */
+ if( !UDFMapICB( ctx, RootICB, &filetype, &partition, &File ) )
+ return 0;
+ if( filetype != 4 )
+ return 0; /* Root dir should be dir */
+ {
+ int cache_file_info = 0;
+ /* Tokenize filepath */
+ char *token = strtok(tokenline, "/");
+
+ while( token != NULL ) {
+ if( !UDFScanDir( ctx, File, token, &partition, &ICB,
+ cache_file_info))
+ return 0;
+ if( !UDFMapICB( ctx, ICB, &filetype, &partition, &File ) )
+ return 0;
+ if(!strcmp(token, "VIDEO_TS"))
+ cache_file_info = 1;
+ token = strtok( NULL, "/" );
+ }
+ }
+
+ /* Sanity check. */
+ if( File.Partition != 0 )
+ return 0;
+ *filesize = File.Length;
+ /* Hack to not return partition.Start for empty files. */
+ if( !File.Location )
+ return 0;
+ else
+ return partition.Start + File.Location;
+}
+
+
+
+/**
+ * Gets a Descriptor .
+ * Returns 1 if descriptor found, 0 on error.
+ * id, tagid of descriptor
+ * bufsize, size of BlockBuf (must be >= DVD_VIDEO_LB_LEN).
+ */
+static int UDFGetDescriptor( dvd_reader_t *ctx, int id,
+ uint8_t *descriptor, int bufsize)
+{
+ uint32_t lbnum, MVDS_location, MVDS_length;
+ struct avdp_t avdp;
+ uint16_t TagID;
+ int i, desc_found = 0, ret;
+ /* Find Anchor */
+ lbnum = 256; /* Try #1, prime anchor */
+ if(bufsize < DVD_VIDEO_LB_LEN)
+ return 0;
+
+ if(!UDFGetAVDP(ctx, &avdp))
+ return 0;
+
+ /* Main volume descriptor */
+ MVDS_location = avdp.mvds.location;
+ MVDS_length = avdp.mvds.length;
+
+ i = 1;
+ do {
+ /* Find Descriptor */
+ lbnum = MVDS_location;
+ do {
+ ret = DVDReadLBUDF( ctx, lbnum++, 1, descriptor, 0 );
+ if( ret < 0 ) {
+ return ret;
+ }
+ else if( ret == 0 ) {
+ TagID = 0;
+ }
+ else {
+ UDFDescriptor( descriptor, &TagID );
+ }
+ if( (TagID == id) && ( !desc_found ) )
+ /* Descriptor */
+ desc_found = 1;
+ } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
+ / DVD_VIDEO_LB_LEN ) && ( TagID != TerminatingDescriptor )
+ && ( !desc_found) );
+
+ if( !desc_found ) {
+ /* Backup volume descriptor */
+ MVDS_location = avdp.rvds.location;
+ MVDS_length = avdp.rvds.length;
+ }
+ } while( i-- && ( !desc_found ) );
+
+ return desc_found;
+}
+
+
+static int UDFGetPVD(dvd_reader_t *ctx, struct pvd_t *pvd)
+{
+ uint8_t pvd_buf_base[DVD_VIDEO_LB_LEN + 2048];
+ uint8_t *pvd_buf = (uint8_t *)(((uintptr_t)pvd_buf_base & ~((uintptr_t)2047)) + 2048);
+ if(GetUDFCache(ctx, PVDCache, 0, pvd))
+ return 1;
+
+ if(!UDFGetDescriptor( ctx, 1, pvd_buf, DVD_VIDEO_LB_LEN))
+ return 0;
+
+ memcpy(pvd->VolumeIdentifier, &pvd_buf[24], 32);
+ memcpy(pvd->VolumeSetIdentifier, &pvd_buf[72], 128);
+ SetUDFCache(ctx, PVDCache, 0, pvd);
+ return 1;
+}
+
+/**
+ * Gets the Volume Identifier string, in 8bit unicode (latin-1)
+ * volid, place to put the string
+ * volid_size, size of the buffer volid points to
+ * returns the size of buffer needed for all data
+ */
+int UDFGetVolumeIdentifier(dvd_reader_t *ctx, char *volid,
+ unsigned int volid_size)
+{
+ struct pvd_t pvd;
+ unsigned int volid_len;
+
+ /* get primary volume descriptor */
+ if(!UDFGetPVD(ctx, &pvd))
+ return 0;
+
+ volid_len = pvd.VolumeIdentifier[31];
+ if(volid_len > 31)
+ /* this field is only 32 bytes something is wrong */
+ volid_len = 31;
+ if(volid_size > volid_len)
+ volid_size = volid_len;
+ Unicodedecode(pvd.VolumeIdentifier, volid_size, volid);
+
+ return volid_len;
+}
+
+/**
+ * Gets the Volume Set Identifier, as a 128-byte dstring (not decoded)
+ * WARNING This is not a null terminated string
+ * volsetid, place to put the data
+ * volsetid_size, size of the buffer volsetid points to
+ * the buffer should be >=128 bytes to store the whole volumesetidentifier
+ * returns the size of the available volsetid information (128)
+ * or 0 on error
+ */
+int UDFGetVolumeSetIdentifier(dvd_reader_t *ctx, uint8_t *volsetid,
+ unsigned int volsetid_size)
+{
+ struct pvd_t pvd;
+
+ /* get primary volume descriptor */
+ if(!UDFGetPVD(ctx, &pvd))
+ return 0;
+
+
+ if(volsetid_size > 128)
+ volsetid_size = 128;
+
+ memcpy(volsetid, pvd.VolumeSetIdentifier, volsetid_size);
+
+ return 128;
+}
diff --git a/libdvdread-embedded/src/dvdread/bitreader.h b/libdvdread-embedded/src/dvdread/bitreader.h
new file mode 100644
index 0000000..801e3a9
--- /dev/null
+++ b/libdvdread-embedded/src/dvdread/bitreader.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>.
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDREAD_BITREADER_H
+#define LIBDVDREAD_BITREADER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ const uint8_t *start;
+ uint32_t byte_position;
+ uint32_t bit_position;
+} getbits_state_t;
+
+int dvdread_getbits_init(getbits_state_t *state, const uint8_t *start);
+uint32_t dvdread_getbits(getbits_state_t *state, uint32_t number_of_bits);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* LIBDVDREAD_BITREADER_H */
diff --git a/libdvdread-embedded/src/dvdread/dvd_reader.h b/libdvdread-embedded/src/dvdread/dvd_reader.h
new file mode 100644
index 0000000..54ef5dd
--- /dev/null
+++ b/libdvdread-embedded/src/dvdread/dvd_reader.h
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>,
+ * Björn Englund <d4bjorn@dtek.chalmers.se>
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDREAD_DVD_READER_H
+#define LIBDVDREAD_DVD_READER_H
+
+#ifdef _MSC_VER
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdarg.h>
+
+/**
+ * The DVD access interface.
+ *
+ * This file contains the functions that form the interface for
+ * reading files located on a DVD.
+ */
+
+/**
+ * The current version.
+ */
+#include "version.h"
+
+/**
+ * The length of one Logical Block of a DVD.
+ */
+#define DVD_VIDEO_LB_LEN 2048
+
+/**
+ * Maximum length of filenames allowed in UDF.
+ */
+#define MAX_UDF_FILE_NAME_LEN 2048
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Opaque type that is used as a handle for one instance of an opened DVD.
+ */
+typedef struct dvd_reader_s dvd_reader_t;
+typedef struct dvd_reader_device_s dvd_reader_device_t;
+
+/**
+ * Opaque type for a file read handle, much like a normal fd or FILE *.
+ */
+typedef struct dvd_file_s dvd_file_t;
+
+struct dvd_reader_stream_cb
+{
+ int ( *pf_seek ) ( void *p_stream, uint64_t i_pos);
+ int ( *pf_read ) ( void *p_stream, void* buffer, int i_read);
+ int ( *pf_readv ) ( void *p_stream, void *p_iovec, int i_blocks);
+};
+typedef struct dvd_reader_stream_cb dvd_reader_stream_cb;
+
+/**
+ * Custom logger callback for DVDOpen[Stream]2
+ * @param private Handle as provided in Open functions
+ * @param level Log level
+ * @param fmt Format string
+ * @param args Arguments list
+ * pf_log(priv, level, fmt, args);
+ */
+typedef enum
+{
+ DVD_LOGGER_LEVEL_INFO,
+ DVD_LOGGER_LEVEL_ERROR,
+ DVD_LOGGER_LEVEL_WARN,
+ DVD_LOGGER_LEVEL_DEBUG,
+} dvd_logger_level_t;
+
+typedef struct
+{
+ void ( *pf_log ) ( void *, dvd_logger_level_t, const char *, va_list );
+} dvd_logger_cb;
+
+/**
+ * Public type that is used to provide statistics on a handle.
+ */
+typedef struct {
+ off_t size; /**< Total size of file in bytes */
+ int nr_parts; /**< Number of file parts */
+ off_t parts_size[9]; /**< Size of each part in bytes */
+} dvd_stat_t;
+
+/**
+ * Opens a block device of a DVD-ROM file, or an image file, or a directory
+ * name for a mounted DVD or HD copy of a DVD.
+ * The second form of Open function (DVDOpenStream) can be used to
+ * provide custom stream_cb functions to access the DVD (see libdvdcss).
+ *
+ * If the given file is a block device, or is the mountpoint for a block
+ * device, then that device is used for CSS authentication using libdvdcss.
+ * If no device is available, then no CSS authentication is performed,
+ * and we hope that the image is decrypted.
+ *
+ * If the path given is a directory, then the files in that directory may be
+ * in any one of these formats:
+ *
+ * path/VIDEO_TS/VTS_01_1.VOB
+ * path/video_ts/vts_01_1.vob
+ * path/VTS_01_1.VOB
+ * path/vts_01_1.vob
+ *
+ * @param path Specifies the device, file or directory to be used.
+ * @param stream is a private handle used by stream_cb
+ * @param stream_cb is a struct containing seek and read functions
+ * @return If successful a read handle is returned. Otherwise 0 is returned.
+ *
+ * dvd = DVDOpen(path);
+ * dvd = DVDOpenStream(stream, &stream_cb);
+ */
+dvd_reader_t *DVDOpen( const char * );
+dvd_reader_t *DVDOpenStream( void *, dvd_reader_stream_cb * );
+
+/**
+ * Same as DVDOpen, but with private handle to be passed back on callbacks
+ *
+ * @param path Specifies the device, file or directory to be used.
+ * @param priv is a private handle
+ * @param logcb is a custom logger callback struct, or NULL if none needed
+ * @param stream_cb is a struct containing seek and read functions
+ * @return If successful a read handle is returned. Otherwise 0 is returned.
+ *
+ * dvd = DVDOpen2(priv, logcb, path);
+ * dvd = DVDOpenStream2(priv, logcb, &stream_cb);
+ */
+dvd_reader_t *DVDOpen2( void *, const dvd_logger_cb *, const char * );
+dvd_reader_t *DVDOpenStream2( void *, const dvd_logger_cb *, dvd_reader_stream_cb * );
+
+/**
+ * Closes and cleans up the DVD reader object.
+ *
+ * You must close all open files before calling this function.
+ *
+ * @param dvd A read handle that should be closed.
+ *
+ * DVDClose(dvd);
+ */
+void DVDClose( dvd_reader_t * );
+
+/**
+ *
+ */
+typedef enum {
+ DVD_READ_INFO_FILE, /**< VIDEO_TS.IFO or VTS_XX_0.IFO (title) */
+ DVD_READ_INFO_BACKUP_FILE, /**< VIDEO_TS.BUP or VTS_XX_0.BUP (title) */
+ DVD_READ_MENU_VOBS, /**< VIDEO_TS.VOB or VTS_XX_0.VOB (title) */
+ DVD_READ_TITLE_VOBS /**< VTS_XX_[1-9].VOB (title). All files in
+ the title set are opened and read as a
+ single file. */
+} dvd_read_domain_t;
+
+/**
+ * Stats a file on the DVD given the title number and domain.
+ * The information about the file is stored in a dvd_stat_t
+ * which contains information about the size of the file and
+ * the number of parts in case of a multipart file and the respective
+ * sizes of the parts.
+ * A multipart file is for instance VTS_02_1.VOB, VTS_02_2.VOB, VTS_02_3.VOB
+ * The size of VTS_02_1.VOB will be stored in stat->parts_size[0],
+ * VTS_02_2.VOB in stat->parts_size[1], ...
+ * The total size (sum of all parts) is stored in stat->size and
+ * stat->nr_parts will hold the number of parts.
+ * Only DVD_READ_TITLE_VOBS (VTS_??_[1-9].VOB) can be multipart files.
+ *
+ * This function is only of use if you want to get the size of each file
+ * in the filesystem. These sizes are not needed to use any other
+ * functions in libdvdread.
+ *
+ * @param dvd A dvd read handle.
+ * @param titlenum Which Video Title Set should be used, VIDEO_TS is 0.
+ * @param domain Which domain.
+ * @param stat Pointer to where the result is stored.
+ * @return If successful 0, otherwise -1.
+ *
+ * int DVDFileStat(dvd, titlenum, domain, stat);
+ */
+int DVDFileStat(dvd_reader_t *, int, dvd_read_domain_t, dvd_stat_t *);
+
+/**
+ * Opens a file on the DVD given the title number and domain.
+ *
+ * If the title number is 0, the video manager information is opened
+ * (VIDEO_TS.[IFO,BUP,VOB]). Returns a file structure which may be
+ * used for reads, or 0 if the file was not found.
+ *
+ * @param dvd A dvd read handle.
+ * @param titlenum Which Video Title Set should be used, VIDEO_TS is 0.
+ * @param domain Which domain.
+ * @return If successful a a file read handle is returned, otherwise 0.
+ *
+ * dvd_file = DVDOpenFile(dvd, titlenum, domain); */
+dvd_file_t *DVDOpenFile( dvd_reader_t *, int, dvd_read_domain_t );
+
+/**
+ * Closes a file and frees the associated structure.
+ *
+ * @param dvd_file The file read handle to be closed.
+ *
+ * DVDCloseFile(dvd_file);
+ */
+void DVDCloseFile( dvd_file_t * );
+
+/**
+ * Reads block_count number of blocks from the file at the given block offset.
+ * Returns number of blocks read on success, -1 on error. This call is only
+ * for reading VOB data, and should not be used when reading the IFO files.
+ * When reading from an encrypted drive, blocks are decrypted using libdvdcss
+ * where required.
+ *
+ * @param dvd_file A file read handle.
+ * @param offset Block offset from the start of the file to start reading at.
+ * @param block_count Number of block to read.
+ * @param data Pointer to a buffer to write the data into.
+ * @return Returns number of blocks read on success, -1 on error.
+ *
+ * blocks_read = DVDReadBlocks(dvd_file, offset, block_count, data);
+ */
+ssize_t DVDReadBlocks( dvd_file_t *, int, size_t, unsigned char * );
+
+/**
+ * Seek to the given position in the file. Returns the resulting position in
+ * bytes from the beginning of the file. The seek position is only used for
+ * byte reads from the file, the block read call always reads from the given
+ * offset.
+ *
+ * @param dvd_file A file read handle.
+ * @param seek_offset Byte offset from the start of the file to seek to.
+ * @return The resulting position in bytes from the beginning of the file.
+ *
+ * offset_set = DVDFileSeek(dvd_file, seek_offset);
+ */
+int32_t DVDFileSeek( dvd_file_t *, int32_t );
+
+/**
+ * Reads the given number of bytes from the file. This call can only be used
+ * on the information files, and may not be used for reading from a VOB. This
+ * reads from and increments the current seek position for the file.
+ *
+ * @param dvd_file A file read handle.
+ * @param data Pointer to a buffer to write the data into.
+ * @param bytes Number of bytes to read.
+ * @return Returns number of bytes read on success, -1 on error.
+ *
+ * bytes_read = DVDReadBytes(dvd_file, data, bytes);
+ */
+ssize_t DVDReadBytes( dvd_file_t *, void *, size_t );
+
+/**
+ * Returns the file size in blocks.
+ *
+ * @param dvd_file A file read handle.
+ * @return The size of the file in blocks, -1 on error.
+ *
+ * blocks = DVDFileSize(dvd_file);
+ */
+ssize_t DVDFileSize( dvd_file_t * );
+
+/**
+ * Get a unique 128 bit disc ID.
+ * This is the MD5 sum of VIDEO_TS.IFO and the VTS_0?_0.IFO files
+ * in title order (those that exist).
+ * If you need a 'text' representation of the id, print it as a
+ * hexadecimal number, using lowercase letters, discid[0] first.
+ * I.e. the same format as the command-line 'md5sum' program uses.
+ *
+ * @param dvd A read handle to get the disc ID from
+ * @param discid The buffer to put the disc ID into. The buffer must
+ * have room for 128 bits (16 chars).
+ * @return 0 on success, -1 on error.
+ */
+int DVDDiscID( dvd_reader_t *, unsigned char * );
+
+/**
+ * Get the UDF VolumeIdentifier and VolumeSetIdentifier
+ * from the PrimaryVolumeDescriptor.
+ *
+ * @param dvd A read handle to get the disc ID from
+ * @param volid The buffer to put the VolumeIdentifier into.
+ * The VolumeIdentifier is latin-1 encoded (8bit unicode)
+ * null terminated and max 32 bytes (including '\0')
+ * @param volid_size No more than volid_size bytes will be copied to volid.
+ * If the VolumeIdentifier is truncated because of this
+ * it will still be null terminated.
+ * @param volsetid The buffer to put the VolumeSetIdentifier into.
+ * The VolumeIdentifier is 128 bytes as
+ * stored in the UDF PrimaryVolumeDescriptor.
+ * Note that this is not a null terminated string.
+ * @param volsetid_size At most volsetid_size bytes will be copied to volsetid.
+ * @return 0 on success, -1 on error.
+ */
+int DVDUDFVolumeInfo( dvd_reader_t *, char *, unsigned int,
+ unsigned char *, unsigned int );
+
+int DVDFileSeekForce( dvd_file_t *, int offset, int force_size);
+
+/**
+ * Get the ISO9660 VolumeIdentifier and VolumeSetIdentifier
+ *
+ * * Only use this function as fallback if DVDUDFVolumeInfo returns -1 *
+ * * this will happen on a disc mastered only with a iso9660 filesystem *
+ * * All video DVD discs have UDF filesystem *
+ *
+ * @param dvd A read handle to get the disc ID from
+ * @param volid The buffer to put the VolumeIdentifier into.
+ * The VolumeIdentifier is coded with '0-9','A-Z','_'
+ * null terminated and max 33 bytes (including '\0')
+ * @param volid_size No more than volid_size bytes will be copied to volid.
+ * If the VolumeIdentifier is truncated because of this
+ * it will still be null terminated.
+ * @param volsetid The buffer to put the VolumeSetIdentifier into.
+ * The VolumeIdentifier is 128 bytes as
+ * stored in the ISO9660 PrimaryVolumeDescriptor.
+ * Note that this is not a null terminated string.
+ * @param volsetid_size At most volsetid_size bytes will be copied to volsetid.
+ * @return 0 on success, -1 on error.
+ */
+int DVDISOVolumeInfo( dvd_reader_t *, char *, unsigned int,
+ unsigned char *, unsigned int );
+
+/**
+ * Sets the level of caching that is done when reading from a device
+ *
+ * @param dvd A read handle to get the disc ID from
+ * @param level The level of caching wanted.
+ * -1 - returns the current setting.
+ * 0 - UDF Cache turned off.
+ * 1 - (default level) Pointers to IFO files and some data from
+ * PrimaryVolumeDescriptor are cached.
+ *
+ * @return The level of caching.
+ */
+int DVDUDFCacheLevel( dvd_reader_t *, int );
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* LIBDVDREAD_DVD_READER_H */
diff --git a/libdvdread-embedded/src/dvdread/dvd_udf.h b/libdvdread-embedded/src/dvdread/dvd_udf.h
new file mode 100644
index 0000000..91d5de6
--- /dev/null
+++ b/libdvdread-embedded/src/dvdread/dvd_udf.h
@@ -0,0 +1,59 @@
+/*
+ * This code is based on dvdudf by:
+ * Christian Wolff <scarabaeus@convergence.de>.
+ *
+ * Modifications by:
+ * Billy Biggs <vektor@dumbterm.net>.
+ * Björn Englund <d4bjorn@dtek.chalmers.se>.
+ *
+ * dvdudf: parse and read the UDF volume information of a DVD Video
+ * Copyright (C) 1999 Christian Wolff for convergence integrated media
+ * GmbH The author can be reached at scarabaeus@convergence.de, the
+ * project's page is at http://linuxtv.org/dvd/
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDREAD_DVD_UDF_H
+#define LIBDVDREAD_DVD_UDF_H
+
+#include <inttypes.h>
+
+#include "dvdread/dvd_reader.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Looks for a file on the UDF disc/imagefile and returns the block number
+ * where it begins, or 0 if it is not found. The filename should be an
+ * absolute pathname on the UDF filesystem, starting with '/'. For example,
+ * '/VIDEO_TS/VTS_01_1.IFO'. On success, filesize will be set to the size of
+ * the file in bytes.
+ */
+uint32_t UDFFindFile( dvd_reader_t *, const char *filename, uint32_t *size );
+
+int UDFGetVolumeIdentifier(dvd_reader_t *,
+ char *volid, unsigned int volid_size);
+int UDFGetVolumeSetIdentifier(dvd_reader_t *,
+ uint8_t *volsetid, unsigned int volsetid_size);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* LIBDVDREAD_DVD_UDF_H */
diff --git a/libdvdread-embedded/src/dvdread/ifo_print.h b/libdvdread-embedded/src/dvdread/ifo_print.h
new file mode 100644
index 0000000..c3068cb
--- /dev/null
+++ b/libdvdread-embedded/src/dvdread/ifo_print.h
@@ -0,0 +1,28 @@
+/*
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDREAD_IFO_PRINT_H
+#define LIBDVDREAD_IFO_PRINT_H
+
+#include <inttypes.h>
+#include "ifo_types.h"
+
+void ifo_print(dvd_reader_t *dvd, int title);
+void dvdread_print_time(dvd_time_t *dtime);
+
+#endif /* LIBDVDREAD_IFO_PRINT_H */
diff --git a/libdvdread-embedded/src/dvdread/ifo_read.h b/libdvdread-embedded/src/dvdread/ifo_read.h
new file mode 100644
index 0000000..df538a8
--- /dev/null
+++ b/libdvdread-embedded/src/dvdread/ifo_read.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDREAD_IFO_READ_H
+#define LIBDVDREAD_IFO_READ_H
+
+#include "ifo_types.h"
+#include "dvdread/dvd_reader.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * handle = ifoOpen(dvd, title);
+ *
+ * Opens an IFO and reads in all the data for the IFO file corresponding to the
+ * given title. If title 0 is given, the video manager IFO file is read.
+ * Returns a handle to a completely parsed structure.
+ */
+ifo_handle_t *ifoOpen(dvd_reader_t *, int );
+
+/**
+ * handle = ifoOpenVMGI(dvd);
+ *
+ * Opens an IFO and reads in _only_ the vmgi_mat data. This call can be used
+ * together with the calls below to read in each segment of the IFO file on
+ * demand.
+ */
+ifo_handle_t *ifoOpenVMGI(dvd_reader_t *);
+
+/**
+ * handle = ifoOpenVTSI(dvd, title);
+ *
+ * Opens an IFO and reads in _only_ the vtsi_mat data. This call can be used
+ * together with the calls below to read in each segment of the IFO file on
+ * demand.
+ */
+ifo_handle_t *ifoOpenVTSI(dvd_reader_t *, int);
+
+/**
+ * ifoClose(ifofile);
+ * Cleans up the IFO information. This will free all data allocated for the
+ * substructures.
+ */
+void ifoClose(ifo_handle_t *);
+
+/**
+ * The following functions are for reading only part of the VMGI/VTSI files.
+ * Returns 1 if the data was successfully read and 0 on error.
+ */
+
+/**
+ * okay = ifoRead_PLT_MAIT(ifofile);
+ *
+ * Read in the Parental Management Information table, filling the
+ * ifofile->ptl_mait structure and its substructures. This data is only
+ * located in the video manager information file. This fills the
+ * ifofile->ptl_mait structure and all its substructures.
+ */
+int ifoRead_PTL_MAIT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VTS_ATRT(ifofile);
+ *
+ * Read in the attribute table for the main menu vob, filling the
+ * ifofile->vts_atrt structure and its substructures. Only located in the
+ * video manager information file. This fills in the ifofile->vts_atrt
+ * structure and all its substructures.
+ */
+int ifoRead_VTS_ATRT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TT_SRPT(ifofile);
+ *
+ * Reads the title info for the main menu, filling the ifofile->tt_srpt
+ * structure and its substructures. This data is only located in the video
+ * manager information file. This structure is mandatory in the IFO file.
+ */
+int ifoRead_TT_SRPT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VTS_PTT_SRPT(ifofile);
+ *
+ * Reads in the part of title search pointer table, filling the
+ * ifofile->vts_ptt_srpt structure and its substructures. This data is only
+ * located in the video title set information file. This structure is
+ * mandatory, and must be included in the VTSI file.
+ */
+int ifoRead_VTS_PTT_SRPT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_FP_PGC(ifofile);
+ *
+ * Reads in the first play program chain data, filling the
+ * ifofile->first_play_pgc structure. This data is only located in the video
+ * manager information file (VMGI). This structure is optional.
+ */
+int ifoRead_FP_PGC(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_PGCIT(ifofile);
+ *
+ * Reads in the program chain information table for the video title set. Fills
+ * in the ifofile->vts_pgcit structure and its substructures, which includes
+ * the data for each program chain in the set. This data is only located in
+ * the video title set information file. This structure is mandatory, and must
+ * be included in the VTSI file.
+ */
+int ifoRead_PGCIT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_PGCI_UT(ifofile);
+ *
+ * Reads in the menu PGCI unit table for the menu VOB. For the video manager,
+ * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
+ * corresponds to the VTS_XX_0.VOB file. This data is located in both the
+ * video manager and video title set information files. For VMGI files, this
+ * fills the ifofile->vmgi_pgci_ut structure and all its substructures. For
+ * VTSI files, this fills the ifofile->vtsm_pgci_ut structure.
+ */
+int ifoRead_PGCI_UT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VTS_TMAPT(ifofile);
+ *
+ * Reads in the VTS Time Map Table, this data is only located in the video
+ * title set information file. This fills the ifofile->vts_tmapt structure
+ * and all its substructures. When present enables VOBU level time-based
+ * seeking for One_Sequential_PGC_Titles.
+ */
+int ifoRead_VTS_TMAPT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_C_ADT(ifofile);
+ *
+ * Reads in the cell address table for the menu VOB. For the video manager,
+ * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
+ * corresponds to the VTS_XX_0.VOB file. This data is located in both the
+ * video manager and video title set information files. For VMGI files, this
+ * fills the ifofile->vmgm_c_adt structure and all its substructures. For VTSI
+ * files, this fills the ifofile->vtsm_c_adt structure.
+ */
+int ifoRead_C_ADT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TITLE_C_ADT(ifofile);
+ *
+ * Reads in the cell address table for the video title set corresponding to
+ * this IFO file. This data is only located in the video title set information
+ * file. This structure is mandatory, and must be included in the VTSI file.
+ * This call fills the ifofile->vts_c_adt structure and its substructures.
+ */
+int ifoRead_TITLE_C_ADT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VOBU_ADMAP(ifofile);
+ *
+ * Reads in the VOBU address map for the menu VOB. For the video manager, this
+ * corresponds to the VIDEO_TS.VOB file, and for each title set, this
+ * corresponds to the VTS_XX_0.VOB file. This data is located in both the
+ * video manager and video title set information files. For VMGI files, this
+ * fills the ifofile->vmgm_vobu_admap structure and all its substructures. For
+ * VTSI files, this fills the ifofile->vtsm_vobu_admap structure.
+ */
+int ifoRead_VOBU_ADMAP(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TITLE_VOBU_ADMAP(ifofile);
+ *
+ * Reads in the VOBU address map for the associated video title set. This data
+ * is only located in the video title set information file. This structure is
+ * mandatory, and must be included in the VTSI file. Fills the
+ * ifofile->vts_vobu_admap structure and its substructures.
+ */
+int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TXTDT_MGI(ifofile);
+ *
+ * Reads in the text data strings for the DVD. Fills the ifofile->txtdt_mgi
+ * structure and all its substructures. This data is only located in the video
+ * manager information file. This structure is mandatory, and must be included
+ * in the VMGI file.
+ */
+int ifoRead_TXTDT_MGI(ifo_handle_t *);
+
+/**
+ * The following functions are used for freeing parsed sections of the
+ * ifo_handle_t structure and the allocated substructures. The free calls
+ * below are safe: they will not mind if you attempt to free part of an IFO
+ * file which was not read in or which does not exist.
+ */
+void ifoFree_PTL_MAIT(ifo_handle_t *);
+void ifoFree_VTS_ATRT(ifo_handle_t *);
+void ifoFree_TT_SRPT(ifo_handle_t *);
+void ifoFree_VTS_PTT_SRPT(ifo_handle_t *);
+void ifoFree_FP_PGC(ifo_handle_t *);
+void ifoFree_PGCIT(ifo_handle_t *);
+void ifoFree_PGCI_UT(ifo_handle_t *);
+void ifoFree_VTS_TMAPT(ifo_handle_t *);
+void ifoFree_C_ADT(ifo_handle_t *);
+void ifoFree_TITLE_C_ADT(ifo_handle_t *);
+void ifoFree_VOBU_ADMAP(ifo_handle_t *);
+void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *);
+void ifoFree_TXTDT_MGI(ifo_handle_t *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* LIBDVDREAD_IFO_READ_H */
diff --git a/libdvdread-embedded/src/dvdread/ifo_types.h b/libdvdread-embedded/src/dvdread/ifo_types.h
new file mode 100644
index 0000000..9293ce7
--- /dev/null
+++ b/libdvdread-embedded/src/dvdread/ifo_types.h
@@ -0,0 +1,755 @@
+/*
+ * Copyright (C) 2000, 2001 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDREAD_IFO_TYPES_H
+#define LIBDVDREAD_IFO_TYPES_H
+
+#include <inttypes.h>
+#include "dvdread/dvd_reader.h"
+
+
+#undef ATTRIBUTE_PACKED
+
+#if defined(__GNUC__)
+# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+# if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && !defined(__clang__)
+# define ATTRIBUTE_PACKED __attribute__ ((packed,gcc_struct))
+# else
+# define ATTRIBUTE_PACKED __attribute__ ((packed))
+# endif
+# define PRAGMA_PACK 0
+# endif
+#endif
+
+#if !defined(ATTRIBUTE_PACKED)
+#define ATTRIBUTE_PACKED
+#define PRAGMA_PACK 1
+#endif
+
+#if PRAGMA_PACK
+#pragma pack(1)
+#endif
+
+
+/**
+ * Common
+ *
+ * The following structures are used in both the VMGI and VTSI.
+ */
+
+
+/**
+ * DVD Time Information.
+ */
+typedef struct {
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+ uint8_t frame_u; /* The two high bits are the frame rate. */
+} ATTRIBUTE_PACKED dvd_time_t;
+
+/**
+ * Type to store per-command data.
+ */
+typedef struct {
+ uint8_t bytes[8];
+} ATTRIBUTE_PACKED vm_cmd_t;
+#define COMMAND_DATA_SIZE 8U
+
+
+/**
+ * Video Attributes.
+ */
+typedef struct {
+ unsigned char mpeg_version : 2;
+ unsigned char video_format : 2;
+ unsigned char display_aspect_ratio : 2;
+ unsigned char permitted_df : 2;
+
+ unsigned char line21_cc_1 : 1;
+ unsigned char line21_cc_2 : 1;
+ unsigned char unknown1 : 1;
+ unsigned char bit_rate : 1;
+
+ unsigned char picture_size : 2;
+ unsigned char letterboxed : 1;
+ unsigned char film_mode : 1;
+} ATTRIBUTE_PACKED video_attr_t;
+
+/**
+ * Audio Attributes.
+ */
+typedef struct {
+ unsigned char audio_format : 3;
+ unsigned char multichannel_extension : 1;
+ unsigned char lang_type : 2;
+ unsigned char application_mode : 2;
+
+ unsigned char quantization : 2;
+ unsigned char sample_frequency : 2;
+ unsigned char unknown1 : 1;
+ unsigned char channels : 3;
+ uint16_t lang_code;
+ uint8_t lang_extension;
+ uint8_t code_extension;
+ uint8_t unknown3;
+ union {
+ struct ATTRIBUTE_PACKED {
+ unsigned char unknown4 : 1;
+ unsigned char channel_assignment : 3;
+ unsigned char version : 2;
+ unsigned char mc_intro : 1; /* probably 0: true, 1:false */
+ unsigned char mode : 1; /* Karaoke mode 0: solo 1: duet */
+ } karaoke;
+ struct ATTRIBUTE_PACKED {
+ unsigned char unknown5 : 4;
+ unsigned char dolby_encoded : 1; /* suitable for surround decoding */
+ unsigned char unknown6 : 3;
+ } surround;
+ } ATTRIBUTE_PACKED app_info;
+} ATTRIBUTE_PACKED audio_attr_t;
+
+
+/**
+ * MultiChannel Extension
+ */
+typedef struct {
+ unsigned char zero1 : 7;
+ unsigned char ach0_gme : 1;
+
+ unsigned char zero2 : 7;
+ unsigned char ach1_gme : 1;
+
+ unsigned char zero3 : 4;
+ unsigned char ach2_gv1e : 1;
+ unsigned char ach2_gv2e : 1;
+ unsigned char ach2_gm1e : 1;
+ unsigned char ach2_gm2e : 1;
+
+ unsigned char zero4 : 4;
+ unsigned char ach3_gv1e : 1;
+ unsigned char ach3_gv2e : 1;
+ unsigned char ach3_gmAe : 1;
+ unsigned char ach3_se2e : 1;
+
+ unsigned char zero5 : 4;
+ unsigned char ach4_gv1e : 1;
+ unsigned char ach4_gv2e : 1;
+ unsigned char ach4_gmBe : 1;
+ unsigned char ach4_seBe : 1;
+ uint8_t zero6[19];
+} ATTRIBUTE_PACKED multichannel_ext_t;
+
+
+/**
+ * Subpicture Attributes.
+ */
+typedef struct {
+ /*
+ * type: 0 not specified
+ * 1 language
+ * 2 other
+ * coding mode: 0 run length
+ * 1 extended
+ * 2 other
+ * language: indicates language if type == 1
+ * lang extension: if type == 1 contains the lang extension
+ */
+ unsigned char code_mode : 3;
+ unsigned char zero1 : 3;
+ unsigned char type : 2;
+ uint8_t zero2;
+ uint16_t lang_code;
+ uint8_t lang_extension;
+ uint8_t code_extension;
+} ATTRIBUTE_PACKED subp_attr_t;
+
+
+
+/**
+ * PGC Command Table.
+ */
+typedef struct {
+ uint16_t nr_of_pre;
+ uint16_t nr_of_post;
+ uint16_t nr_of_cell;
+ uint16_t last_byte;
+ vm_cmd_t *pre_cmds;
+ vm_cmd_t *post_cmds;
+ vm_cmd_t *cell_cmds;
+} ATTRIBUTE_PACKED pgc_command_tbl_t;
+#define PGC_COMMAND_TBL_SIZE 8U
+
+/**
+ * PGC Program Map
+ */
+typedef uint8_t pgc_program_map_t;
+
+/**
+ * Cell Playback Information.
+ */
+typedef struct {
+ unsigned char block_mode : 2;
+ unsigned char block_type : 2;
+ unsigned char seamless_play : 1;
+ unsigned char interleaved : 1;
+ unsigned char stc_discontinuity: 1;
+ unsigned char seamless_angle : 1;
+ unsigned char zero_1 : 1;
+ unsigned char playback_mode : 1; /**< When set, enter StillMode after each VOBU */
+ unsigned char restricted : 1; /**< ?? drop out of fastforward? */
+ unsigned char cell_type : 5; /** for karaoke, reserved otherwise */
+ uint8_t still_time;
+ uint8_t cell_cmd_nr;
+ dvd_time_t playback_time;
+ uint32_t first_sector;
+ uint32_t first_ilvu_end_sector;
+ uint32_t last_vobu_start_sector;
+ uint32_t last_sector;
+} ATTRIBUTE_PACKED cell_playback_t;
+
+#define BLOCK_TYPE_NONE 0x0
+#define BLOCK_TYPE_ANGLE_BLOCK 0x1
+
+#define BLOCK_MODE_NOT_IN_BLOCK 0x0
+#define BLOCK_MODE_FIRST_CELL 0x1
+#define BLOCK_MODE_IN_BLOCK 0x2
+#define BLOCK_MODE_LAST_CELL 0x3
+
+/**
+ * Cell Position Information.
+ */
+typedef struct {
+ uint16_t vob_id_nr;
+ uint8_t zero_1;
+ uint8_t cell_nr;
+} ATTRIBUTE_PACKED cell_position_t;
+
+/**
+ * User Operations.
+ */
+typedef struct {
+ unsigned char zero : 7; /* 25-31 */
+ unsigned char video_pres_mode_change : 1; /* 24 */
+
+ unsigned char karaoke_audio_pres_mode_change : 1; /* 23 */
+ unsigned char angle_change : 1;
+ unsigned char subpic_stream_change : 1;
+ unsigned char audio_stream_change : 1;
+ unsigned char pause_on : 1;
+ unsigned char still_off : 1;
+ unsigned char button_select_or_activate : 1;
+ unsigned char resume : 1; /* 16 */
+
+ unsigned char chapter_menu_call : 1; /* 15 */
+ unsigned char angle_menu_call : 1;
+ unsigned char audio_menu_call : 1;
+ unsigned char subpic_menu_call : 1;
+ unsigned char root_menu_call : 1;
+ unsigned char title_menu_call : 1;
+ unsigned char backward_scan : 1;
+ unsigned char forward_scan : 1; /* 8 */
+
+ unsigned char next_pg_search : 1; /* 7 */
+ unsigned char prev_or_top_pg_search : 1;
+ unsigned char time_or_chapter_search : 1;
+ unsigned char go_up : 1;
+ unsigned char stop : 1;
+ unsigned char title_play : 1;
+ unsigned char chapter_search_or_play : 1;
+ unsigned char title_or_time_play : 1; /* 0 */
+} ATTRIBUTE_PACKED user_ops_t;
+
+/**
+ * Program Chain Information.
+ */
+typedef struct {
+ uint16_t zero_1;
+ uint8_t nr_of_programs;
+ uint8_t nr_of_cells;
+ dvd_time_t playback_time;
+ user_ops_t prohibited_ops;
+ uint16_t audio_control[8]; /* New type? */
+ uint32_t subp_control[32]; /* New type? */
+ uint16_t next_pgc_nr;
+ uint16_t prev_pgc_nr;
+ uint16_t goup_pgc_nr;
+ uint8_t pg_playback_mode;
+ uint8_t still_time;
+ uint32_t palette[16]; /* New type struct {zero_1, Y, Cr, Cb} ? */
+ uint16_t command_tbl_offset;
+ uint16_t program_map_offset;
+ uint16_t cell_playback_offset;
+ uint16_t cell_position_offset;
+ pgc_command_tbl_t *command_tbl;
+ pgc_program_map_t *program_map;
+ cell_playback_t *cell_playback;
+ cell_position_t *cell_position;
+ int ref_count;
+} ATTRIBUTE_PACKED pgc_t;
+#define PGC_SIZE 236U
+
+/**
+ * Program Chain Information Search Pointer.
+ */
+typedef struct {
+ uint8_t entry_id;
+ unsigned char block_mode : 2;
+ unsigned char block_type : 2;
+ unsigned char zero_1 : 4;
+ uint16_t ptl_id_mask;
+ uint32_t pgc_start_byte;
+ pgc_t *pgc;
+} ATTRIBUTE_PACKED pgci_srp_t;
+#define PGCI_SRP_SIZE 8U
+
+/**
+ * Program Chain Information Table.
+ */
+typedef struct {
+ uint16_t nr_of_pgci_srp;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ pgci_srp_t *pgci_srp;
+ int ref_count;
+} ATTRIBUTE_PACKED pgcit_t;
+#define PGCIT_SIZE 8U
+
+/**
+ * Menu PGCI Language Unit.
+ */
+typedef struct {
+ uint16_t lang_code;
+ uint8_t lang_extension;
+ uint8_t exists;
+ uint32_t lang_start_byte;
+ pgcit_t *pgcit;
+} ATTRIBUTE_PACKED pgci_lu_t;
+#define PGCI_LU_SIZE 8U
+
+/**
+ * Menu PGCI Unit Table.
+ */
+typedef struct {
+ uint16_t nr_of_lus;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ pgci_lu_t *lu;
+} ATTRIBUTE_PACKED pgci_ut_t;
+#define PGCI_UT_SIZE 8U
+
+/**
+ * Cell Address Information.
+ */
+typedef struct {
+ uint16_t vob_id;
+ uint8_t cell_id;
+ uint8_t zero_1;
+ uint32_t start_sector;
+ uint32_t last_sector;
+} ATTRIBUTE_PACKED cell_adr_t;
+
+/**
+ * Cell Address Table.
+ */
+typedef struct {
+ uint16_t nr_of_vobs; /* VOBs */
+ uint16_t zero_1;
+ uint32_t last_byte;
+ cell_adr_t *cell_adr_table; /* No explicit size given. */
+} ATTRIBUTE_PACKED c_adt_t;
+#define C_ADT_SIZE 8U
+
+/**
+ * VOBU Address Map.
+ */
+typedef struct {
+ uint32_t last_byte;
+ uint32_t *vobu_start_sectors;
+} ATTRIBUTE_PACKED vobu_admap_t;
+#define VOBU_ADMAP_SIZE 4U
+
+
+
+
+/**
+ * VMGI
+ *
+ * The following structures relate to the Video Manager.
+ */
+
+/**
+ * Video Manager Information Management Table.
+ */
+typedef struct {
+ char vmg_identifier[12];
+ uint32_t vmg_last_sector;
+ uint8_t zero_1[12];
+ uint32_t vmgi_last_sector;
+ uint8_t zero_2;
+ uint8_t specification_version;
+ uint32_t vmg_category;
+ uint16_t vmg_nr_of_volumes;
+ uint16_t vmg_this_volume_nr;
+ uint8_t disc_side;
+ uint8_t zero_3[19];
+ uint16_t vmg_nr_of_title_sets; /* Number of VTSs. */
+ char provider_identifier[32];
+ uint64_t vmg_pos_code;
+ uint8_t zero_4[24];
+ uint32_t vmgi_last_byte;
+ uint32_t first_play_pgc;
+ uint8_t zero_5[56];
+ uint32_t vmgm_vobs; /* sector */
+ uint32_t tt_srpt; /* sector */
+ uint32_t vmgm_pgci_ut; /* sector */
+ uint32_t ptl_mait; /* sector */
+ uint32_t vts_atrt; /* sector */
+ uint32_t txtdt_mgi; /* sector */
+ uint32_t vmgm_c_adt; /* sector */
+ uint32_t vmgm_vobu_admap; /* sector */
+ uint8_t zero_6[32];
+
+ video_attr_t vmgm_video_attr;
+ uint8_t zero_7;
+ uint8_t nr_of_vmgm_audio_streams; /* should be 0 or 1 */
+ audio_attr_t vmgm_audio_attr;
+ audio_attr_t zero_8[7];
+ uint8_t zero_9[17];
+ uint8_t nr_of_vmgm_subp_streams; /* should be 0 or 1 */
+ subp_attr_t vmgm_subp_attr;
+ subp_attr_t zero_10[27]; /* XXX: how much 'padding' here? */
+} ATTRIBUTE_PACKED vmgi_mat_t;
+
+typedef struct {
+ unsigned char zero_1 : 1;
+ unsigned char multi_or_random_pgc_title : 1; /* 0: one sequential pgc title */
+ unsigned char jlc_exists_in_cell_cmd : 1;
+ unsigned char jlc_exists_in_prepost_cmd : 1;
+ unsigned char jlc_exists_in_button_cmd : 1;
+ unsigned char jlc_exists_in_tt_dom : 1;
+ unsigned char chapter_search_or_play : 1; /* UOP 1 */
+ unsigned char title_or_time_play : 1; /* UOP 0 */
+} ATTRIBUTE_PACKED playback_type_t;
+
+/**
+ * Title Information.
+ */
+typedef struct {
+ playback_type_t pb_ty;
+ uint8_t nr_of_angles;
+ uint16_t nr_of_ptts;
+ uint16_t parental_id;
+ uint8_t title_set_nr;
+ uint8_t vts_ttn;
+ uint32_t title_set_sector;
+} ATTRIBUTE_PACKED title_info_t;
+
+/**
+ * PartOfTitle Search Pointer Table.
+ */
+typedef struct {
+ uint16_t nr_of_srpts;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ title_info_t *title;
+} ATTRIBUTE_PACKED tt_srpt_t;
+#define TT_SRPT_SIZE 8U
+
+
+/**
+ * Parental Management Information Unit Table.
+ * Level 1 (US: G), ..., 7 (US: NC-17), 8
+ */
+#define PTL_MAIT_NUM_LEVEL 8
+typedef uint16_t pf_level_t[PTL_MAIT_NUM_LEVEL];
+
+/**
+ * Parental Management Information Unit Table.
+ */
+typedef struct {
+ uint16_t country_code;
+ uint16_t zero_1;
+ uint16_t pf_ptl_mai_start_byte;
+ uint16_t zero_2;
+ pf_level_t *pf_ptl_mai; /* table of (nr_of_vtss + 1), video_ts is first */
+} ATTRIBUTE_PACKED ptl_mait_country_t;
+#define PTL_MAIT_COUNTRY_SIZE 8U
+
+/**
+ * Parental Management Information Table.
+ */
+typedef struct {
+ uint16_t nr_of_countries;
+ uint16_t nr_of_vtss;
+ uint32_t last_byte;
+ ptl_mait_country_t *countries;
+} ATTRIBUTE_PACKED ptl_mait_t;
+#define PTL_MAIT_SIZE 8U
+
+/**
+ * Video Title Set Attributes.
+ */
+typedef struct {
+ uint32_t last_byte;
+ uint32_t vts_cat;
+
+ video_attr_t vtsm_vobs_attr;
+ uint8_t zero_1;
+ uint8_t nr_of_vtsm_audio_streams; /* should be 0 or 1 */
+ audio_attr_t vtsm_audio_attr;
+ audio_attr_t zero_2[7];
+ uint8_t zero_3[16];
+ uint8_t zero_4;
+ uint8_t nr_of_vtsm_subp_streams; /* should be 0 or 1 */
+ subp_attr_t vtsm_subp_attr;
+ subp_attr_t zero_5[27];
+
+ uint8_t zero_6[2];
+
+ video_attr_t vtstt_vobs_video_attr;
+ uint8_t zero_7;
+ uint8_t nr_of_vtstt_audio_streams;
+ audio_attr_t vtstt_audio_attr[8];
+ uint8_t zero_8[16];
+ uint8_t zero_9;
+ uint8_t nr_of_vtstt_subp_streams;
+ subp_attr_t vtstt_subp_attr[32];
+} ATTRIBUTE_PACKED vts_attributes_t;
+#define VTS_ATTRIBUTES_SIZE 542U
+#define VTS_ATTRIBUTES_MIN_SIZE 356U
+
+/**
+ * Video Title Set Attribute Table.
+ */
+typedef struct {
+ uint16_t nr_of_vtss;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ vts_attributes_t *vts;
+ uint32_t *vts_atrt_offsets; /* offsets table for each vts_attributes */
+} ATTRIBUTE_PACKED vts_atrt_t;
+#define VTS_ATRT_SIZE 8U
+
+/**
+ * Text Data. (Incomplete)
+ */
+typedef struct {
+ uint32_t last_byte; /* offsets are relative here */
+ uint16_t offsets[100]; /* == nr_of_srpts + 1 (first is disc title) */
+#if 0
+ uint16_t unknown; /* 0x48 ?? 0x48 words (16bit) info following */
+ uint16_t zero_1;
+
+ uint8_t type_of_info; /* ?? 01 == disc, 02 == Title, 04 == Title part */
+ uint8_t unknown1;
+ uint8_t unknown2;
+ uint8_t unknown3;
+ uint8_t unknown4; /* ?? always 0x30 language?, text format? */
+ uint8_t unknown5;
+ uint16_t offset; /* from first */
+
+ char text[12]; /* ended by 0x09 */
+#endif
+} ATTRIBUTE_PACKED txtdt_t;
+
+/**
+ * Text Data Language Unit. (Incomplete)
+ */
+typedef struct {
+ uint16_t lang_code;
+ uint8_t zero_1;
+ uint8_t char_set; /* 0x00 reserved Unicode, 0x01 ISO 646, 0x10 JIS Roman & JIS Kanji, 0x11 ISO 8859-1, 0x12 Shift JIS Kanji */
+ uint32_t txtdt_start_byte; /* prt, rel start of vmg_txtdt_mgi */
+ txtdt_t *txtdt;
+} ATTRIBUTE_PACKED txtdt_lu_t;
+#define TXTDT_LU_SIZE 8U
+
+/**
+ * Text Data Manager Information. (Incomplete)
+ */
+typedef struct {
+ char disc_name[12];
+ uint16_t unknown1;
+ uint16_t nr_of_language_units;
+ uint32_t last_byte;
+ txtdt_lu_t *lu;
+} ATTRIBUTE_PACKED txtdt_mgi_t;
+#define TXTDT_MGI_SIZE 20U
+
+
+/**
+ * VTS
+ *
+ * Structures relating to the Video Title Set (VTS).
+ */
+
+/**
+ * Video Title Set Information Management Table.
+ */
+typedef struct {
+ char vts_identifier[12];
+ uint32_t vts_last_sector;
+ uint8_t zero_1[12];
+ uint32_t vtsi_last_sector;
+ uint8_t zero_2;
+ uint8_t specification_version;
+ uint32_t vts_category;
+ uint16_t zero_3;
+ uint16_t zero_4;
+ uint8_t zero_5;
+ uint8_t zero_6[19];
+ uint16_t zero_7;
+ uint8_t zero_8[32];
+ uint64_t zero_9;
+ uint8_t zero_10[24];
+ uint32_t vtsi_last_byte;
+ uint32_t zero_11;
+ uint8_t zero_12[56];
+ uint32_t vtsm_vobs; /* sector */
+ uint32_t vtstt_vobs; /* sector */
+ uint32_t vts_ptt_srpt; /* sector */
+ uint32_t vts_pgcit; /* sector */
+ uint32_t vtsm_pgci_ut; /* sector */
+ uint32_t vts_tmapt; /* sector */
+ uint32_t vtsm_c_adt; /* sector */
+ uint32_t vtsm_vobu_admap; /* sector */
+ uint32_t vts_c_adt; /* sector */
+ uint32_t vts_vobu_admap; /* sector */
+ uint8_t zero_13[24];
+
+ video_attr_t vtsm_video_attr;
+ uint8_t zero_14;
+ uint8_t nr_of_vtsm_audio_streams; /* should be 0 or 1 */
+ audio_attr_t vtsm_audio_attr;
+ audio_attr_t zero_15[7];
+ uint8_t zero_16[17];
+ uint8_t nr_of_vtsm_subp_streams; /* should be 0 or 1 */
+ subp_attr_t vtsm_subp_attr;
+ subp_attr_t zero_17[27];
+ uint8_t zero_18[2];
+
+ video_attr_t vts_video_attr;
+ uint8_t zero_19;
+ uint8_t nr_of_vts_audio_streams;
+ audio_attr_t vts_audio_attr[8];
+ uint8_t zero_20[17];
+ uint8_t nr_of_vts_subp_streams;
+ subp_attr_t vts_subp_attr[32];
+ uint16_t zero_21;
+ multichannel_ext_t vts_mu_audio_attr[8];
+ /* XXX: how much 'padding' here, if any? */
+} ATTRIBUTE_PACKED vtsi_mat_t;
+
+/**
+ * PartOfTitle Unit Information.
+ */
+typedef struct {
+ uint16_t pgcn;
+ uint16_t pgn;
+} ATTRIBUTE_PACKED ptt_info_t;
+
+/**
+ * PartOfTitle Information.
+ */
+typedef struct {
+ uint16_t nr_of_ptts;
+ ptt_info_t *ptt;
+} ATTRIBUTE_PACKED ttu_t;
+
+/**
+ * PartOfTitle Search Pointer Table.
+ */
+typedef struct {
+ uint16_t nr_of_srpts;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ ttu_t *title;
+ uint32_t *ttu_offset; /* offset table for each ttu */
+} ATTRIBUTE_PACKED vts_ptt_srpt_t;
+#define VTS_PTT_SRPT_SIZE 8U
+
+
+/**
+ * Time Map Entry.
+ */
+/* Should this be bit field at all or just the uint32_t? */
+typedef uint32_t map_ent_t;
+
+/**
+ * Time Map.
+ */
+typedef struct {
+ uint8_t tmu; /* Time unit, in seconds */
+ uint8_t zero_1;
+ uint16_t nr_of_entries;
+ map_ent_t *map_ent;
+} ATTRIBUTE_PACKED vts_tmap_t;
+#define VTS_TMAP_SIZE 4U
+
+/**
+ * Time Map Table.
+ */
+typedef struct {
+ uint16_t nr_of_tmaps;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ vts_tmap_t *tmap;
+ uint32_t *tmap_offset; /* offset table for each tmap */
+} ATTRIBUTE_PACKED vts_tmapt_t;
+#define VTS_TMAPT_SIZE 8U
+
+
+#if PRAGMA_PACK
+#pragma pack()
+#endif
+
+
+/**
+ * The following structure defines an IFO file. The structure is divided into
+ * two parts, the VMGI, or Video Manager Information, which is read from the
+ * VIDEO_TS.[IFO,BUP] file, and the VTSI, or Video Title Set Information, which
+ * is read in from the VTS_XX_0.[IFO,BUP] files.
+ */
+typedef struct {
+ /* VMGI */
+ vmgi_mat_t *vmgi_mat;
+ tt_srpt_t *tt_srpt;
+ pgc_t *first_play_pgc;
+ ptl_mait_t *ptl_mait;
+ vts_atrt_t *vts_atrt;
+ txtdt_mgi_t *txtdt_mgi;
+
+ /* Common */
+ pgci_ut_t *pgci_ut;
+ c_adt_t *menu_c_adt;
+ vobu_admap_t *menu_vobu_admap;
+
+ /* VTSI */
+ vtsi_mat_t *vtsi_mat;
+ vts_ptt_srpt_t *vts_ptt_srpt;
+ pgcit_t *vts_pgcit;
+ vts_tmapt_t *vts_tmapt;
+ c_adt_t *vts_c_adt;
+ vobu_admap_t *vts_vobu_admap;
+} ifo_handle_t;
+
+#endif /* LIBDVDREAD_IFO_TYPES_H */
diff --git a/libdvdread-embedded/src/dvdread/nav_print.h b/libdvdread-embedded/src/dvdread/nav_print.h
new file mode 100644
index 0000000..4960615
--- /dev/null
+++ b/libdvdread-embedded/src/dvdread/nav_print.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDREAD_NAV_PRINT_H
+#define LIBDVDREAD_NAV_PRINT_H
+
+#include "nav_types.h"
+
+/**
+ * Pretty printing of the NAV packets, PCI and DSI structs.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Prints information contained in the PCI to stdout.
+ *
+ * @param pci Pointer to the PCI data structure to be printed.
+ */
+void navPrint_PCI(pci_t *);
+
+/**
+ * Prints information contained in the DSI to stdout.
+ *
+ * @param dsi Pointer to the DSI data structure to be printed.
+ */
+void navPrint_DSI(dsi_t *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* LIBDVDREAD_NAV_PRINT_H */
diff --git a/libdvdread-embedded/src/dvdread/nav_read.h b/libdvdread-embedded/src/dvdread/nav_read.h
new file mode 100644
index 0000000..256fadb
--- /dev/null
+++ b/libdvdread-embedded/src/dvdread/nav_read.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>.
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDREAD_NAV_READ_H
+#define LIBDVDREAD_NAV_READ_H
+
+#include "nav_types.h"
+
+/**
+ * Parsing of NAV data, PCI and DSI parts.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Reads the PCI packet data pointed to into th pci struct.
+ *
+ * @param pci Pointer to the PCI data structure to be filled in.
+ * @param buffer Pointer to the buffer of the on disc PCI data.
+ */
+void navRead_PCI(pci_t *, unsigned char *);
+
+/**
+ * Reads the DSI packet data pointed to into dsi struct.
+ *
+ * @param dsi Pointer to the DSI data structure to be filled in.
+ * @param buffer Pointer to the buffer of the on disc DSI data.
+ */
+void navRead_DSI(dsi_t *, unsigned char *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* LIBDVDREAD_NAV_READ_H */
diff --git a/libdvdread-embedded/src/dvdread/nav_types.h b/libdvdread-embedded/src/dvdread/nav_types.h
new file mode 100644
index 0000000..3debbee
--- /dev/null
+++ b/libdvdread-embedded/src/dvdread/nav_types.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * The data structures in this file should represent the layout of the
+ * pci and dsi packets as they are stored in the stream. Information
+ * found by reading the source to VOBDUMP is the base for the structure
+ * and names of these data types.
+ *
+ * VOBDUMP: a program for examining DVD .VOB files.
+ * Copyright 1998, 1999 Eric Smith <eric@brouhaha.com>
+ *
+ * VOBDUMP is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 or version 3 as
+ * published by the Free Software Foundation. Note that I am not
+ * granting permission to redistribute or modify VOBDUMP under the terms
+ * of any later version of the General Public License.
+ *
+ * This program is distributed in the hope that it will be useful (or at
+ * least amusing), but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDREAD_NAV_TYPES_H
+#define LIBDVDREAD_NAV_TYPES_H
+
+#include <inttypes.h>
+#include "ifo_types.h" /* only dvd_time_t, vm_cmd_t and user_ops_t */
+
+/* The length including the substream id byte. */
+#define PCI_BYTES 0x3d4
+#define DSI_BYTES 0x3fa
+
+#define PS2_PCI_SUBSTREAM_ID 0x00
+#define PS2_DSI_SUBSTREAM_ID 0x01
+
+/* Remove this */
+#define DSI_START_BYTE 1031
+
+/**
+ * PCI General Information
+ */
+typedef struct {
+ uint32_t nv_pck_lbn; /**< sector address of this nav pack */
+ uint16_t vobu_cat; /**< 'category' of vobu */
+ uint16_t zero1; /**< reserved */
+ user_ops_t vobu_uop_ctl; /**< UOP of vobu */
+ uint32_t vobu_s_ptm; /**< start presentation time of vobu */
+ uint32_t vobu_e_ptm; /**< end presentation time of vobu */
+ uint32_t vobu_se_e_ptm; /**< end ptm of sequence end in vobu */
+ dvd_time_t e_eltm; /**< Cell elapsed time */
+ char vobu_isrc[32];
+} ATTRIBUTE_PACKED pci_gi_t;
+
+/**
+ * Non Seamless Angle Information
+ */
+typedef struct {
+ uint32_t nsml_agl_dsta[9]; /**< address of destination vobu in AGL_C#n */
+} ATTRIBUTE_PACKED nsml_agli_t;
+
+/**
+ * Highlight General Information
+ *
+ * For btngrX_dsp_ty the bits have the following meaning:
+ * 000b: normal 4/3 only buttons
+ * XX1b: wide (16/9) buttons
+ * X1Xb: letterbox buttons
+ * 1XXb: pan&scan buttons
+ */
+typedef struct {
+ uint16_t hli_ss; /**< status, only low 2 bits 0: no buttons, 1: different 2: equal 3: eual except for button cmds */
+ uint32_t hli_s_ptm; /**< start ptm of hli */
+ uint32_t hli_e_ptm; /**< end ptm of hli */
+ uint32_t btn_se_e_ptm; /**< end ptm of button select */
+ unsigned char zero1 : 2; /**< reserved */
+ unsigned char btngr_ns : 2; /**< number of button groups 1, 2 or 3 with 36/18/12 buttons */
+ unsigned char zero2 : 1; /**< reserved */
+ unsigned char btngr1_dsp_ty : 3; /**< display type of subpic stream for button group 1 */
+ unsigned char zero3 : 1; /**< reserved */
+ unsigned char btngr2_dsp_ty : 3; /**< display type of subpic stream for button group 2 */
+ unsigned char zero4 : 1; /**< reserved */
+ unsigned char btngr3_dsp_ty : 3; /**< display type of subpic stream for button group 3 */
+ uint8_t btn_ofn; /**< button offset number range 0-255 */
+ uint8_t btn_ns; /**< number of valid buttons <= 36/18/12 (low 6 bits) */
+ uint8_t nsl_btn_ns; /**< number of buttons selectable by U_BTNNi (low 6 bits) nsl_btn_ns <= btn_ns */
+ uint8_t zero5; /**< reserved */
+ uint8_t fosl_btnn; /**< forcedly selected button (low 6 bits) */
+ uint8_t foac_btnn; /**< forcedly activated button (low 6 bits) */
+} ATTRIBUTE_PACKED hl_gi_t;
+
+
+/**
+ * Button Color Information Table
+ * Each entry is a 32bit word that contains the color indices and alpha
+ * values to use. They are all represented by 4 bit number and stored
+ * like this [Ci3, Ci2, Ci1, Ci0, A3, A2, A1, A0]. The actual palette
+ * that the indexes reference is in the PGC.
+ * @TODO split the uint32_t into a struct
+ */
+typedef struct {
+ uint32_t btn_coli[3][2]; /**< [button color number-1][select:0/action:1] */
+} ATTRIBUTE_PACKED btn_colit_t;
+
+/**
+ * Button Information
+ *
+ * NOTE: I've had to change the structure from the disk layout to get
+ * the packing to work with Sun's Forte C compiler.
+ * The 4 and 7 bytes are 'rotated' was: ABC DEF GHIJ is: ABCG DEFH IJ
+ */
+typedef struct {
+ unsigned int btn_coln : 2; /**< button color number */
+ unsigned int x_start : 10; /**< x start offset within the overlay */
+ unsigned int zero1 : 2; /**< reserved */
+ unsigned int x_end : 10; /**< x end offset within the overlay */
+
+ unsigned int auto_action_mode : 2; /**< 0: no, 1: activated if selected */
+ unsigned int y_start : 10; /**< y start offset within the overlay */
+ unsigned int zero2 : 2; /**< reserved */
+ unsigned int y_end : 10; /**< y end offset within the overlay */
+
+ unsigned int zero3 : 2; /**< reserved */
+ unsigned int up : 6; /**< button index when pressing up */
+ unsigned int zero4 : 2; /**< reserved */
+ unsigned int down : 6; /**< button index when pressing down */
+ unsigned int zero5 : 2; /**< reserved */
+ unsigned int left : 6; /**< button index when pressing left */
+ unsigned int zero6 : 2; /**< reserved */
+ unsigned int right : 6; /**< button index when pressing right */
+ vm_cmd_t cmd;
+} ATTRIBUTE_PACKED btni_t;
+
+/**
+ * Highlight Information
+ */
+typedef struct {
+ hl_gi_t hl_gi;
+ btn_colit_t btn_colit;
+ btni_t btnit[36];
+} ATTRIBUTE_PACKED hli_t;
+
+/**
+ * PCI packet
+ */
+typedef struct {
+ pci_gi_t pci_gi;
+ nsml_agli_t nsml_agli;
+ hli_t hli;
+ uint8_t zero1[189];
+} ATTRIBUTE_PACKED pci_t;
+
+
+
+
+/**
+ * DSI General Information
+ */
+typedef struct {
+ uint32_t nv_pck_scr;
+ uint32_t nv_pck_lbn; /**< sector address of this nav pack */
+ uint32_t vobu_ea; /**< end address of this VOBU */
+ uint32_t vobu_1stref_ea; /**< end address of the 1st reference image */
+ uint32_t vobu_2ndref_ea; /**< end address of the 2nd reference image */
+ uint32_t vobu_3rdref_ea; /**< end address of the 3rd reference image */
+ uint16_t vobu_vob_idn; /**< VOB Id number that this VOBU is part of */
+ uint8_t zero1; /**< reserved */
+ uint8_t vobu_c_idn; /**< Cell Id number that this VOBU is part of */
+ dvd_time_t c_eltm; /**< Cell elapsed time */
+} ATTRIBUTE_PACKED dsi_gi_t;
+
+/**
+ * Seamless Playback Information
+ */
+typedef struct {
+ uint16_t category; /**< 'category' of seamless VOBU */
+ uint32_t ilvu_ea; /**< end address of interleaved Unit */
+ uint32_t ilvu_sa; /**< start address of next interleaved unit */
+ uint16_t size; /**< size of next interleaved unit */
+ uint32_t vob_v_s_s_ptm; /**< video start ptm in vob */
+ uint32_t vob_v_e_e_ptm; /**< video end ptm in vob */
+ struct {
+ uint32_t stp_ptm1;
+ uint32_t stp_ptm2;
+ uint32_t gap_len1;
+ uint32_t gap_len2;
+ } vob_a[8];
+} ATTRIBUTE_PACKED sml_pbi_t;
+
+/**
+ * Seamless Angle Information for one angle
+ */
+typedef struct {
+ uint32_t address; /**< offset to next ILVU, high bit is before/after */
+ uint16_t size; /**< byte size of the ILVU pointed to by address */
+} ATTRIBUTE_PACKED sml_agl_data_t;
+
+/**
+ * Seamless Angle Information
+ */
+typedef struct {
+ sml_agl_data_t data[9];
+} ATTRIBUTE_PACKED sml_agli_t;
+
+/**
+ * VOBU Search Information
+ */
+typedef struct {
+ uint32_t next_video; /**< Next vobu that contains video */
+ uint32_t fwda[19]; /**< Forwards, time */
+ uint32_t next_vobu;
+ uint32_t prev_vobu;
+ uint32_t bwda[19]; /**< Backwards, time */
+ uint32_t prev_video;
+} ATTRIBUTE_PACKED vobu_sri_t;
+
+#define SRI_END_OF_CELL 0x3fffffff
+
+/**
+ * Synchronous Information
+ */
+typedef struct {
+ uint16_t a_synca[8]; /**< offset to first audio packet for this VOBU */
+ uint32_t sp_synca[32]; /**< offset to first subpicture packet */
+} ATTRIBUTE_PACKED synci_t;
+
+/**
+ * DSI packet
+ */
+typedef struct {
+ dsi_gi_t dsi_gi;
+ sml_pbi_t sml_pbi;
+ sml_agli_t sml_agli;
+ vobu_sri_t vobu_sri;
+ synci_t synci;
+ uint8_t zero1[471];
+} ATTRIBUTE_PACKED dsi_t;
+
+
+#if PRAGMA_PACK
+#pragma pack()
+#endif
+
+#endif /* LIBDVDREAD_NAV_TYPES_H */
diff --git a/libdvdread-embedded/src/dvdread/version.h.in b/libdvdread-embedded/src/dvdread/version.h.in
new file mode 100644
index 0000000..0ab1676
--- /dev/null
+++ b/libdvdread-embedded/src/dvdread/version.h.in
@@ -0,0 +1,37 @@
+/*
+ * This file is part of libdvdread
+ * Copyright (C) 2015 VideoLAN
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DVDREAD_VERSION_H_
+#define DVDREAD_VERSION_H_
+
+#define DVDREAD_VERSION_CODE(major, minor, micro) \
+ (((major) * 10000) + \
+ ((minor) * 100) + \
+ ((micro) * 1))
+
+#define DVDREAD_VERSION_MAJOR @DVDREAD_VERSION_MAJOR@
+#define DVDREAD_VERSION_MINOR @DVDREAD_VERSION_MINOR@
+#define DVDREAD_VERSION_MICRO @DVDREAD_VERSION_MICRO@
+
+#define DVDREAD_VERSION_STRING "@DVDREAD_VERSION_MAJOR@.@DVDREAD_VERSION_MINOR@.@DVDREAD_VERSION_MICRO@"
+
+#define DVDREAD_VERSION \
+ DVDREAD_VERSION_CODE(DVDREAD_VERSION_MAJOR, DVDREAD_VERSION_MINOR, DVDREAD_VERSION_MICRO)
+
+#endif /* DVDREAD_VERSION_H_ */
diff --git a/libdvdread-embedded/src/dvdread_internal.h b/libdvdread-embedded/src/dvdread_internal.h
new file mode 100644
index 0000000..bf4e2e1
--- /dev/null
+++ b/libdvdread-embedded/src/dvdread_internal.h
@@ -0,0 +1,86 @@
+/*
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDREAD_DVDREAD_INTERNAL_H
+#define LIBDVDREAD_DVDREAD_INTERNAL_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#ifdef _WIN32
+# include <unistd.h>
+#endif /* _WIN32 */
+
+#include "dvdread/dvd_reader.h"
+#include "dvdread/ifo_types.h"
+#include "logger.h"
+
+#define container_of(ptr, type, member) \
+ ((type *)(((char *)(ptr)) - offsetof(type, member)))
+
+struct dvd_reader_s
+{
+ dvd_reader_device_t *rd;
+ void *priv; /* User provided context */
+ dvd_logger_cb logcb;
+ /* Set 100 flags for BUP fallback, most signifiant left
+ [0] for upper remaining VTS, [1] for the first Main + 63 VTS */
+ uint64_t ifoBUPflags[2];
+};
+
+struct ifo_handle_private_s
+{
+ ifo_handle_t handle;
+ dvd_reader_t *ctx;
+ dvd_file_t *file;
+};
+
+enum TagIdentifier {
+ /* ECMA 167 3/7.2.1 */
+ PrimaryVolumeDescriptor = 1,
+ AnchorVolumeDescriptorPointer = 2,
+ VolumeDescriptorPointer = 3,
+ ImplementationUseVolumeDescriptor = 4,
+ PartitionDescriptor = 5,
+ LogicalVolumeDescriptor = 6,
+ UnallocatedSpaceDescriptor = 7,
+ TerminatingDescriptor = 8,
+ LogicalVolumeIntegrityDescriptor = 9,
+ /* ECMA 167 4/7.2.1 */
+ FileSetDescriptor = 256,
+ FileIdentifierDescriptor = 257,
+ AllocationExtentDescriptor = 258,
+ IndirectEntry = 259,
+ TerminalEntry = 260,
+ FileEntry = 261,
+ ExtendedAttributeHeaderDescriptor = 262,
+ UnallocatedSpaceEntry = 263,
+ SpaceBitmapDescriptor = 264,
+ PartitionIntegrityEntry = 265,
+ ExtendedFileEntry = 266,
+};
+
+int InternalUDFReadBlocksRaw(const dvd_reader_t *, uint32_t lb_number,
+ size_t block_count, unsigned char *data, int encrypted);
+
+void *GetUDFCacheHandle(dvd_reader_t *);
+void SetUDFCacheHandle(dvd_reader_t *, void *cache);
+void FreeUDFCache(void *cache);
+
+#endif /* LIBDVDREAD_DVDREAD_INTERNAL_H */
diff --git a/libdvdread-embedded/src/ifo_print.c b/libdvdread-embedded/src/ifo_print.c
new file mode 100644
index 0000000..f61a2b3
--- /dev/null
+++ b/libdvdread-embedded/src/ifo_print.c
@@ -0,0 +1,1209 @@
+/*
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "dvdread/ifo_types.h"
+#include "dvdread/ifo_read.h"
+#include "dvdread/ifo_print.h"
+#include "dvdread_internal.h"
+#include "logger.h"
+
+/* Put this in some other file / package? It's used in nav_print too. */
+static void ifo_print_time(dvd_time_t *dtime) {
+ const char *rate;
+ assert((dtime->hour>>4) < 0xa && (dtime->hour&0xf) < 0xa);
+ assert((dtime->minute>>4) < 0x7 && (dtime->minute&0xf) < 0xa);
+ assert((dtime->second>>4) < 0x7 && (dtime->second&0xf) < 0xa);
+ assert((dtime->frame_u&0xf) < 0xa);
+
+ printf("%02x:%02x:%02x.%02x",
+ dtime->hour,
+ dtime->minute,
+ dtime->second,
+ dtime->frame_u & 0x3f);
+ switch((dtime->frame_u & 0xc0) >> 6) {
+ case 1:
+ rate = "25.00";
+ break;
+ case 3:
+ rate = "29.97";
+ break;
+ default:
+ if(dtime->hour == 0 && dtime->minute == 0
+ && dtime->second == 0 && dtime->frame_u == 0)
+ rate = "no";
+ else
+ rate = "(please send a bug report)";
+ break;
+ }
+ printf(" @ %s fps", rate);
+}
+
+void dvdread_print_time(dvd_time_t *dtime) {
+ ifo_print_time(dtime);
+}
+
+/* Put this in some other file / package? It's used in nav_print too.
+ Possibly also by the vm / navigator. */
+static void ifo_print_cmd(int row, vm_cmd_t *command) {
+ int i;
+
+ printf("(%03d) ", row + 1);
+ for(i=0;i<8;i++)
+ printf("%02x ", command->bytes[i]);
+ printf("| ");
+#if 0
+ //disabled call of dvdnav function
+ vm_print_mnemonic(command);
+#endif
+ printf("\n");
+}
+
+static void ifo_print_video_attributes(video_attr_t *attr) {
+
+ /* The following test is shorter but not correct ISO C,
+ memcmp(attr,my_friendly_zeros, sizeof(video_attr_t)) */
+ if(attr->mpeg_version == 0
+ && attr->video_format == 0
+ && attr->display_aspect_ratio == 0
+ && attr->permitted_df == 0
+ && attr->line21_cc_1 == 0
+ && attr->line21_cc_2 == 0
+ && attr->unknown1 == 0
+ && attr->letterboxed == 0
+ && attr->film_mode == 0) {
+ printf("-- Unspecified --");
+ return;
+ }
+
+ switch(attr->mpeg_version) {
+ case 0:
+ printf("mpeg1, ");
+ break;
+ case 1:
+ printf("mpeg2, ");
+ break;
+ default:
+ printf("(please send a bug report), ");
+ }
+
+ switch(attr->video_format) {
+ case 0:
+ printf("ntsc, ");
+ break;
+ case 1:
+ printf("pal, ");
+ break;
+ default:
+ printf("(please send a bug report), ");
+ }
+
+ switch(attr->display_aspect_ratio) {
+ case 0:
+ printf("4:3, ");
+ break;
+ case 3:
+ printf("16:9, ");
+ break;
+ default:
+ printf("(please send a bug report), ");
+ }
+
+ // Wide is always allowed..!!!
+ switch(attr->permitted_df) {
+ case 0:
+ printf("pan&scan+letterboxed, ");
+ break;
+ case 1:
+ printf("only pan&scan, "); //??
+ break;
+ case 2:
+ printf("only letterboxed, ");
+ break;
+ case 3:
+ printf("not specified, ");
+ break;
+ default:
+ printf("(please send a bug report), ");
+ }
+
+ if(attr->line21_cc_1 || attr->line21_cc_2) {
+ printf("NTSC CC ");
+ if(attr->line21_cc_1)
+ printf("1, ");
+ if(attr->line21_cc_2)
+ printf("2, ");
+ }
+
+ {
+ int height = 480;
+ if(attr->video_format != 0)
+ height = 576;
+ switch(attr->picture_size) {
+ case 0:
+ printf("720x%d, ", height);
+ break;
+ case 1:
+ printf("704x%d, ", height);
+ break;
+ case 2:
+ printf("352x%d, ", height);
+ break;
+ case 3:
+ printf("352x%d, ", height/2);
+ break;
+ default:
+ printf("(please send a bug report), ");
+ }
+ }
+
+ if(attr->letterboxed) {
+ printf("source letterboxed, ");
+ }
+
+ if(attr->film_mode) {
+ printf("film, ");
+ } else {
+ printf("video, "); //camera
+ }
+
+ printf("Unknown1: %x", attr->unknown1);
+
+}
+
+static void ifo_print_audio_attributes(audio_attr_t *attr) {
+
+ if(attr->audio_format == 0
+ && attr->multichannel_extension == 0
+ && attr->lang_type == 0
+ && attr->application_mode == 0
+ && attr->quantization == 0
+ && attr->sample_frequency == 0
+ && attr->unknown1 == 0
+ && attr->channels == 0
+ && attr->lang_extension == 0
+ && attr->unknown3 == 0) {
+ printf("-- Unspecified --");
+ return;
+ }
+
+ switch(attr->audio_format) {
+ case 0:
+ printf("ac3 ");
+ if(attr->quantization != 3)
+ printf("(please send a bug report) ac3 quant/drc not 3 (%d)", attr->quantization);
+ break;
+ case 1:
+ printf("(please send a bug report) ");
+ break;
+ case 2:
+ printf("mpeg1 ");
+ /* Falls Through. */ /* to MPEG-2 */
+ case 3:
+ printf("mpeg2ext ");
+ switch(attr->quantization) {
+ case 0:
+ printf("no drc ");
+ break;
+ case 1:
+ printf("drc ");
+ break;
+ default:
+ printf("(please send a bug report) mpeg reserved quant/drc (%d)", attr->quantization);
+ }
+ break;
+ case 4:
+ printf("lpcm ");
+ switch(attr->quantization) {
+ case 0:
+ printf("16bit ");
+ break;
+ case 1:
+ printf("20bit ");
+ break;
+ case 2:
+ printf("24bit ");
+ break;
+ case 3:
+ printf("(please send a bug report) lpcm reserved quant/drc (%d)", attr->quantization);
+ break;
+ }
+ break;
+ case 5:
+ printf("(please send a bug report) ");
+ break;
+ case 6:
+ printf("dts ");
+ if(attr->quantization != 3)
+ printf("(please send a bug report) dts quant/drc not 3 (%d)", attr->quantization);
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ if(attr->multichannel_extension)
+ printf("multichannel_extension ");
+
+ switch(attr->lang_type) {
+ case 0:
+ // not specified
+ if(attr->lang_code != 0 && attr->lang_code != 0xffff)
+ printf("Lang_code 0x%x, please send a bug report!", attr->lang_code);
+ break;
+ case 1:
+ printf("%c%c ", attr->lang_code>>8, attr->lang_code & 0xff);
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ switch(attr->application_mode) {
+ case 0:
+ // not specified
+ break;
+ case 1:
+ printf("karaoke mode ");
+ break;
+ case 2:
+ printf("surround sound mode ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ switch(attr->quantization) {
+ case 0:
+ printf("16bit ");
+ break;
+ case 1:
+ printf("20bit ");
+ break;
+ case 2:
+ printf("24bit ");
+ break;
+ case 3:
+ printf("drc ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ switch(attr->sample_frequency) {
+ case 0:
+ printf("48kHz ");
+ break;
+ case 1:
+ printf("??kHz ");
+ break;
+ default:
+ printf("sample_frequency %i (please send a bug report) ",
+ attr->sample_frequency);
+ }
+
+ printf("%dCh ", attr->channels + 1);
+
+ switch(attr->lang_extension) {
+ case 0:
+ printf("Not specified ");
+ break;
+ case 1: // Normal audio
+ printf("Normal Caption ");
+ break;
+ case 2: // visually impaired
+ printf("Audio for visually impaired ");
+ break;
+ case 3: // Directors 1
+ printf("Director's comments 1 ");
+ break;
+ case 4: // Directors 2
+ printf("Director's comments 2 ");
+ break;
+ //case 4: // Music score ?
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ printf("Unknown1: %d ", attr->unknown1);
+ printf("Unknown3: %d ", attr->unknown3);
+}
+
+static void ifo_print_subp_attributes(subp_attr_t *attr) {
+
+ if(attr->type == 0
+ && attr->zero1 == 0
+ && attr->zero2 == 0
+ && attr->lang_code == 0
+ && attr->lang_extension== 0) {
+ printf("-- Unspecified --");
+ return;
+ }
+
+ printf("type %02x ", attr->type);
+
+ if(isalpha((int)(attr->lang_code >> 8))
+ && isalpha((int)(attr->lang_code & 0xff))) {
+ printf("%c%c ", attr->lang_code >> 8, attr->lang_code & 0xff);
+ } else {
+ printf("%02x%02x ", 0xff & (unsigned)(attr->lang_code >> 8),
+ 0xff & (unsigned)(attr->lang_code & 0xff));
+ }
+
+ printf("%d ", attr->zero1);
+ printf("%d ", attr->zero2);
+
+ switch(attr->lang_extension) {
+ case 0:
+ printf("Not specified ");
+ break;
+ case 1:
+ printf("Caption with normal size character ");
+ break;
+ case 2:
+ printf("Caption with bigger size character ");
+ break;
+ case 3:
+ printf("Caption for children ");
+ break;
+ case 4:
+ printf("reserved ");
+ break;
+ case 5:
+ printf("Closed Caption with normal size character ");
+ break;
+ case 6:
+ printf("Closed Caption with bigger size character ");
+ break;
+ case 7:
+ printf("Closed Caption for children ");
+ break;
+ case 8:
+ printf("reserved ");
+ break;
+ case 9:
+ printf("Forced Caption");
+ break;
+ case 10:
+ printf("reserved ");
+ break;
+ case 11:
+ printf("reserved ");
+ break;
+ case 12:
+ printf("reserved ");
+ break;
+ case 13:
+ printf("Director's comments with normal size character ");
+ break;
+ case 14:
+ printf("Director's comments with bigger size character ");
+ break;
+ case 15:
+ printf("Director's comments for children ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+}
+
+
+static void ifoPrint_USER_OPS(user_ops_t *user_ops) {
+ uint32_t uops;
+ unsigned char *ptr = (unsigned char *)user_ops;
+
+ uops = (*ptr++ << 24);
+ uops |= (*ptr++ << 16);
+ uops |= (*ptr++ << 8);
+ uops |= (*ptr++);
+
+ if(uops == 0) {
+ printf("None\n");
+ } else if(uops == 0x01ffffff) {
+ printf("All\n");
+ } else {
+ if(user_ops->title_or_time_play)
+ printf("Title or Time Play, ");
+ if(user_ops->chapter_search_or_play)
+ printf("Chapter Search or Play, ");
+ if(user_ops->title_play)
+ printf("Title Play, ");
+ if(user_ops->stop)
+ printf("Stop, ");
+ if(user_ops->go_up)
+ printf("Go Up, ");
+ if(user_ops->time_or_chapter_search)
+ printf("Time or Chapter Search, ");
+ if(user_ops->prev_or_top_pg_search)
+ printf("Prev or Top PG Search, ");
+ if(user_ops->next_pg_search)
+ printf("Next PG Search, ");
+ if(user_ops->forward_scan)
+ printf("Forward Scan, ");
+ if(user_ops->backward_scan)
+ printf("Backward Scan, ");
+ if(user_ops->title_menu_call)
+ printf("Title Menu Call, ");
+ if(user_ops->root_menu_call)
+ printf("Root Menu Call, ");
+ if(user_ops->subpic_menu_call)
+ printf("SubPic Menu Call, ");
+ if(user_ops->audio_menu_call)
+ printf("Audio Menu Call, ");
+ if(user_ops->angle_menu_call)
+ printf("Angle Menu Call, ");
+ if(user_ops->chapter_menu_call)
+ printf("Chapter Menu Call, ");
+ if(user_ops->resume)
+ printf("Resume, ");
+ if(user_ops->button_select_or_activate)
+ printf("Button Select or Activate, ");
+ if(user_ops->still_off)
+ printf("Still Off, ");
+ if(user_ops->pause_on)
+ printf("Pause On, ");
+ if(user_ops->audio_stream_change)
+ printf("Audio Stream Change, ");
+ if(user_ops->subpic_stream_change)
+ printf("SubPic Stream Change, ");
+ if(user_ops->angle_change)
+ printf("Angle Change, ");
+ if(user_ops->karaoke_audio_pres_mode_change)
+ printf("Karaoke Audio Pres Mode Change, ");
+ if(user_ops->video_pres_mode_change)
+ printf("Video Pres Mode Change, ");
+ printf("\n");
+ }
+}
+
+
+static void ifoPrint_VMGI_MAT(vmgi_mat_t *vmgi_mat) {
+
+ printf("VMG Identifier: %.12s\n", vmgi_mat->vmg_identifier);
+ printf("Last Sector of VMG: %08x\n", vmgi_mat->vmg_last_sector);
+ printf("Last Sector of VMGI: %08x\n", vmgi_mat->vmgi_last_sector);
+ printf("Specification version number: %01x.%01x\n",
+ vmgi_mat->specification_version >> 4,
+ vmgi_mat->specification_version & 0xf);
+ /* Byte 2 of 'VMG Category' (00xx0000) is the Region Code */
+ printf("VMG Category: %08x (Region Code=%02x)\n", vmgi_mat->vmg_category, ((vmgi_mat->vmg_category >> 16) & 0xff) ^0xff);
+ printf("VMG Number of Volumes: %i\n", vmgi_mat->vmg_nr_of_volumes);
+ printf("VMG This Volume: %i\n", vmgi_mat->vmg_this_volume_nr);
+ printf("Disc side %i\n", vmgi_mat->disc_side);
+ printf("VMG Number of Title Sets %i\n", vmgi_mat->vmg_nr_of_title_sets);
+ printf("Provider ID: %.32s\n", vmgi_mat->provider_identifier);
+ printf("VMG POS Code: %08x", (uint32_t)(vmgi_mat->vmg_pos_code >> 32));
+ printf("%08x\n", (uint32_t)vmgi_mat->vmg_pos_code);
+ printf("End byte of VMGI_MAT: %08x\n", vmgi_mat->vmgi_last_byte);
+ printf("Start byte of First Play PGC (FP PGC): %08x\n",
+ vmgi_mat->first_play_pgc);
+ printf("Start sector of VMGM_VOBS: %08x\n", vmgi_mat->vmgm_vobs);
+ printf("Start sector of TT_SRPT: %08x\n", vmgi_mat->tt_srpt);
+ printf("Start sector of VMGM_PGCI_UT: %08x\n", vmgi_mat->vmgm_pgci_ut);
+ printf("Start sector of PTL_MAIT: %08x\n", vmgi_mat->ptl_mait);
+ printf("Start sector of VTS_ATRT: %08x\n", vmgi_mat->vts_atrt);
+ printf("Start sector of TXTDT_MG: %08x\n", vmgi_mat->txtdt_mgi);
+ printf("Start sector of VMGM_C_ADT: %08x\n", vmgi_mat->vmgm_c_adt);
+ printf("Start sector of VMGM_VOBU_ADMAP: %08x\n",
+ vmgi_mat->vmgm_vobu_admap);
+ printf("Video attributes of VMGM_VOBS: ");
+ ifo_print_video_attributes(&vmgi_mat->vmgm_video_attr);
+ printf("\n");
+ printf("VMGM Number of Audio attributes: %i\n",
+ vmgi_mat->nr_of_vmgm_audio_streams);
+ if(vmgi_mat->nr_of_vmgm_audio_streams > 0) {
+ printf("\tstream %i status: ", 1);
+ ifo_print_audio_attributes(&vmgi_mat->vmgm_audio_attr);
+ printf("\n");
+ }
+ printf("VMGM Number of Sub-picture attributes: %i\n",
+ vmgi_mat->nr_of_vmgm_subp_streams);
+ if(vmgi_mat->nr_of_vmgm_subp_streams > 0) {
+ printf("\tstream %2i status: ", 1);
+ ifo_print_subp_attributes(&vmgi_mat->vmgm_subp_attr);
+ printf("\n");
+ }
+}
+
+
+static void ifoPrint_VTSI_MAT(vtsi_mat_t *vtsi_mat) {
+ int i;
+
+ printf("VTS Identifier: %.12s\n", vtsi_mat->vts_identifier);
+ printf("Last Sector of VTS: %08x\n", vtsi_mat->vts_last_sector);
+ printf("Last Sector of VTSI: %08x\n", vtsi_mat->vtsi_last_sector);
+ printf("Specification version number: %01x.%01x\n",
+ vtsi_mat->specification_version>>4,
+ vtsi_mat->specification_version&0xf);
+ printf("VTS Category: %08x\n", vtsi_mat->vts_category);
+ printf("End byte of VTSI_MAT: %08x\n", vtsi_mat->vtsi_last_byte);
+ printf("Start sector of VTSM_VOBS: %08x\n", vtsi_mat->vtsm_vobs);
+ printf("Start sector of VTSTT_VOBS: %08x\n", vtsi_mat->vtstt_vobs);
+ printf("Start sector of VTS_PTT_SRPT: %08x\n", vtsi_mat->vts_ptt_srpt);
+ printf("Start sector of VTS_PGCIT: %08x\n", vtsi_mat->vts_pgcit);
+ printf("Start sector of VTSM_PGCI_UT: %08x\n", vtsi_mat->vtsm_pgci_ut);
+ printf("Start sector of VTS_TMAPT: %08x\n", vtsi_mat->vts_tmapt);
+ printf("Start sector of VTSM_C_ADT: %08x\n", vtsi_mat->vtsm_c_adt);
+ printf("Start sector of VTSM_VOBU_ADMAP: %08x\n",vtsi_mat->vtsm_vobu_admap);
+ printf("Start sector of VTS_C_ADT: %08x\n", vtsi_mat->vts_c_adt);
+ printf("Start sector of VTS_VOBU_ADMAP: %08x\n", vtsi_mat->vts_vobu_admap);
+
+ printf("Video attributes of VTSM_VOBS: ");
+ ifo_print_video_attributes(&vtsi_mat->vtsm_video_attr);
+ printf("\n");
+
+ printf("VTSM Number of Audio attributes: %i\n",
+ vtsi_mat->nr_of_vtsm_audio_streams);
+ if(vtsi_mat->nr_of_vtsm_audio_streams > 0) {
+ printf("\tstream %i status: ", 1);
+ ifo_print_audio_attributes(&vtsi_mat->vtsm_audio_attr);
+ printf("\n");
+ }
+
+ printf("VTSM Number of Sub-picture attributes: %i\n",
+ vtsi_mat->nr_of_vtsm_subp_streams);
+ if(vtsi_mat->nr_of_vtsm_subp_streams > 0) {
+ printf("\tstream %2i status: ", 1);
+ ifo_print_subp_attributes(&vtsi_mat->vtsm_subp_attr);
+ printf("\n");
+ }
+
+ printf("Video attributes of VTS_VOBS: ");
+ ifo_print_video_attributes(&vtsi_mat->vts_video_attr);
+ printf("\n");
+
+ printf("VTS Number of Audio attributes: %i\n",
+ vtsi_mat->nr_of_vts_audio_streams);
+ for(i = 0; i < vtsi_mat->nr_of_vts_audio_streams; i++) {
+ printf("\tstream %i status: ", i);
+ ifo_print_audio_attributes(&vtsi_mat->vts_audio_attr[i]);
+ printf("\n");
+ }
+
+ printf("VTS Number of Subpicture attributes: %i\n",
+ vtsi_mat->nr_of_vts_subp_streams);
+ for(i = 0; i < vtsi_mat->nr_of_vts_subp_streams; i++) {
+ printf("\tstream %2i status: ", i);
+ ifo_print_subp_attributes(&vtsi_mat->vts_subp_attr[i]);
+ printf("\n");
+ }
+}
+
+
+static void ifoPrint_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
+ int i;
+
+ if(cmd_tbl == NULL) {
+ printf("No Command table present\n");
+ return;
+ }
+
+ printf("Number of Pre commands: %i\n", cmd_tbl->nr_of_pre);
+ for(i = 0; i < cmd_tbl->nr_of_pre; i++) {
+ ifo_print_cmd(i, &cmd_tbl->pre_cmds[i]);
+ }
+
+ printf("Number of Post commands: %i\n", cmd_tbl->nr_of_post);
+ for(i = 0; i < cmd_tbl->nr_of_post; i++) {
+ ifo_print_cmd(i, &cmd_tbl->post_cmds[i]);
+ }
+
+ printf("Number of Cell commands: %i\n", cmd_tbl->nr_of_cell);
+ for(i = 0; i < cmd_tbl->nr_of_cell; i++) {
+ ifo_print_cmd(i, &cmd_tbl->cell_cmds[i]);
+ }
+}
+
+
+static void ifoPrint_PGC_PROGRAM_MAP(pgc_program_map_t *program_map, int nr) {
+ int i;
+
+ if(program_map == NULL) {
+ printf("No Program map present\n");
+ return;
+ }
+
+ for(i = 0; i < nr; i++) {
+ printf("Program %3i Entry Cell: %3i\n", i + 1, program_map[i]);
+ }
+}
+
+
+static void ifoPrint_CELL_PLAYBACK(cell_playback_t *cell_playback, int nr) {
+ int i;
+
+ if(cell_playback == NULL) {
+ printf("No Cell Playback info present\n");
+ return;
+ }
+
+ for(i=0;i<nr;i++) {
+ printf("Cell: %3i ", i + 1);
+
+ dvdread_print_time(&cell_playback[i].playback_time);
+ printf("\t");
+
+ if(cell_playback[i].block_mode || cell_playback[i].block_type) {
+ const char *s;
+ switch(cell_playback[i].block_mode) {
+ case 0:
+ s = "not a"; break;
+ case 1:
+ s = "the first"; break;
+ case 2:
+ default:
+ s = ""; break;
+ case 3:
+ s = "last"; break;
+ }
+ printf("%s cell in the block ", s);
+
+ switch(cell_playback[i].block_type) {
+ case 0:
+ printf("not part of the block ");
+ break;
+ case 1:
+ printf("angle block ");
+ break;
+ case 2:
+ case 3:
+ printf("(send bug report) ");
+ break;
+ }
+ }
+ if(cell_playback[i].seamless_play)
+ printf("presented seamlessly ");
+ if(cell_playback[i].interleaved)
+ printf("cell is interleaved ");
+ if(cell_playback[i].stc_discontinuity)
+ printf("STC_discontinuty ");
+ if(cell_playback[i].seamless_angle)
+ printf("only seamless angle ");
+ if(cell_playback[i].playback_mode)
+ printf("only still VOBUs ");
+ if(cell_playback[i].restricted)
+ printf("restricted cell ");
+ if(cell_playback[i].cell_type)
+ printf("cell type 0x%x ", cell_playback[i].cell_type);
+ if(cell_playback[i].still_time)
+ printf("still time %d ", cell_playback[i].still_time);
+ if(cell_playback[i].cell_cmd_nr)
+ printf("cell command %d", cell_playback[i].cell_cmd_nr);
+
+ printf("\n\tStart sector: %08x\tFirst ILVU end sector: %08x\n",
+ cell_playback[i].first_sector,
+ cell_playback[i].first_ilvu_end_sector);
+ printf("\tEnd sector: %08x\tLast VOBU start sector: %08x\n",
+ cell_playback[i].last_sector,
+ cell_playback[i].last_vobu_start_sector);
+ }
+}
+
+static void ifoPrint_CELL_POSITION(cell_position_t *cell_position, int nr) {
+ int i;
+
+ if(cell_position == NULL) {
+ printf("No Cell Position info present\n");
+ return;
+ }
+
+ for(i=0;i<nr;i++) {
+ printf("Cell: %3i has VOB ID: %3i, Cell ID: %3i\n", i + 1,
+ cell_position[i].vob_id_nr, cell_position[i].cell_nr);
+ }
+}
+
+
+static void ifoPrint_PGC(pgc_t *pgc) {
+ int i;
+
+ if (!pgc) {
+ printf("None\n");
+ return;
+ }
+ printf("Number of Programs: %i\n", pgc->nr_of_programs);
+ printf("Number of Cells: %i\n", pgc->nr_of_cells);
+ /* Check that time is 0:0:0:0 also if nr_of_programs==0 */
+ printf("Playback time: ");
+ dvdread_print_time(&pgc->playback_time); printf("\n");
+
+ /* If no programs/no time then does this mean anything? */
+ printf("Prohibited user operations: ");
+ ifoPrint_USER_OPS(&pgc->prohibited_ops);
+
+ for(i = 0; i < 8; i++) {
+ if(pgc->audio_control[i] & 0x8000) { /* The 'is present' bit */
+ printf("Audio stream %i control: %04x\n",
+ i, pgc->audio_control[i]);
+ }
+ }
+
+ for(i = 0; i < 32; i++) {
+ if(pgc->subp_control[i] & 0x80000000) { /* The 'is present' bit */
+ printf("Subpicture stream %2i control: %08x: 4:3=%d, Wide=%d, Letterbox=%d, Pan-Scan=%d\n",
+ i, pgc->subp_control[i],
+ (pgc->subp_control[i] >>24) & 0x1f,
+ (pgc->subp_control[i] >>16) & 0x1f,
+ (pgc->subp_control[i] >>8) & 0x1f,
+ (pgc->subp_control[i] ) & 0x1f);
+ }
+ }
+
+ printf("Next PGC number: %i\n", pgc->next_pgc_nr);
+ printf("Prev PGC number: %i\n", pgc->prev_pgc_nr);
+ printf("GoUp PGC number: %i\n", pgc->goup_pgc_nr);
+ if(pgc->nr_of_programs != 0) {
+ printf("Still time: %i seconds (255=inf)\n", pgc->still_time);
+ printf("PG Playback mode %02x\n", pgc->pg_playback_mode);
+ }
+
+ if(pgc->nr_of_programs != 0) {
+ for(i = 0; i < 16; i++) {
+ printf("Color %2i: %08x\n", i, pgc->palette[i]);
+ }
+ }
+
+ /* Memory offsets to div. tables. */
+ ifoPrint_PGC_COMMAND_TBL(pgc->command_tbl);
+ ifoPrint_PGC_PROGRAM_MAP(pgc->program_map, pgc->nr_of_programs);
+ ifoPrint_CELL_PLAYBACK(pgc->cell_playback, pgc->nr_of_cells);
+ ifoPrint_CELL_POSITION(pgc->cell_position, pgc->nr_of_cells);
+}
+
+
+static void ifoPrint_TT_SRPT(tt_srpt_t *tt_srpt) {
+ int i;
+
+ printf("Number of TitleTrack search pointers: %i\n",
+ tt_srpt->nr_of_srpts);
+ for(i=0;i<tt_srpt->nr_of_srpts;i++) {
+ printf("Title Track index %i\n", i + 1);
+ printf("\tTitle set number (VTS): %i",
+ tt_srpt->title[i].title_set_nr);
+ printf("\tVTS_TTN: %i\n", tt_srpt->title[i].vts_ttn);
+ printf("\tNumber of PTTs: %i\n", tt_srpt->title[i].nr_of_ptts);
+ printf("\tNumber of angles: %i\n",
+ tt_srpt->title[i].nr_of_angles);
+
+ printf("\tTitle playback type: (%02x)\n",
+ *(uint8_t *)&(tt_srpt->title[i].pb_ty));
+ printf("\t\t%s\n",
+ tt_srpt->title[i].pb_ty.multi_or_random_pgc_title ? "Random or Shuffle" : "Sequential");
+ if (tt_srpt->title[i].pb_ty.jlc_exists_in_cell_cmd) printf("\t\tJump/Link/Call exists in cell cmd\n");
+ if (tt_srpt->title[i].pb_ty.jlc_exists_in_prepost_cmd) printf("\t\tJump/Link/Call exists in pre/post cmd\n");
+ if (tt_srpt->title[i].pb_ty.jlc_exists_in_button_cmd) printf("\t\tJump/Link/Call exists in button cmd\n");
+ if (tt_srpt->title[i].pb_ty.jlc_exists_in_tt_dom) printf("\t\tJump/Link/Call exists in tt_dom cmd\n");
+ printf("\t\tTitle or time play:%u\n", tt_srpt->title[i].pb_ty.title_or_time_play);
+ printf("\t\tChapter search or play:%u\n", tt_srpt->title[i].pb_ty.chapter_search_or_play);
+
+ printf("\tParental ID field: %04x\n",
+ tt_srpt->title[i].parental_id);
+ printf("\tTitle set starting sector %08x\n",
+ tt_srpt->title[i].title_set_sector);
+ }
+}
+
+
+static void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *vts_ptt_srpt) {
+ int i, j;
+ printf(" nr_of_srpts %i last byte %i\n",
+ vts_ptt_srpt->nr_of_srpts,
+ vts_ptt_srpt->last_byte);
+ for(i=0;i<vts_ptt_srpt->nr_of_srpts;i++) {
+ for(j=0;j<vts_ptt_srpt->title[i].nr_of_ptts;j++) {
+ printf("VTS_PTT_SRPT - Title %3i part %3i: PGC: %3i PG: %3i\n",
+ i + 1, j + 1,
+ vts_ptt_srpt->title[i].ptt[j].pgcn,
+ vts_ptt_srpt->title[i].ptt[j].pgn );
+ }
+ }
+}
+
+
+static void hexdump(uint8_t *ptr, int len) {
+ while(len--)
+ printf("%02x ", *ptr++);
+}
+
+static void ifoPrint_PTL_MAIT(ptl_mait_t *ptl_mait) {
+ int i, j;
+
+ printf("Number of Countries: %i\n", ptl_mait->nr_of_countries);
+ printf("Number of VTSs: %i\n", ptl_mait->nr_of_vtss);
+ //printf("Last byte: %i\n", ptl_mait->last_byte);
+
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+ printf("Country code: %c%c\n",
+ ptl_mait->countries[i].country_code >> 8,
+ ptl_mait->countries[i].country_code & 0xff);
+ /*
+ printf("Start byte: %04x %i\n",
+ ptl_mait->countries[i].pf_ptl_mai_start_byte,
+ ptl_mait->countries[i].pf_ptl_mai_start_byte);
+ */
+ /* This seems to be pointing at a array with 8 2byte fields per VTS
+ ? and one extra for the menu? always an odd number of VTSs on
+ all the dics I tested so it might be padding to even also.
+ If it is for the menu it probably the first entry. */
+ for(j=0;j<8;j++) {
+ hexdump( (uint8_t *)ptl_mait->countries - PTL_MAIT_COUNTRY_SIZE
+ + ptl_mait->countries[i].pf_ptl_mai_start_byte
+ + j*(ptl_mait->nr_of_vtss+1)*2, (ptl_mait->nr_of_vtss+1)*2);
+ printf("\n");
+ }
+ }
+}
+
+static void ifoPrint_VTS_TMAPT(vts_tmapt_t *vts_tmapt) {
+ unsigned int timeunit;
+ int i, j;
+
+ printf("Number of VTS_TMAPS: %i\n", vts_tmapt->nr_of_tmaps);
+ printf("Last byte: %i\n", vts_tmapt->last_byte);
+
+ for(i = 0; i < vts_tmapt->nr_of_tmaps; i++) {
+ printf("TMAP %i (number matches title PGC number.)\n", i + 1);
+ printf(" offset %d relative to VTS_TMAPTI\n", vts_tmapt->tmap_offset[i]);
+ printf(" Time unit (seconds): %i\n", vts_tmapt->tmap[i].tmu);
+ printf(" Number of entries: %i\n", vts_tmapt->tmap[i].nr_of_entries);
+ timeunit = vts_tmapt->tmap[i].tmu;
+ for(j = 0; j < vts_tmapt->tmap[i].nr_of_entries; j++) {
+ unsigned int ac_time = timeunit * (j + 1);
+ printf("Time: %2i:%02i:%02i VOBU Sector: 0x%08x %s\n",
+ ac_time / (60 * 60), (ac_time / 60) % 60, ac_time % 60,
+ vts_tmapt->tmap[i].map_ent[j] & 0x7fffffff,
+ (vts_tmapt->tmap[i].map_ent[j] >> 31) ? "discontinuity" : "");
+ }
+ }
+}
+
+static void ifoPrint_C_ADT(c_adt_t *c_adt) {
+ int i, entries;
+
+ printf("Number of VOBs in this VOBS: %i\n", c_adt->nr_of_vobs);
+ //entries = c_adt->nr_of_vobs;
+ entries = (c_adt->last_byte + 1 - C_ADT_SIZE)/sizeof(c_adt_t);
+
+ for(i = 0; i < entries; i++) {
+ printf("VOB ID: %3i, Cell ID: %3i ",
+ c_adt->cell_adr_table[i].vob_id, c_adt->cell_adr_table[i].cell_id);
+ printf("Sector (first): 0x%08x (last): 0x%08x\n",
+ c_adt->cell_adr_table[i].start_sector,
+ c_adt->cell_adr_table[i].last_sector);
+ }
+}
+
+
+static void ifoPrint_VOBU_ADMAP(vobu_admap_t *vobu_admap) {
+ int i, entries;
+
+ entries = (vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE)/4;
+ for(i = 0; i < entries; i++) {
+ printf("VOBU %5i First sector: 0x%08x\n", i + 1,
+ vobu_admap->vobu_start_sectors[i]);
+ }
+}
+
+static const char *ifo_print_menu_name(int type) {
+ const char *menu_name;
+ menu_name="";
+ switch (type) {
+ case 2:
+ menu_name="Title";
+ break;
+ case 3:
+ menu_name = "Root";
+ break;
+ case 4:
+ menu_name = "Sub-Picture";
+ break;
+ case 5:
+ menu_name = "Audio";
+ break;
+ case 6:
+ menu_name = "Angle";
+ break;
+ case 7:
+ menu_name = "PTT (Chapter)";
+ break;
+ default:
+ menu_name = "Unknown";
+ break;
+ }
+ return &menu_name[0];
+}
+
+/* pgc_type=1 for menu, 0 for title. */
+static void ifoPrint_PGCIT(pgcit_t *pgcit, int pgc_type) {
+ int i;
+
+ printf("\nNumber of Program Chains: %3i\n", pgcit->nr_of_pgci_srp);
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ printf("\nProgram (PGC): %3i\n", i + 1);
+ if (pgc_type) {
+ printf("PGC Category: Entry PGC %d, Menu Type=0x%02x:%s (Entry id 0x%02x), ",
+ pgcit->pgci_srp[i].entry_id >> 7,
+ pgcit->pgci_srp[i].entry_id & 0xf,
+ ifo_print_menu_name(pgcit->pgci_srp[i].entry_id & 0xf),
+ pgcit->pgci_srp[i].entry_id);
+ } else {
+ printf("PGC Category: %s VTS_TTN:0x%02x (Entry id 0x%02x), ",
+ pgcit->pgci_srp[i].entry_id >> 7 ? "At Start of" : "During",
+ pgcit->pgci_srp[i].entry_id & 0xf,
+ pgcit->pgci_srp[i].entry_id);
+ }
+ printf("Parental ID mask 0x%04x\n", pgcit->pgci_srp[i].ptl_id_mask);
+ ifoPrint_PGC(pgcit->pgci_srp[i].pgc);
+ }
+}
+
+
+static void ifoPrint_PGCI_UT(pgci_ut_t *pgci_ut) {
+ int i, menu;
+
+ printf("Number of Menu Language Units (PGCI_LU): %3i\n", pgci_ut->nr_of_lus);
+ for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+ printf("\nMenu Language Unit %d\n", i+1);
+ printf("\nMenu Language Code: %c%c\n",
+ pgci_ut->lu[i].lang_code >> 8,
+ pgci_ut->lu[i].lang_code & 0xff);
+
+ menu = pgci_ut->lu[i].exists;
+ printf("Menu Existence: %02x: ", menu);
+ if (menu == 0) {
+ printf("No menus ");
+ }
+ if (menu & 0x80) {
+ printf("Root ");
+ menu^=0x80;
+ }
+ if (menu & 0x40) {
+ printf("Sub-Picture ");
+ menu^=0x40;
+ }
+ if (menu & 0x20) {
+ printf("Audio ");
+ menu^=0x20;
+ }
+ if (menu & 0x10) {
+ printf("Angle ");
+ menu^=0x10;
+ }
+ if (menu & 0x08) {
+ printf("PTT ");
+ menu^=0x08;
+ }
+ if (menu > 0) {
+ printf("Unknown extra menus ");
+ menu^=0x08;
+ }
+ printf("\n");
+ ifoPrint_PGCIT(pgci_ut->lu[i].pgcit, 1);
+ }
+}
+
+
+static void ifoPrint_VTS_ATTRIBUTES(vts_attributes_t *vts_attributes) {
+ int i;
+
+ printf("VTS_CAT Application type: %08x\n", vts_attributes->vts_cat);
+
+ printf("Video attributes of VTSM_VOBS: ");
+ ifo_print_video_attributes(&vts_attributes->vtsm_vobs_attr);
+ printf("\n");
+ printf("Number of Audio streams: %i\n",
+ vts_attributes->nr_of_vtsm_audio_streams);
+ if(vts_attributes->nr_of_vtsm_audio_streams > 0) {
+ printf("\tstream %i attributes: ", 1);
+ ifo_print_audio_attributes(&vts_attributes->vtsm_audio_attr);
+ printf("\n");
+ }
+ printf("Number of Subpicture streams: %i\n",
+ vts_attributes->nr_of_vtsm_subp_streams);
+ if(vts_attributes->nr_of_vtsm_subp_streams > 0) {
+ printf("\tstream %2i attributes: ", 1);
+ ifo_print_subp_attributes(&vts_attributes->vtsm_subp_attr);
+ printf("\n");
+ }
+
+ printf("Video attributes of VTSTT_VOBS: ");
+ ifo_print_video_attributes(&vts_attributes->vtstt_vobs_video_attr);
+ printf("\n");
+ printf("Number of Audio streams: %i\n",
+ vts_attributes->nr_of_vtstt_audio_streams);
+ for(i = 0; i < vts_attributes->nr_of_vtstt_audio_streams; i++) {
+ printf("\tstream %i attributes: ", i);
+ ifo_print_audio_attributes(&vts_attributes->vtstt_audio_attr[i]);
+ printf("\n");
+ }
+
+ printf("Number of Subpicture streams: %i\n",
+ vts_attributes->nr_of_vtstt_subp_streams);
+ for(i = 0; i < vts_attributes->nr_of_vtstt_subp_streams; i++) {
+ printf("\tstream %2i attributes: ", i);
+ ifo_print_subp_attributes(&vts_attributes->vtstt_subp_attr[i]);
+ printf("\n");
+ }
+}
+
+
+static void ifoPrint_VTS_ATRT(vts_atrt_t *vts_atrt) {
+ int i;
+
+ printf("Number of Video Title Sets: %3i\n", vts_atrt->nr_of_vtss);
+ for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+ printf("\nVideo Title Set %i\n", i + 1);
+ ifoPrint_VTS_ATTRIBUTES(&vts_atrt->vts[i]);
+ }
+}
+
+
+void ifo_print(dvd_reader_t *dvd, int title) {
+ ifo_handle_t *ifohandle;
+ printf("Local ifo_print\n");
+ ifohandle = ifoOpen(dvd, title);
+ if(!ifohandle) {
+ Log0(dvd, "Can't open info file for title %d", title);
+ return;
+ }
+
+
+ if(ifohandle->vmgi_mat) {
+
+ printf("VMG top level\n-------------\n");
+ ifoPrint_VMGI_MAT(ifohandle->vmgi_mat);
+
+ printf("\nFirst Play PGC\n--------------\n");
+ if(ifohandle->first_play_pgc)
+ ifoPrint_PGC(ifohandle->first_play_pgc);
+ else
+ printf("No First Play PGC present\n");
+
+ printf("\nTitle Track search pointer table\n");
+ printf( "------------------------------------------------\n");
+ ifoPrint_TT_SRPT(ifohandle->tt_srpt);
+
+ printf("\nMenu PGCI Unit table\n");
+ printf( "--------------------\n");
+ if(ifohandle->pgci_ut) {
+ ifoPrint_PGCI_UT(ifohandle->pgci_ut);
+ } else {
+ printf("No PGCI Unit table present\n");
+ }
+
+ printf("\nParental Management Information table\n");
+ printf( "------------------------------------\n");
+ if(ifohandle->ptl_mait) {
+ ifoPrint_PTL_MAIT(ifohandle->ptl_mait);
+ } else {
+ printf("No Parental Management Information present\n");
+ }
+
+ printf("\nVideo Title Set Attribute Table\n");
+ printf( "-------------------------------\n");
+ ifoPrint_VTS_ATRT(ifohandle->vts_atrt);
+
+ printf("\nText Data Manager Information\n");
+ printf( "-----------------------------\n");
+ if(ifohandle->txtdt_mgi) {
+ //ifo_print_TXTDT_MGI(&(vmgi->txtdt_mgi));
+ } else {
+ printf("No Text Data Manager Information present\n");
+ }
+
+ printf("\nMenu Cell Address table\n");
+ printf( "-----------------\n");
+ if(ifohandle->menu_c_adt) {
+ ifoPrint_C_ADT(ifohandle->menu_c_adt);
+ } else {
+ printf("No Menu Cell Address table present\n");
+ }
+
+ printf("\nVideo Manager Menu VOBU address map\n");
+ printf( "-----------------\n");
+ if(ifohandle->menu_vobu_admap) {
+ ifoPrint_VOBU_ADMAP(ifohandle->menu_vobu_admap);
+ } else {
+ printf("No Menu VOBU address map present\n");
+ }
+ }
+
+
+ if(ifohandle->vtsi_mat) {
+
+ printf("VTS top level\n-------------\n");
+ ifoPrint_VTSI_MAT(ifohandle->vtsi_mat);
+
+ printf("\nPart of Title Track search pointer table\n");
+ printf( "----------------------------------------------\n");
+ ifoPrint_VTS_PTT_SRPT(ifohandle->vts_ptt_srpt);
+
+ printf("\nPGCI Unit table\n");
+ printf( "--------------------\n");
+ ifoPrint_PGCIT(ifohandle->vts_pgcit, 0);
+
+ printf("\nMenu PGCI Unit table\n");
+ printf( "--------------------\n");
+ if(ifohandle->pgci_ut) {
+ ifoPrint_PGCI_UT(ifohandle->pgci_ut);
+ } else {
+ printf("No Menu PGCI Unit table present\n");
+ }
+
+ printf("\nVTS Time Map table\n");
+ printf( "-----------------\n");
+ if(ifohandle->vts_tmapt) {
+ ifoPrint_VTS_TMAPT(ifohandle->vts_tmapt);
+ } else {
+ printf("No VTS Time Map table present\n");
+ }
+
+ printf("\nMenu Cell Address table\n");
+ printf( "-----------------\n");
+ if(ifohandle->menu_c_adt) {
+ ifoPrint_C_ADT(ifohandle->menu_c_adt);
+ } else {
+ printf("No Cell Address table present\n");
+ }
+
+ printf("\nVideo Title Set Menu VOBU address map\n");
+ printf( "-----------------\n");
+ if(ifohandle->menu_vobu_admap) {
+ ifoPrint_VOBU_ADMAP(ifohandle->menu_vobu_admap);
+ } else {
+ printf("No Menu VOBU address map present\n");
+ }
+
+ printf("\nCell Address table\n");
+ printf( "-----------------\n");
+ ifoPrint_C_ADT(ifohandle->vts_c_adt);
+
+ printf("\nVideo Title Set VOBU address map\n");
+ printf( "-----------------\n");
+ ifoPrint_VOBU_ADMAP(ifohandle->vts_vobu_admap);
+ }
+
+ ifoClose(ifohandle);
+}
diff --git a/libdvdread-embedded/src/ifo_read.c b/libdvdread-embedded/src/ifo_read.c
new file mode 100644
index 0000000..6fd913d
--- /dev/null
+++ b/libdvdread-embedded/src/ifo_read.c
@@ -0,0 +1,2341 @@
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003
+ * Björn Englund <d4bjorn@dtek.chalmers.se>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include "bswap.h"
+#include "dvdread/ifo_types.h"
+#include "dvdread/ifo_read.h"
+#include "dvdread/dvd_reader.h"
+#include "dvdread_internal.h"
+#include "dvdread/bitreader.h"
+
+#ifndef DVD_BLOCK_LEN
+#define DVD_BLOCK_LEN 2048
+#endif
+
+#define PRIV(a) container_of(a, struct ifo_handle_private_s, handle)
+
+#define CHECK_VALUE(arg)\
+ if(!(arg)) {\
+ Log1(ifop->ctx, "CHECK_VALUE failed in %s:%i for %s",\
+ __FILE__, __LINE__, # arg );\
+ }
+
+#ifndef NDEBUG
+static inline char * makehexdump(const uint8_t *p_CZ, size_t i_CZ)
+{
+ char *alloc = malloc(i_CZ * 2 + 1);
+ if(alloc)
+ {
+ *alloc = 0;
+ for(size_t i = 0; i < i_CZ; i++)
+ sprintf(&alloc[i*2], "%02x", *((uint8_t*)&p_CZ[i]));
+ }
+ return alloc;
+}
+#define CHECK_ZERO0(arg) \
+ if(arg != 0) { \
+ Log1(ifop->ctx, "Zero check failed in %s:%i\n for %s = 0x%x", \
+ __FILE__, __LINE__, # arg, arg); \
+ }
+#define CHECK_ZERO(arg) \
+ if(memcmp(my_friendly_zeros, &arg, sizeof(arg))) { \
+ char *dump = makehexdump((const uint8_t *)&arg, sizeof(arg)); \
+ Log0(ifop->ctx, "Zero check failed in %s:%i for %s : 0x%s", \
+ __FILE__, __LINE__, # arg, dump ); \
+ free(dump); \
+ }
+static const uint8_t my_friendly_zeros[2048];
+#else
+#define CHECK_ZERO0(arg) (void)(arg)
+#define CHECK_ZERO(arg) (void)(arg)
+#endif
+
+
+/* Prototypes for internal functions */
+static int ifoRead_VMG(ifo_handle_t *ifofile);
+static int ifoRead_VTS(ifo_handle_t *ifofile);
+static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset);
+static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile,
+ pgc_command_tbl_t *cmd_tbl,
+ unsigned int offset);
+static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile,
+ pgc_program_map_t *program_map,
+ unsigned int nr, unsigned int offset);
+static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile,
+ cell_playback_t *cell_playback,
+ unsigned int nr, unsigned int offset);
+static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile,
+ cell_position_t *cell_position,
+ unsigned int nr, unsigned int offset);
+static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile,
+ vts_attributes_t *vts_attributes,
+ unsigned int offset);
+static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile, c_adt_t *c_adt,
+ unsigned int sector);
+static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile,
+ vobu_admap_t *vobu_admap,
+ unsigned int sector);
+static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit,
+ unsigned int offset);
+
+static void ifoFree_PGC(pgc_t **pgc);
+static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl);
+static void ifoFree_PGCIT_internal(pgcit_t **pgcit);
+
+static inline int DVDFileSeekForce_( dvd_file_t *dvd_file, uint32_t offset, int force_size ) {
+ return (DVDFileSeekForce(dvd_file, (int)offset, force_size) == (int)offset);
+}
+
+static inline int DVDFileSeek_( dvd_file_t *dvd_file, uint32_t offset ) {
+ return (DVDFileSeek(dvd_file, (int)offset) == (int)offset);
+}
+
+static void read_video_attr(video_attr_t *va) {
+ getbits_state_t state;
+ uint8_t buf[sizeof(video_attr_t)];
+
+ memcpy(buf, va, sizeof(video_attr_t));
+ if (!dvdread_getbits_init(&state, buf)) abort();
+ va->mpeg_version = dvdread_getbits(&state, 2);
+ va->video_format = dvdread_getbits(&state, 2);
+ va->display_aspect_ratio = dvdread_getbits(&state, 2);
+ va->permitted_df = dvdread_getbits(&state, 2);
+ va->line21_cc_1 = dvdread_getbits(&state, 1);
+ va->line21_cc_2 = dvdread_getbits(&state, 1);
+ va->unknown1 = dvdread_getbits(&state, 1);
+ va->bit_rate = dvdread_getbits(&state, 1);
+ va->picture_size = dvdread_getbits(&state, 2);
+ va->letterboxed = dvdread_getbits(&state, 1);
+ va->film_mode = dvdread_getbits(&state, 1);
+}
+
+static void read_audio_attr(audio_attr_t *aa) {
+ getbits_state_t state;
+ uint8_t buf[sizeof(audio_attr_t)];
+
+ memcpy(buf, aa, sizeof(audio_attr_t));
+ if (!dvdread_getbits_init(&state, buf)) abort();
+ aa->audio_format = dvdread_getbits(&state, 3);
+ aa->multichannel_extension = dvdread_getbits(&state, 1);
+ aa->lang_type = dvdread_getbits(&state, 2);
+ aa->application_mode = dvdread_getbits(&state, 2);
+ aa->quantization = dvdread_getbits(&state, 2);
+ aa->sample_frequency = dvdread_getbits(&state, 2);
+ aa->unknown1 = dvdread_getbits(&state, 1);
+ aa->channels = dvdread_getbits(&state, 3);
+ aa->lang_code = dvdread_getbits(&state, 16);
+ aa->lang_extension = dvdread_getbits(&state, 8);
+ aa->code_extension = dvdread_getbits(&state, 8);
+ aa->unknown3 = dvdread_getbits(&state, 8);
+ aa->app_info.karaoke.unknown4 = dvdread_getbits(&state, 1);
+ aa->app_info.karaoke.channel_assignment = dvdread_getbits(&state, 3);
+ aa->app_info.karaoke.version = dvdread_getbits(&state, 2);
+ aa->app_info.karaoke.mc_intro = dvdread_getbits(&state, 1);
+ aa->app_info.karaoke.mode = dvdread_getbits(&state, 1);
+}
+
+static void read_multichannel_ext(multichannel_ext_t *me) {
+ getbits_state_t state;
+ uint8_t buf[sizeof(multichannel_ext_t)];
+
+ memcpy(buf, me, sizeof(multichannel_ext_t));
+ if (!dvdread_getbits_init(&state, buf)) abort();
+ me->zero1 = dvdread_getbits(&state, 7);
+ me->ach0_gme = dvdread_getbits(&state, 1);
+ me->zero2 = dvdread_getbits(&state, 7);
+ me->ach1_gme = dvdread_getbits(&state, 1);
+ me->zero3 = dvdread_getbits(&state, 4);
+ me->ach2_gv1e = dvdread_getbits(&state, 1);
+ me->ach2_gv2e = dvdread_getbits(&state, 1);
+ me->ach2_gm1e = dvdread_getbits(&state, 1);
+ me->ach2_gm2e = dvdread_getbits(&state, 1);
+ me->zero4 = dvdread_getbits(&state, 4);
+ me->ach3_gv1e = dvdread_getbits(&state, 1);
+ me->ach3_gv2e = dvdread_getbits(&state, 1);
+ me->ach3_gmAe = dvdread_getbits(&state, 1);
+ me->ach3_se2e = dvdread_getbits(&state, 1);
+ me->zero5 = dvdread_getbits(&state, 4);
+ me->ach4_gv1e = dvdread_getbits(&state, 1);
+ me->ach4_gv2e = dvdread_getbits(&state, 1);
+ me->ach4_gmBe = dvdread_getbits(&state, 1);
+ me->ach4_seBe = dvdread_getbits(&state, 1);
+}
+
+static void read_subp_attr(subp_attr_t *sa) {
+ getbits_state_t state;
+ uint8_t buf[sizeof(subp_attr_t)];
+
+ memcpy(buf, sa, sizeof(subp_attr_t));
+ if (!dvdread_getbits_init(&state, buf)) abort();
+ sa->code_mode = dvdread_getbits(&state, 3);
+ sa->zero1 = dvdread_getbits(&state, 3);
+ sa->type = dvdread_getbits(&state, 2);
+ sa->zero2 = dvdread_getbits(&state, 8);
+ sa->lang_code = dvdread_getbits(&state, 16);
+ sa->lang_extension = dvdread_getbits(&state, 8);
+ sa->code_extension = dvdread_getbits(&state, 8);
+}
+
+static void read_user_ops(user_ops_t *uo) {
+ getbits_state_t state;
+ uint8_t buf[sizeof(user_ops_t)];
+
+ memcpy(buf, uo, sizeof(user_ops_t));
+ if (!dvdread_getbits_init(&state, buf)) abort();
+ uo->zero = dvdread_getbits(&state, 7);
+ uo->video_pres_mode_change = dvdread_getbits(&state, 1);
+ uo->karaoke_audio_pres_mode_change = dvdread_getbits(&state, 1);
+ uo->angle_change = dvdread_getbits(&state, 1);
+ uo->subpic_stream_change = dvdread_getbits(&state, 1);
+ uo->audio_stream_change = dvdread_getbits(&state, 1);
+ uo->pause_on = dvdread_getbits(&state, 1);
+ uo->still_off = dvdread_getbits(&state, 1);
+ uo->button_select_or_activate = dvdread_getbits(&state, 1);
+ uo->resume = dvdread_getbits(&state, 1);
+ uo->chapter_menu_call = dvdread_getbits(&state, 1);
+ uo->angle_menu_call = dvdread_getbits(&state, 1);
+ uo->audio_menu_call = dvdread_getbits(&state, 1);
+ uo->subpic_menu_call = dvdread_getbits(&state, 1);
+ uo->root_menu_call = dvdread_getbits(&state, 1);
+ uo->title_menu_call = dvdread_getbits(&state, 1);
+ uo->backward_scan = dvdread_getbits(&state, 1);
+ uo->forward_scan = dvdread_getbits(&state, 1);
+ uo->next_pg_search = dvdread_getbits(&state, 1);
+ uo->prev_or_top_pg_search = dvdread_getbits(&state, 1);
+ uo->time_or_chapter_search = dvdread_getbits(&state, 1);
+ uo->go_up = dvdread_getbits(&state, 1);
+ uo->stop = dvdread_getbits(&state, 1);
+ uo->title_play = dvdread_getbits(&state, 1);
+ uo->chapter_search_or_play = dvdread_getbits(&state, 1);
+ uo->title_or_time_play = dvdread_getbits(&state, 1);
+}
+
+static void read_pgci_srp(pgci_srp_t *ps) {
+ getbits_state_t state;
+ uint8_t buf[sizeof(pgci_srp_t)];
+
+ memcpy(buf, ps, sizeof(pgci_srp_t));
+ if (!dvdread_getbits_init(&state, buf)) abort();
+ ps->entry_id = dvdread_getbits(&state, 8);
+ ps->block_mode = dvdread_getbits(&state, 2);
+ ps->block_type = dvdread_getbits(&state, 2);
+ ps->zero_1 = dvdread_getbits(&state, 4);
+ ps->ptl_id_mask = dvdread_getbits(&state, 16);
+ ps->pgc_start_byte = dvdread_getbits(&state, 32);
+}
+
+static void read_cell_playback(cell_playback_t *cp) {
+ getbits_state_t state;
+ uint8_t buf[sizeof(cell_playback_t)];
+
+ memcpy(buf, cp, sizeof(cell_playback_t));
+ if (!dvdread_getbits_init(&state, buf)) abort();
+ cp->block_mode = dvdread_getbits(&state, 2);
+ cp->block_type = dvdread_getbits(&state, 2);
+ cp->seamless_play = dvdread_getbits(&state, 1);
+ cp->interleaved = dvdread_getbits(&state, 1);
+ cp->stc_discontinuity = dvdread_getbits(&state, 1);
+ cp->seamless_angle = dvdread_getbits(&state, 1);
+ cp->zero_1 = dvdread_getbits(&state, 1);
+ cp->playback_mode = dvdread_getbits(&state, 1);
+ cp->restricted = dvdread_getbits(&state, 1);
+ cp->cell_type = dvdread_getbits(&state, 5);
+ cp->still_time = dvdread_getbits(&state, 8);
+ cp->cell_cmd_nr = dvdread_getbits(&state, 8);
+
+ cp->playback_time.hour = dvdread_getbits(&state, 8);
+ cp->playback_time.minute = dvdread_getbits(&state, 8);
+ cp->playback_time.second = dvdread_getbits(&state, 8);
+ cp->playback_time.frame_u = dvdread_getbits(&state, 8);
+
+ cp->first_sector = dvdread_getbits(&state, 32);
+ cp->first_ilvu_end_sector = dvdread_getbits(&state, 32);
+ cp->last_vobu_start_sector = dvdread_getbits(&state, 32);
+ cp->last_sector = dvdread_getbits(&state, 32);
+}
+
+static void read_playback_type(playback_type_t *pt) {
+ getbits_state_t state;
+ uint8_t buf[sizeof(playback_type_t)];
+
+ memcpy(buf, pt, sizeof(playback_type_t));
+ if (!dvdread_getbits_init(&state, buf)) abort();
+ pt->zero_1 = dvdread_getbits(&state, 1);
+ pt->multi_or_random_pgc_title = dvdread_getbits(&state, 1);
+ pt->jlc_exists_in_cell_cmd = dvdread_getbits(&state, 1);
+ pt->jlc_exists_in_prepost_cmd = dvdread_getbits(&state, 1);
+ pt->jlc_exists_in_button_cmd = dvdread_getbits(&state, 1);
+ pt->jlc_exists_in_tt_dom = dvdread_getbits(&state, 1);
+ pt->chapter_search_or_play = dvdread_getbits(&state, 1);
+ pt->title_or_time_play = dvdread_getbits(&state, 1);
+}
+
+static void free_ptl_mait(ptl_mait_t* ptl_mait, int num_entries) {
+ int i;
+ for (i = 0; i < num_entries; i++)
+ free(ptl_mait->countries[i].pf_ptl_mai);
+
+ free(ptl_mait->countries);
+ free(ptl_mait);
+}
+
+static ifo_handle_t *ifoOpenFileOrBackup(dvd_reader_t *ctx, int title,
+ int backup) {
+ struct ifo_handle_private_s *ifop;
+ dvd_read_domain_t domain = backup ? DVD_READ_INFO_BACKUP_FILE
+ : DVD_READ_INFO_FILE;
+ char ifo_filename[13];
+
+ ifop = calloc(1, sizeof(*ifop));
+ if(!ifop)
+ return NULL;
+
+ ifop->ctx = ctx;
+ ifop->file = DVDOpenFile(ctx, title, domain);
+ if(!ifop->file)
+ {
+ free(ifop);
+ return NULL;
+ }
+
+ if (title)
+ snprintf(ifo_filename, 13, "VTS_%02d_0.%s", title, backup ? "BUP" : "IFO");
+ else
+ snprintf(ifo_filename, 13, "VIDEO_TS.%s", backup ? "BUP" : "IFO");
+
+ if(!ifop->file) {
+ Log1(ctx, "Can't open file %s.", ifo_filename);
+ free(ifop);
+ return NULL;
+ }
+
+ ifo_handle_t *ifofile = &ifop->handle;
+ /* First check if this is a VMGI file. */
+ if(ifoRead_VMG(ifofile)) {
+
+ /* These are both mandatory. */
+ if(!ifoRead_FP_PGC(ifofile) || !ifoRead_TT_SRPT(ifofile))
+ goto ifoOpen_fail;
+
+ ifoRead_PGCI_UT(ifofile);
+ ifoRead_PTL_MAIT(ifofile);
+
+ /* This is also mandatory. */
+ if(!ifoRead_VTS_ATRT(ifofile))
+ goto ifoOpen_fail;
+
+ ifoRead_TXTDT_MGI(ifofile);
+ ifoRead_C_ADT(ifofile);
+ ifoRead_VOBU_ADMAP(ifofile);
+
+ return ifofile;
+ }
+
+ if(ifoRead_VTS(ifofile)) {
+
+ if(!ifoRead_VTS_PTT_SRPT(ifofile) || !ifoRead_PGCIT(ifofile))
+ goto ifoOpen_fail;
+
+ ifoRead_PGCI_UT(ifofile);
+ ifoRead_VTS_TMAPT(ifofile);
+ ifoRead_C_ADT(ifofile);
+ ifoRead_VOBU_ADMAP(ifofile);
+
+ if(!ifoRead_TITLE_C_ADT(ifofile) || !ifoRead_TITLE_VOBU_ADMAP(ifofile))
+ goto ifoOpen_fail;
+
+ return ifofile;
+ }
+
+ifoOpen_fail:
+ Log1(ctx, "Invalid IFO for title %d (%s).", title, ifo_filename);
+ ifoClose(ifofile);
+ return NULL;
+}
+
+static void ifoSetBupFlag(dvd_reader_t *ctx, int title)
+{
+ if(title > 63)
+ ctx->ifoBUPflags[0] |= 1 << (title - 64);
+ else
+ ctx->ifoBUPflags[1] |= 1 << title;
+}
+
+static int ifoGetBupFlag(const dvd_reader_t *ctx, int title)
+{
+ int bupflag;
+ if(title > 63)
+ bupflag = !! (ctx->ifoBUPflags[0] & (1 << (title - 64)));
+ else
+ bupflag = !! (ctx->ifoBUPflags[1] & (1 << title));
+ return bupflag;
+}
+
+ifo_handle_t *ifoOpen(dvd_reader_t *ctx, int title) {
+ ifo_handle_t *ifofile;
+ int bupflag = ifoGetBupFlag(ctx, title);
+
+ ifofile = ifoOpenFileOrBackup(ctx, title, bupflag);
+ if(!ifofile) /* Try backup */
+ {
+ ifofile = ifoOpenFileOrBackup(ctx, title, 1);
+ if(ifofile && !bupflag)
+ ifoSetBupFlag(ctx, title);
+ }
+ return ifofile;
+}
+
+ifo_handle_t *ifoOpenVMGI(dvd_reader_t *ctx) {
+ struct ifo_handle_private_s *ifop;
+
+ for(int backup = ifoGetBupFlag(ctx, 0); backup <= 1; backup++)
+ {
+ ifop = calloc(1, sizeof(*ifop));
+ if(!ifop)
+ return NULL;
+
+ const dvd_read_domain_t domain = backup ? DVD_READ_INFO_BACKUP_FILE
+ : DVD_READ_INFO_FILE;
+ const char *ext = backup ? "BUP" : "IFO";
+
+ ifop->ctx = ctx;
+ ifop->file = DVDOpenFile(ctx, 0, domain);
+ if(!ifop->file) { /* Should really catch any error */
+ Log1(ctx, "Can't open file VIDEO_TS.%s.", ext);
+ free(ifop);
+ return NULL;
+ }
+
+ if(ifoRead_VMG(&ifop->handle))
+ return &ifop->handle;
+
+ Log1(ctx, "ifoOpenVMGI(): Invalid main menu IFO (VIDEO_TS.%s).", ext);
+ ifoClose(&ifop->handle);
+ }
+ return NULL;
+}
+
+
+ifo_handle_t *ifoOpenVTSI(dvd_reader_t *ctx, int title) {
+ struct ifo_handle_private_s *ifop;
+
+ if(title <= 0 || title > 99) {
+ Log1(ctx, "ifoOpenVTSI invalid title (%d).", title);
+ return NULL;
+ }
+
+ for(int backup = ifoGetBupFlag(ctx, title); backup <= 1; backup++)
+ {
+ ifop = calloc(1, sizeof(*ifop));
+ if(!ifop)
+ return NULL;
+
+ const dvd_read_domain_t domain = backup ? DVD_READ_INFO_BACKUP_FILE
+ : DVD_READ_INFO_FILE;
+ const char *ext = backup ? "BUP" : "IFO";
+ ifop->ctx = ctx;
+ ifop->file = DVDOpenFile(ctx, title, domain);
+ /* Should really catch any error */
+ if(!ifop->file) {
+ Log1(ctx, "Can't open file VTS_%02d_0.%s.", title, ext);
+ free(ifop);
+ continue;
+ }
+
+ if(ifoRead_VTS(&ifop->handle) && ifop->handle.vtsi_mat)
+ return &ifop->handle;
+
+ Log1(ctx, "Invalid IFO for title %d (VTS_%02d_0.%s).",
+ title, title, ext);
+ ifoClose(&ifop->handle);
+ }
+
+ return NULL;
+}
+
+
+void ifoClose(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_VOBU_ADMAP(ifofile);
+ ifoFree_TITLE_VOBU_ADMAP(ifofile);
+ ifoFree_C_ADT(ifofile);
+ ifoFree_TITLE_C_ADT(ifofile);
+ ifoFree_TXTDT_MGI(ifofile);
+ ifoFree_VTS_ATRT(ifofile);
+ ifoFree_PTL_MAIT(ifofile);
+ ifoFree_PGCI_UT(ifofile);
+ ifoFree_TT_SRPT(ifofile);
+ ifoFree_FP_PGC(ifofile);
+ ifoFree_PGCIT(ifofile);
+ ifoFree_VTS_PTT_SRPT(ifofile);
+ ifoFree_VTS_TMAPT(ifofile);
+
+ if(ifofile->vmgi_mat)
+ free(ifofile->vmgi_mat);
+
+ if(ifofile->vtsi_mat)
+ free(ifofile->vtsi_mat);
+
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ DVDCloseFile(ifop->file);
+ free(ifop);
+}
+
+
+static int ifoRead_VMG(ifo_handle_t *ifofile) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ vmgi_mat_t *vmgi_mat;
+
+ vmgi_mat = calloc(1, sizeof(vmgi_mat_t));
+ if(!vmgi_mat)
+ return 0;
+
+ ifofile->vmgi_mat = vmgi_mat;
+
+ if(!DVDFileSeek_(ifop->file, 0)) {
+ free(ifofile->vmgi_mat);
+ ifofile->vmgi_mat = NULL;
+ return 0;
+ }
+
+ if(!DVDReadBytes(ifop->file, vmgi_mat, sizeof(vmgi_mat_t))) {
+ free(ifofile->vmgi_mat);
+ ifofile->vmgi_mat = NULL;
+ return 0;
+ }
+
+ if(strncmp("DVDVIDEO-VMG", vmgi_mat->vmg_identifier, 12) != 0) {
+ free(ifofile->vmgi_mat);
+ ifofile->vmgi_mat = NULL;
+ return 0;
+ }
+
+ B2N_32(vmgi_mat->vmg_last_sector);
+ B2N_32(vmgi_mat->vmgi_last_sector);
+ B2N_32(vmgi_mat->vmg_category);
+ B2N_16(vmgi_mat->vmg_nr_of_volumes);
+ B2N_16(vmgi_mat->vmg_this_volume_nr);
+ B2N_16(vmgi_mat->vmg_nr_of_title_sets);
+ B2N_64(vmgi_mat->vmg_pos_code);
+ B2N_32(vmgi_mat->vmgi_last_byte);
+ B2N_32(vmgi_mat->first_play_pgc);
+ B2N_32(vmgi_mat->vmgm_vobs);
+ B2N_32(vmgi_mat->tt_srpt);
+ B2N_32(vmgi_mat->vmgm_pgci_ut);
+ B2N_32(vmgi_mat->ptl_mait);
+ B2N_32(vmgi_mat->vts_atrt);
+ B2N_32(vmgi_mat->txtdt_mgi);
+ B2N_32(vmgi_mat->vmgm_c_adt);
+ B2N_32(vmgi_mat->vmgm_vobu_admap);
+ read_video_attr(&vmgi_mat->vmgm_video_attr);
+ read_audio_attr(&vmgi_mat->vmgm_audio_attr);
+ read_subp_attr(&vmgi_mat->vmgm_subp_attr);
+
+
+ CHECK_ZERO(vmgi_mat->zero_1);
+ CHECK_ZERO(vmgi_mat->zero_2);
+ /* DVDs created by VDR-to-DVD device LG RC590M violate the following check with
+ * vmgi_mat->zero_3 = 0x00000000010000000000000000000000000000. */
+ CHECK_ZERO(vmgi_mat->zero_3);
+ CHECK_ZERO(vmgi_mat->zero_4);
+ CHECK_ZERO(vmgi_mat->zero_5);
+ CHECK_ZERO(vmgi_mat->zero_6);
+ CHECK_ZERO(vmgi_mat->zero_7);
+ CHECK_ZERO(vmgi_mat->zero_8);
+ CHECK_ZERO(vmgi_mat->zero_9);
+ CHECK_ZERO(vmgi_mat->zero_10);
+ CHECK_VALUE(vmgi_mat->vmg_last_sector != 0);
+ CHECK_VALUE(vmgi_mat->vmgi_last_sector != 0);
+ CHECK_VALUE(vmgi_mat->vmgi_last_sector * 2 <= vmgi_mat->vmg_last_sector);
+ CHECK_VALUE(vmgi_mat->vmgi_last_sector * 2 <= vmgi_mat->vmg_last_sector);
+ CHECK_VALUE(vmgi_mat->vmg_nr_of_volumes != 0);
+ CHECK_VALUE(vmgi_mat->vmg_this_volume_nr != 0);
+ CHECK_VALUE(vmgi_mat->vmg_this_volume_nr <= vmgi_mat->vmg_nr_of_volumes);
+ CHECK_VALUE(vmgi_mat->disc_side == 1 || vmgi_mat->disc_side == 2);
+ CHECK_VALUE(vmgi_mat->vmg_nr_of_title_sets != 0);
+ CHECK_VALUE(vmgi_mat->vmgi_last_byte >= 341);
+ CHECK_VALUE(vmgi_mat->vmgi_last_byte / DVD_BLOCK_LEN <=
+ vmgi_mat->vmgi_last_sector);
+ /* It seems that first_play_pgc is optional. */
+ CHECK_VALUE(vmgi_mat->first_play_pgc < vmgi_mat->vmgi_last_byte);
+ CHECK_VALUE(vmgi_mat->vmgm_vobs == 0 ||
+ (vmgi_mat->vmgm_vobs > vmgi_mat->vmgi_last_sector &&
+ vmgi_mat->vmgm_vobs < vmgi_mat->vmg_last_sector));
+ CHECK_VALUE(vmgi_mat->tt_srpt <= vmgi_mat->vmgi_last_sector);
+ CHECK_VALUE(vmgi_mat->vmgm_pgci_ut <= vmgi_mat->vmgi_last_sector);
+ CHECK_VALUE(vmgi_mat->ptl_mait <= vmgi_mat->vmgi_last_sector);
+ CHECK_VALUE(vmgi_mat->vts_atrt <= vmgi_mat->vmgi_last_sector);
+ CHECK_VALUE(vmgi_mat->txtdt_mgi <= vmgi_mat->vmgi_last_sector);
+ CHECK_VALUE(vmgi_mat->vmgm_c_adt <= vmgi_mat->vmgi_last_sector);
+ CHECK_VALUE(vmgi_mat->vmgm_vobu_admap <= vmgi_mat->vmgi_last_sector);
+
+ CHECK_VALUE(vmgi_mat->nr_of_vmgm_audio_streams <= 1);
+ CHECK_VALUE(vmgi_mat->nr_of_vmgm_subp_streams <= 1);
+
+ return 1;
+}
+
+
+static int ifoRead_VTS(ifo_handle_t *ifofile) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ vtsi_mat_t *vtsi_mat;
+ int i;
+
+ vtsi_mat = calloc(1, sizeof(vtsi_mat_t));
+ if(!vtsi_mat)
+ return 0;
+
+ ifofile->vtsi_mat = vtsi_mat;
+
+ if(!DVDFileSeek_(ifop->file, 0)) {
+ free(ifofile->vtsi_mat);
+ ifofile->vtsi_mat = NULL;
+ return 0;
+ }
+
+ if(!(DVDReadBytes(ifop->file, vtsi_mat, sizeof(vtsi_mat_t)))) {
+ free(ifofile->vtsi_mat);
+ ifofile->vtsi_mat = NULL;
+ return 0;
+ }
+
+ if(strncmp("DVDVIDEO-VTS", vtsi_mat->vts_identifier, 12) != 0) {
+ free(ifofile->vtsi_mat);
+ ifofile->vtsi_mat = NULL;
+ return 0;
+ }
+
+ read_video_attr(&vtsi_mat->vtsm_video_attr);
+ read_video_attr(&vtsi_mat->vts_video_attr);
+ read_audio_attr(&vtsi_mat->vtsm_audio_attr);
+ for(i=0; i<8; i++)
+ read_audio_attr(&vtsi_mat->vts_audio_attr[i]);
+ read_subp_attr(&vtsi_mat->vtsm_subp_attr);
+ for(i=0; i<32; i++)
+ read_subp_attr(&vtsi_mat->vts_subp_attr[i]);
+ B2N_32(vtsi_mat->vts_last_sector);
+ B2N_32(vtsi_mat->vtsi_last_sector);
+ B2N_32(vtsi_mat->vts_category);
+ B2N_32(vtsi_mat->vtsi_last_byte);
+ B2N_32(vtsi_mat->vtsm_vobs);
+ B2N_32(vtsi_mat->vtstt_vobs);
+ B2N_32(vtsi_mat->vts_ptt_srpt);
+ B2N_32(vtsi_mat->vts_pgcit);
+ B2N_32(vtsi_mat->vtsm_pgci_ut);
+ B2N_32(vtsi_mat->vts_tmapt);
+ B2N_32(vtsi_mat->vtsm_c_adt);
+ B2N_32(vtsi_mat->vtsm_vobu_admap);
+ B2N_32(vtsi_mat->vts_c_adt);
+ B2N_32(vtsi_mat->vts_vobu_admap);
+
+
+ CHECK_ZERO(vtsi_mat->zero_1);
+ CHECK_ZERO(vtsi_mat->zero_2);
+ CHECK_ZERO(vtsi_mat->zero_3);
+ CHECK_ZERO(vtsi_mat->zero_4);
+ CHECK_ZERO(vtsi_mat->zero_5);
+ CHECK_ZERO(vtsi_mat->zero_6);
+ CHECK_ZERO(vtsi_mat->zero_7);
+ CHECK_ZERO(vtsi_mat->zero_8);
+ CHECK_ZERO(vtsi_mat->zero_9);
+ CHECK_ZERO(vtsi_mat->zero_10);
+ CHECK_ZERO(vtsi_mat->zero_11);
+ CHECK_ZERO(vtsi_mat->zero_12);
+ CHECK_ZERO(vtsi_mat->zero_13);
+ CHECK_ZERO(vtsi_mat->zero_14);
+ CHECK_ZERO(vtsi_mat->zero_15);
+ CHECK_ZERO(vtsi_mat->zero_16);
+ CHECK_ZERO(vtsi_mat->zero_17);
+ CHECK_ZERO(vtsi_mat->zero_18);
+ CHECK_ZERO(vtsi_mat->zero_19);
+ CHECK_ZERO(vtsi_mat->zero_20);
+ CHECK_ZERO(vtsi_mat->zero_21);
+ CHECK_VALUE(vtsi_mat->vtsi_last_sector*2 <= vtsi_mat->vts_last_sector);
+ CHECK_VALUE(vtsi_mat->vtsi_last_byte/DVD_BLOCK_LEN <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vtsm_vobs == 0 ||
+ (vtsi_mat->vtsm_vobs > vtsi_mat->vtsi_last_sector &&
+ vtsi_mat->vtsm_vobs < vtsi_mat->vts_last_sector));
+ CHECK_VALUE(vtsi_mat->vtstt_vobs == 0 ||
+ (vtsi_mat->vtstt_vobs > vtsi_mat->vtsi_last_sector &&
+ vtsi_mat->vtstt_vobs < vtsi_mat->vts_last_sector));
+ CHECK_VALUE(vtsi_mat->vts_ptt_srpt <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vts_pgcit <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vtsm_pgci_ut <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vts_tmapt <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vtsm_c_adt <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vtsm_vobu_admap <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vts_c_adt <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vts_vobu_admap <= vtsi_mat->vtsi_last_sector);
+
+ CHECK_VALUE(vtsi_mat->nr_of_vtsm_audio_streams <= 1);
+ CHECK_VALUE(vtsi_mat->nr_of_vtsm_subp_streams <= 1);
+
+ CHECK_VALUE(vtsi_mat->nr_of_vts_audio_streams <= 8);
+ for(i = vtsi_mat->nr_of_vts_audio_streams; i < 8; i++)
+ CHECK_ZERO(vtsi_mat->vts_audio_attr[i]);
+
+ CHECK_VALUE(vtsi_mat->nr_of_vts_subp_streams <= 32);
+ for(i = vtsi_mat->nr_of_vts_subp_streams; i < 32; i++)
+ CHECK_ZERO(vtsi_mat->vts_subp_attr[i]);
+
+ for(i = 0; i < 8; i++) {
+ read_multichannel_ext(&vtsi_mat->vts_mu_audio_attr[i]);
+ CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero1);
+ CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero2);
+ CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero3);
+ CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero4);
+ CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero5);
+ CHECK_ZERO(vtsi_mat->vts_mu_audio_attr[i].zero6);
+ }
+
+ return 1;
+}
+
+
+static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile,
+ pgc_command_tbl_t *cmd_tbl,
+ unsigned int offset) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ if(!DVDFileSeek_(ifop->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifop->file, cmd_tbl, PGC_COMMAND_TBL_SIZE)))
+ return 0;
+
+ B2N_16(cmd_tbl->nr_of_pre);
+ B2N_16(cmd_tbl->nr_of_post);
+ B2N_16(cmd_tbl->nr_of_cell);
+ B2N_16(cmd_tbl->last_byte);
+
+ CHECK_VALUE(cmd_tbl->nr_of_pre + cmd_tbl->nr_of_post + cmd_tbl->nr_of_cell<= 255);
+ CHECK_VALUE((cmd_tbl->nr_of_pre + cmd_tbl->nr_of_post + cmd_tbl->nr_of_cell) * COMMAND_DATA_SIZE
+ + PGC_COMMAND_TBL_SIZE <= cmd_tbl->last_byte + 1);
+
+ if(cmd_tbl->nr_of_pre != 0) {
+ unsigned int pre_cmds_size = cmd_tbl->nr_of_pre * COMMAND_DATA_SIZE;
+ cmd_tbl->pre_cmds = malloc(pre_cmds_size);
+ if(!cmd_tbl->pre_cmds)
+ return 0;
+
+ if(!(DVDReadBytes(ifop->file, cmd_tbl->pre_cmds, pre_cmds_size))) {
+ free(cmd_tbl->pre_cmds);
+ return 0;
+ }
+ }
+
+ if(cmd_tbl->nr_of_post != 0) {
+ unsigned int post_cmds_size = cmd_tbl->nr_of_post * COMMAND_DATA_SIZE;
+ cmd_tbl->post_cmds = malloc(post_cmds_size);
+ if(!cmd_tbl->post_cmds) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ return 0;
+ }
+ if(!(DVDReadBytes(ifop->file, cmd_tbl->post_cmds, post_cmds_size))) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ free(cmd_tbl->post_cmds);
+ return 0;
+ }
+ }
+
+ if(cmd_tbl->nr_of_cell != 0) {
+ unsigned int cell_cmds_size = cmd_tbl->nr_of_cell * COMMAND_DATA_SIZE;
+ cmd_tbl->cell_cmds = malloc(cell_cmds_size);
+ if(!cmd_tbl->cell_cmds) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ if(cmd_tbl->post_cmds)
+ free(cmd_tbl->post_cmds);
+ return 0;
+ }
+ if(!(DVDReadBytes(ifop->file, cmd_tbl->cell_cmds, cell_cmds_size))) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ if(cmd_tbl->post_cmds)
+ free(cmd_tbl->post_cmds);
+ free(cmd_tbl->cell_cmds);
+ return 0;
+ }
+ }
+
+ /*
+ * Make a run over all the commands and see that we can interpret them all?
+ */
+ return 1;
+}
+
+
+static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
+ if(cmd_tbl) {
+ if(cmd_tbl->nr_of_pre && cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ if(cmd_tbl->nr_of_post && cmd_tbl->post_cmds)
+ free(cmd_tbl->post_cmds);
+ if(cmd_tbl->nr_of_cell && cmd_tbl->cell_cmds)
+ free(cmd_tbl->cell_cmds);
+ free(cmd_tbl);
+ }
+}
+
+static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile,
+ pgc_program_map_t *program_map,
+ unsigned int nr, unsigned int offset) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ unsigned int size = nr * sizeof(pgc_program_map_t);
+
+ if(!DVDFileSeek_(ifop->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifop->file, program_map, size)))
+ return 0;
+
+ return 1;
+}
+
+static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile,
+ cell_playback_t *cell_playback,
+ unsigned int nr, unsigned int offset) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ unsigned int i;
+ unsigned int size = nr * sizeof(cell_playback_t);
+
+ if(!DVDFileSeek_(ifop->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifop->file, cell_playback, size)))
+ return 0;
+
+ for(i = 0; i < nr; i++) {
+ read_cell_playback(&cell_playback[i]);
+ /* Changed < to <= because this was false in the movie 'Pi'. */
+ CHECK_VALUE(cell_playback[i].last_vobu_start_sector <=
+ cell_playback[i].last_sector);
+ CHECK_VALUE(cell_playback[i].first_sector <=
+ cell_playback[i].last_vobu_start_sector);
+ }
+
+ return 1;
+}
+
+
+static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile,
+ cell_position_t *cell_position,
+ unsigned int nr, unsigned int offset) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ unsigned int i;
+ unsigned int size = nr * sizeof(cell_position_t);
+
+ if(!DVDFileSeek_(ifop->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifop->file, cell_position, size)))
+ return 0;
+
+ for(i = 0; i < nr; i++) {
+ B2N_16(cell_position[i].vob_id_nr);
+ CHECK_ZERO(cell_position[i].zero_1);
+ }
+
+ return 1;
+}
+
+static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ unsigned int i;
+
+ if(!DVDFileSeek_(ifop->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifop->file, pgc, PGC_SIZE)))
+ return 0;
+
+ read_user_ops(&pgc->prohibited_ops);
+ B2N_16(pgc->next_pgc_nr);
+ B2N_16(pgc->prev_pgc_nr);
+ B2N_16(pgc->goup_pgc_nr);
+ B2N_16(pgc->command_tbl_offset);
+ B2N_16(pgc->program_map_offset);
+ B2N_16(pgc->cell_playback_offset);
+ B2N_16(pgc->cell_position_offset);
+
+ for(i = 0; i < 8; i++)
+ B2N_16(pgc->audio_control[i]);
+ for(i = 0; i < 32; i++)
+ B2N_32(pgc->subp_control[i]);
+ for(i = 0; i < 16; i++)
+ B2N_32(pgc->palette[i]);
+
+ CHECK_ZERO(pgc->zero_1);
+ CHECK_VALUE(pgc->nr_of_programs <= pgc->nr_of_cells);
+
+ /* verify time (look at print_time) */
+ for(i = 0; i < 8; i++)
+ if(!(pgc->audio_control[i] & 0x8000)) /* The 'is present' bit */
+ CHECK_ZERO(pgc->audio_control[i]);
+ for(i = 0; i < 32; i++)
+ if(!(pgc->subp_control[i] & 0x80000000)) /* The 'is present' bit */
+ CHECK_ZERO(pgc->subp_control[i]);
+
+ /* Check that time is 0:0:0:0 also if nr_of_programs == 0 */
+ if(pgc->nr_of_programs == 0) {
+ CHECK_ZERO(pgc->still_time);
+ CHECK_ZERO(pgc->pg_playback_mode); /* ?? */
+ CHECK_VALUE(pgc->program_map_offset == 0);
+ CHECK_VALUE(pgc->cell_playback_offset == 0);
+ CHECK_VALUE(pgc->cell_position_offset == 0);
+ } else {
+ CHECK_VALUE(pgc->program_map_offset != 0);
+ CHECK_VALUE(pgc->cell_playback_offset != 0);
+ CHECK_VALUE(pgc->cell_position_offset != 0);
+ }
+
+ if(pgc->command_tbl_offset != 0) {
+ pgc->command_tbl = calloc(1, sizeof(pgc_command_tbl_t));
+ if(!pgc->command_tbl)
+ return 0;
+
+ if(!ifoRead_PGC_COMMAND_TBL(ifofile, pgc->command_tbl,
+ offset + pgc->command_tbl_offset)) {
+ return 0;
+ }
+ } else {
+ pgc->command_tbl = NULL;
+ }
+
+ if(pgc->program_map_offset != 0 && pgc->nr_of_programs>0) {
+ pgc->program_map = calloc(pgc->nr_of_programs, sizeof(pgc_program_map_t));
+ if(!pgc->program_map) {
+ return 0;
+ }
+ if(!ifoRead_PGC_PROGRAM_MAP(ifofile, pgc->program_map,pgc->nr_of_programs,
+ offset + pgc->program_map_offset)) {
+ return 0;
+ }
+ } else {
+ pgc->program_map = NULL;
+ }
+
+ if(pgc->cell_playback_offset != 0 && pgc->nr_of_cells>0) {
+ pgc->cell_playback = calloc(pgc->nr_of_cells, sizeof(cell_playback_t));
+ if(!pgc->cell_playback) {
+ return 0;
+ }
+ if(!ifoRead_CELL_PLAYBACK_TBL(ifofile, pgc->cell_playback,
+ pgc->nr_of_cells,
+ offset + pgc->cell_playback_offset)) {
+ return 0;
+ }
+ } else {
+ pgc->cell_playback = NULL;
+ }
+
+ if(pgc->cell_position_offset != 0 && pgc->nr_of_cells>0) {
+ pgc->cell_position = calloc(pgc->nr_of_cells, sizeof(cell_position_t));
+ if(!pgc->cell_position) {
+ return 0;
+ }
+ if(!ifoRead_CELL_POSITION_TBL(ifofile, pgc->cell_position,
+ pgc->nr_of_cells,
+ offset + pgc->cell_position_offset)) {
+ return 0;
+ }
+ } else {
+ pgc->cell_position = NULL;
+ }
+
+ return 1;
+}
+
+int ifoRead_FP_PGC(ifo_handle_t *ifofile) {
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ /* It seems that first_play_pgc is optional after all. */
+ ifofile->first_play_pgc = NULL;
+ if(!ifofile->vmgi_mat->first_play_pgc)
+ return 1;
+
+ ifofile->first_play_pgc = calloc(1, sizeof(pgc_t));
+ if(!ifofile->first_play_pgc)
+ return 0;
+
+ ifofile->first_play_pgc->ref_count = 1;
+ if(!ifoRead_PGC(ifofile, ifofile->first_play_pgc,
+ ifofile->vmgi_mat->first_play_pgc)) {
+ ifoFree_PGC(&ifofile->first_play_pgc);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void ifoFree_PGC(pgc_t **pgc) {
+ if(pgc && *pgc && (--(*pgc)->ref_count) <= 0) {
+ ifoFree_PGC_COMMAND_TBL((*pgc)->command_tbl);
+ if((*pgc)->program_map)
+ free((*pgc)->program_map);
+ if((*pgc)->cell_playback)
+ free((*pgc)->cell_playback);
+ if((*pgc)->cell_position)
+ free((*pgc)->cell_position);
+ free(*pgc);
+ }
+ if (pgc) {
+ *pgc = NULL;
+ }
+}
+
+void ifoFree_FP_PGC(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->first_play_pgc) {
+ ifoFree_PGC(&ifofile->first_play_pgc);
+ }
+}
+
+
+int ifoRead_TT_SRPT(ifo_handle_t *ifofile) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ tt_srpt_t *tt_srpt;
+ unsigned int i;
+ size_t info_length;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ if(ifofile->vmgi_mat->tt_srpt == 0) /* mandatory */
+ return 0;
+
+ if(!DVDFileSeek_(ifop->file, ifofile->vmgi_mat->tt_srpt * DVD_BLOCK_LEN))
+ return 0;
+
+ tt_srpt = calloc(1, sizeof(tt_srpt_t));
+ if(!tt_srpt)
+ return 0;
+
+ ifofile->tt_srpt = tt_srpt;
+
+ if(!(DVDReadBytes(ifop->file, tt_srpt, TT_SRPT_SIZE))) {
+ Log0(ifop->ctx, "Unable to read read TT_SRPT.");
+ free(tt_srpt);
+ return 0;
+ }
+
+ B2N_16(tt_srpt->nr_of_srpts);
+ B2N_32(tt_srpt->last_byte);
+
+ /* E-One releases don't fill this field */
+ if(tt_srpt->last_byte == 0) {
+ tt_srpt->last_byte = tt_srpt->nr_of_srpts * sizeof(title_info_t) - 1 + TT_SRPT_SIZE;
+ }
+ info_length = tt_srpt->last_byte + 1 - TT_SRPT_SIZE;
+
+ tt_srpt->title = calloc(1, info_length);
+ if(!tt_srpt->title) {
+ free(tt_srpt);
+ ifofile->tt_srpt = NULL;
+ return 0;
+ }
+ if(!(DVDReadBytes(ifop->file, tt_srpt->title, info_length))) {
+ Log0(ifop->ctx, "libdvdread: Unable to read read TT_SRPT.");
+ ifoFree_TT_SRPT(ifofile);
+ return 0;
+ }
+
+ if(tt_srpt->nr_of_srpts>info_length/sizeof(title_info_t)){
+ Log1(ifop->ctx, "data mismatch: info_length (%zd)!= nr_of_srpts (%d). Truncating.",
+ info_length/sizeof(title_info_t),tt_srpt->nr_of_srpts);
+ tt_srpt->nr_of_srpts=info_length/sizeof(title_info_t);
+ }
+
+ for(i = 0; i < tt_srpt->nr_of_srpts; i++) {
+ B2N_16(tt_srpt->title[i].nr_of_ptts);
+ B2N_16(tt_srpt->title[i].parental_id);
+ B2N_32(tt_srpt->title[i].title_set_sector);
+ }
+
+
+ CHECK_ZERO(tt_srpt->zero_1);
+ CHECK_VALUE(tt_srpt->nr_of_srpts != 0);
+ CHECK_VALUE(tt_srpt->nr_of_srpts < 100); /* ?? */
+ CHECK_VALUE(tt_srpt->nr_of_srpts * sizeof(title_info_t) <= info_length);
+
+ for(i = 0; i < tt_srpt->nr_of_srpts; i++) {
+ read_playback_type(&tt_srpt->title[i].pb_ty);
+ CHECK_VALUE(tt_srpt->title[i].pb_ty.zero_1 == 0);
+ CHECK_VALUE(tt_srpt->title[i].nr_of_angles != 0);
+ CHECK_VALUE(tt_srpt->title[i].nr_of_angles < 10);
+ /* CHECK_VALUE(tt_srpt->title[i].nr_of_ptts != 0); */
+ /* XXX: this assertion breaks Ghostbusters: */
+ CHECK_VALUE(tt_srpt->title[i].nr_of_ptts < 1000); /* ?? */
+ CHECK_VALUE(tt_srpt->title[i].title_set_nr != 0);
+ CHECK_VALUE(tt_srpt->title[i].title_set_nr < 100); /* ?? */
+ CHECK_VALUE(tt_srpt->title[i].vts_ttn != 0);
+ CHECK_VALUE(tt_srpt->title[i].vts_ttn < 100); /* ?? */
+ /* CHECK_VALUE(tt_srpt->title[i].title_set_sector != 0); */
+ }
+
+ /* Make this a function */
+#if 0
+ if(memcmp((uint8_t *)tt_srpt->title +
+ tt_srpt->nr_of_srpts * sizeof(title_info_t),
+ my_friendly_zeros,
+ info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t))) {
+ Log1(ifop->ctx, "VMG_PTT_SRPT slack is != 0, ");
+ hexdump((uint8_t *)tt_srpt->title +
+ tt_srpt->nr_of_srpts * sizeof(title_info_t),
+ info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t));
+ }
+#endif
+
+ return 1;
+}
+
+
+void ifoFree_TT_SRPT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->tt_srpt) {
+ free(ifofile->tt_srpt->title);
+ ifofile->tt_srpt->title = NULL;
+ free(ifofile->tt_srpt);
+ ifofile->tt_srpt = NULL;
+ }
+}
+
+
+int ifoRead_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ vts_ptt_srpt_t *vts_ptt_srpt = NULL;
+ int info_length, i, j;
+ uint32_t *data = NULL;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_ptt_srpt == 0) /* mandatory */
+ return 0;
+
+ if(!DVDFileSeek_(ifop->file,
+ ifofile->vtsi_mat->vts_ptt_srpt * DVD_BLOCK_LEN))
+ return 0;
+
+ vts_ptt_srpt = calloc(1, sizeof(vts_ptt_srpt_t));
+ if(!vts_ptt_srpt)
+ return 0;
+
+ vts_ptt_srpt->title = NULL;
+ ifofile->vts_ptt_srpt = vts_ptt_srpt;
+
+ if(!(DVDReadBytes(ifop->file, vts_ptt_srpt, VTS_PTT_SRPT_SIZE))) {
+ Log0(ifop->ctx, "Unable to read PTT search table.");
+ goto fail;
+ }
+
+ B2N_16(vts_ptt_srpt->nr_of_srpts);
+ B2N_32(vts_ptt_srpt->last_byte);
+
+ CHECK_ZERO(vts_ptt_srpt->zero_1);
+ CHECK_VALUE(vts_ptt_srpt->nr_of_srpts != 0);
+ CHECK_VALUE(vts_ptt_srpt->nr_of_srpts < 100); /* ?? */
+
+ /* E-One releases don't fill this field */
+ if(vts_ptt_srpt->last_byte == 0) {
+ vts_ptt_srpt->last_byte = vts_ptt_srpt->nr_of_srpts * sizeof(*data) - 1 + VTS_PTT_SRPT_SIZE;
+ }
+ info_length = vts_ptt_srpt->last_byte + 1 - VTS_PTT_SRPT_SIZE;
+ data = calloc(1, info_length);
+ if(!data)
+ goto fail;
+
+ if(!(DVDReadBytes(ifop->file, data, info_length))) {
+ Log0(ifop->ctx, "Unable to read PTT search table.");
+ goto fail;
+ }
+
+ if(vts_ptt_srpt->nr_of_srpts > info_length / sizeof(*data)) {
+ Log0(ifop->ctx, "PTT search table too small.");
+ goto fail;
+ }
+
+ if(vts_ptt_srpt->nr_of_srpts == 0) {
+ Log0(ifop->ctx, "Zero entries in PTT search table.");
+ goto fail;
+ }
+
+ for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+ /* Transformers 3 has PTT start bytes that point outside the SRPT PTT */
+ uint32_t start = data[i];
+ B2N_32(start);
+ if(start + sizeof(ptt_info_t) > vts_ptt_srpt->last_byte + 1) {
+ /* don't mess with any bytes beyond the end of the allocation */
+ vts_ptt_srpt->nr_of_srpts = i;
+ break;
+ }
+ data[i] = start;
+ /* assert(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with 0 PTTs. They all have a data[i] offsets beyond the end of
+ of the vts_ptt_srpt structure. */
+ CHECK_VALUE(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1 + 4);
+ }
+
+ vts_ptt_srpt->ttu_offset = data;
+
+ vts_ptt_srpt->title = calloc(vts_ptt_srpt->nr_of_srpts, sizeof(ttu_t));
+ if(!vts_ptt_srpt->title)
+ goto fail;
+
+ for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+ int n;
+ if(i < vts_ptt_srpt->nr_of_srpts - 1)
+ n = (data[i+1] - data[i]);
+ else
+ n = (vts_ptt_srpt->last_byte + 1 - data[i]);
+
+ /* assert(n > 0 && (n % 4) == 0);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with 0 PTTs. */
+ if(n < 0) n = 0;
+
+ /* DVDs created by the VDR-to-DVD device LG RC590M violate the following requirement */
+ CHECK_VALUE(n % 4 == 0);
+
+ vts_ptt_srpt->title[i].nr_of_ptts = n / 4;
+ vts_ptt_srpt->title[i].ptt = calloc(n / 4, sizeof(ptt_info_t));
+ if(!vts_ptt_srpt->title[i].ptt) {
+ for(n = 0; n < i; n++)
+ free(vts_ptt_srpt->title[n].ptt);
+
+ goto fail;
+ }
+ for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+ /* The assert placed here because of Magic Knight Rayearth Daybreak */
+ CHECK_VALUE(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1);
+ vts_ptt_srpt->title[i].ptt[j].pgcn
+ = *(uint16_t*)(((char *)data) + data[i] + 4*j - VTS_PTT_SRPT_SIZE);
+ vts_ptt_srpt->title[i].ptt[j].pgn
+ = *(uint16_t*)(((char *)data) + data[i] + 4*j + 2 - VTS_PTT_SRPT_SIZE);
+ }
+ }
+
+ for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+ for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+ B2N_16(vts_ptt_srpt->title[i].ptt[j].pgcn);
+ B2N_16(vts_ptt_srpt->title[i].ptt[j].pgn);
+ }
+ }
+
+ for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+ CHECK_VALUE(vts_ptt_srpt->title[i].nr_of_ptts < 1000); /* ?? */
+ for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+ CHECK_VALUE(vts_ptt_srpt->title[i].ptt[j].pgcn != 0 );
+ CHECK_VALUE(vts_ptt_srpt->title[i].ptt[j].pgcn < 1000); /* ?? */
+ CHECK_VALUE(vts_ptt_srpt->title[i].ptt[j].pgn != 0);
+ CHECK_VALUE(vts_ptt_srpt->title[i].ptt[j].pgn < 100); /* ?? */
+ //don't abort here. E-One DVDs contain PTT with pgcn or pgn == 0
+ }
+ }
+
+ return 1;
+
+fail:
+ free(data);
+ ifofile->vts_ptt_srpt = NULL;
+ free(vts_ptt_srpt->title);
+ free(vts_ptt_srpt);
+ return 0;
+}
+
+
+void ifoFree_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->vts_ptt_srpt) {
+ int i;
+ for(i = 0; i < ifofile->vts_ptt_srpt->nr_of_srpts; i++)
+ free(ifofile->vts_ptt_srpt->title[i].ptt);
+ free(ifofile->vts_ptt_srpt->ttu_offset);
+ free(ifofile->vts_ptt_srpt->title);
+ free(ifofile->vts_ptt_srpt);
+ ifofile->vts_ptt_srpt = 0;
+ }
+}
+
+
+int ifoRead_PTL_MAIT(ifo_handle_t *ifofile) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ ptl_mait_t *ptl_mait;
+ int info_length;
+ unsigned int i, j;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ if(!ifofile->vmgi_mat->ptl_mait)
+ return 1;
+
+ if(!DVDFileSeek_(ifop->file, ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN))
+ return 0;
+
+ ptl_mait = calloc(1, sizeof(ptl_mait_t));
+ if(!ptl_mait)
+ return 0;
+
+ ifofile->ptl_mait = ptl_mait;
+
+ if(!(DVDReadBytes(ifop->file, ptl_mait, PTL_MAIT_SIZE))) {
+ free(ptl_mait);
+ ifofile->ptl_mait = NULL;
+ return 0;
+ }
+
+ B2N_16(ptl_mait->nr_of_countries);
+ B2N_16(ptl_mait->nr_of_vtss);
+ B2N_32(ptl_mait->last_byte);
+
+ CHECK_VALUE(ptl_mait->nr_of_countries != 0);
+ CHECK_VALUE(ptl_mait->nr_of_countries < 100); /* ?? */
+ CHECK_VALUE(ptl_mait->nr_of_vtss != 0);
+ CHECK_VALUE(ptl_mait->nr_of_vtss < 100); /* ?? */
+ CHECK_VALUE(ptl_mait->nr_of_countries * PTL_MAIT_COUNTRY_SIZE
+ <= ptl_mait->last_byte + 1 - PTL_MAIT_SIZE);
+
+ info_length = ptl_mait->nr_of_countries * sizeof(ptl_mait_country_t);
+ ptl_mait->countries = calloc(1, info_length);
+ if(!ptl_mait->countries) {
+ free(ptl_mait);
+ ifofile->ptl_mait = NULL;
+ return 0;
+ }
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+ ptl_mait->countries[i].pf_ptl_mai = NULL;
+ }
+
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+ if(!(DVDReadBytes(ifop->file, &ptl_mait->countries[i], PTL_MAIT_COUNTRY_SIZE))) {
+ Log0(ifop->ctx, "Unable to read PTL_MAIT.");
+ free(ptl_mait->countries);
+ free(ptl_mait);
+ ifofile->ptl_mait = NULL;
+ return 0;
+ }
+ }
+
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+ B2N_16(ptl_mait->countries[i].country_code);
+ B2N_16(ptl_mait->countries[i].pf_ptl_mai_start_byte);
+ }
+
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+ CHECK_ZERO(ptl_mait->countries[i].zero_1);
+ CHECK_ZERO(ptl_mait->countries[i].zero_2);
+ CHECK_VALUE(ptl_mait->countries[i].pf_ptl_mai_start_byte
+ + sizeof(pf_level_t) * (ptl_mait->nr_of_vtss + 1) <= ptl_mait->last_byte + 1);
+ }
+
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+ uint16_t *pf_temp;
+
+ if(!DVDFileSeek_(ifop->file,
+ ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN
+ + ptl_mait->countries[i].pf_ptl_mai_start_byte)) {
+ Log0(ifop->ctx, "Unable to seek PTL_MAIT table at index %d.",i);
+ free(ptl_mait->countries);
+ free(ptl_mait);
+ ifofile->ptl_mait = NULL;
+ return 0;
+ }
+ info_length = (ptl_mait->nr_of_vtss + 1) * sizeof(pf_level_t);
+ pf_temp = calloc(1, info_length);
+ if(!pf_temp) {
+ free_ptl_mait(ptl_mait, i);
+ ifofile->ptl_mait = NULL;
+ return 0;
+ }
+ if(!(DVDReadBytes(ifop->file, pf_temp, info_length))) {
+ Log0(ifop->ctx, "Unable to read PTL_MAIT table at index %d.",i);
+ free(pf_temp);
+ free_ptl_mait(ptl_mait, i);
+ ifofile->ptl_mait = NULL;
+ return 0;
+ }
+ for (j = 0; j < ((ptl_mait->nr_of_vtss + 1U) * 8U); j++) {
+ B2N_16(pf_temp[j]);
+ }
+ ptl_mait->countries[i].pf_ptl_mai = calloc(1, info_length);
+ if(!ptl_mait->countries[i].pf_ptl_mai) {
+ free(pf_temp);
+ free_ptl_mait(ptl_mait, i);
+ ifofile->ptl_mait = NULL;
+ return 0;
+ }
+ { /* Transpose the array so we can use C indexing. */
+ int level, vts;
+ for(level = 0; level < PTL_MAIT_NUM_LEVEL; level++) {
+ for(vts = 0; vts <= ptl_mait->nr_of_vtss; vts++) {
+ ptl_mait->countries[i].pf_ptl_mai[vts][level] =
+ pf_temp[(7-level)*(ptl_mait->nr_of_vtss+1) + vts];
+ }
+ }
+ free(pf_temp);
+ }
+ }
+ return 1;
+}
+
+void ifoFree_PTL_MAIT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->ptl_mait) {
+ unsigned int i;
+
+ for(i = 0; i < ifofile->ptl_mait->nr_of_countries; i++) {
+ free(ifofile->ptl_mait->countries[i].pf_ptl_mai);
+ }
+ free(ifofile->ptl_mait->countries);
+ free(ifofile->ptl_mait);
+ ifofile->ptl_mait = NULL;
+ }
+}
+
+int ifoRead_VTS_TMAPT(ifo_handle_t *ifofile) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ vts_tmapt_t *vts_tmapt;
+ uint32_t *vts_tmap_srp;
+ unsigned int offset;
+ int info_length;
+ unsigned int i, j;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_tmapt == 0) {
+ ifofile->vts_tmapt = NULL;
+ return 1;
+ }
+
+ offset = ifofile->vtsi_mat->vts_tmapt * DVD_BLOCK_LEN;
+
+ if(!DVDFileSeek_(ifop->file, offset))
+ return 0;
+
+ vts_tmapt = calloc(1, sizeof(vts_tmapt_t));
+ if(!vts_tmapt)
+ return 0;
+
+ ifofile->vts_tmapt = vts_tmapt;
+
+ if(!(DVDReadBytes(ifop->file, vts_tmapt, VTS_TMAPT_SIZE))) {
+ Log0(ifop->ctx, "Unable to read VTS_TMAPT.");
+ free(vts_tmapt);
+ ifofile->vts_tmapt = NULL;
+ return 0;
+ }
+
+ B2N_16(vts_tmapt->nr_of_tmaps);
+ B2N_32(vts_tmapt->last_byte);
+
+ CHECK_ZERO(vts_tmapt->zero_1);
+
+ info_length = vts_tmapt->nr_of_tmaps * 4;
+
+ vts_tmap_srp = calloc(1, info_length);
+ if(!vts_tmap_srp) {
+ free(vts_tmapt);
+ ifofile->vts_tmapt = NULL;
+ return 0;
+ }
+
+ vts_tmapt->tmap_offset = vts_tmap_srp;
+
+ if(!(DVDReadBytes(ifop->file, vts_tmap_srp, info_length))) {
+ Log0(ifop->ctx, "Unable to read VTS_TMAPT.");
+ free(vts_tmap_srp);
+ free(vts_tmapt);
+ ifofile->vts_tmapt = NULL;
+ return 0;
+ }
+
+ for (i = 0; i < vts_tmapt->nr_of_tmaps; i++) {
+ B2N_32(vts_tmap_srp[i]);
+ }
+
+
+ info_length = vts_tmapt->nr_of_tmaps * sizeof(vts_tmap_t);
+
+ vts_tmapt->tmap = calloc(1, info_length);
+ if(!vts_tmapt->tmap) {
+ free(vts_tmap_srp);
+ free(vts_tmapt);
+ ifofile->vts_tmapt = NULL;
+ return 0;
+ }
+
+ for(i = 0; i < vts_tmapt->nr_of_tmaps; i++) {
+ if(!DVDFileSeek_(ifop->file, offset + vts_tmap_srp[i])) {
+ ifoFree_VTS_TMAPT(ifofile);
+ return 0;
+ }
+
+ if(!(DVDReadBytes(ifop->file, &vts_tmapt->tmap[i], VTS_TMAP_SIZE))) {
+ Log0(ifop->ctx, "Unable to read VTS_TMAP.");
+ ifoFree_VTS_TMAPT(ifofile);
+ return 0;
+ }
+
+ B2N_16(vts_tmapt->tmap[i].nr_of_entries);
+ CHECK_ZERO(vts_tmapt->tmap[i].zero_1);
+
+ if(vts_tmapt->tmap[i].nr_of_entries == 0) { /* Early out if zero entries */
+ vts_tmapt->tmap[i].map_ent = NULL;
+ continue;
+ }
+
+ info_length = vts_tmapt->tmap[i].nr_of_entries * sizeof(map_ent_t);
+
+ vts_tmapt->tmap[i].map_ent = calloc(1, info_length);
+ if(!vts_tmapt->tmap[i].map_ent) {
+ ifoFree_VTS_TMAPT(ifofile);
+ return 0;
+ }
+
+ if(!(DVDReadBytes(ifop->file, vts_tmapt->tmap[i].map_ent, info_length))) {
+ Log0(ifop->ctx, "Unable to read VTS_TMAP_ENT.");
+ ifoFree_VTS_TMAPT(ifofile);
+ return 0;
+ }
+
+ for(j = 0; j < vts_tmapt->tmap[i].nr_of_entries; j++)
+ B2N_32(vts_tmapt->tmap[i].map_ent[j]);
+ }
+
+ return 1;
+}
+
+void ifoFree_VTS_TMAPT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->vts_tmapt) {
+ unsigned int i;
+
+ for(i = 0; i < ifofile->vts_tmapt->nr_of_tmaps; i++)
+ if(ifofile->vts_tmapt->tmap[i].map_ent)
+ free(ifofile->vts_tmapt->tmap[i].map_ent);
+ free(ifofile->vts_tmapt->tmap);
+ free(ifofile->vts_tmapt->tmap_offset);
+ free(ifofile->vts_tmapt);
+ ifofile->vts_tmapt = NULL;
+ }
+}
+
+
+int ifoRead_TITLE_C_ADT(ifo_handle_t *ifofile) {
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_c_adt == 0) /* mandatory */
+ return 0;
+
+ ifofile->vts_c_adt = calloc(1, sizeof(c_adt_t));
+ if(!ifofile->vts_c_adt)
+ return 0;
+
+ if(!ifoRead_C_ADT_internal(ifofile, ifofile->vts_c_adt,
+ ifofile->vtsi_mat->vts_c_adt)) {
+ free(ifofile->vts_c_adt);
+ ifofile->vts_c_adt = NULL;
+ return 0;
+ }
+
+ return 1;
+}
+
+int ifoRead_C_ADT(ifo_handle_t *ifofile) {
+ unsigned int sector;
+
+ if(!ifofile)
+ return 0;
+
+ if(ifofile->vmgi_mat) {
+ if(ifofile->vmgi_mat->vmgm_c_adt == 0)
+ return 1;
+ sector = ifofile->vmgi_mat->vmgm_c_adt;
+ } else if(ifofile->vtsi_mat) {
+ if(ifofile->vtsi_mat->vtsm_c_adt == 0)
+ return 1;
+ sector = ifofile->vtsi_mat->vtsm_c_adt;
+ } else {
+ return 0;
+ }
+
+ ifofile->menu_c_adt = calloc(1, sizeof(c_adt_t));
+ if(!ifofile->menu_c_adt)
+ return 0;
+
+ if(!ifoRead_C_ADT_internal(ifofile, ifofile->menu_c_adt, sector)) {
+ free(ifofile->menu_c_adt);
+ ifofile->menu_c_adt = NULL;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile,
+ c_adt_t *c_adt, unsigned int sector) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ size_t i, info_length;
+
+ if(!DVDFileSeek_(ifop->file, sector * DVD_BLOCK_LEN))
+ return 0;
+
+ if(!(DVDReadBytes(ifop->file, c_adt, C_ADT_SIZE)))
+ return 0;
+
+ B2N_16(c_adt->nr_of_vobs);
+ B2N_32(c_adt->last_byte);
+
+ if(c_adt->last_byte + 1 < C_ADT_SIZE)
+ return 0;
+
+ info_length = c_adt->last_byte + 1 - C_ADT_SIZE;
+
+ CHECK_ZERO(c_adt->zero_1);
+ /* assert(c_adt->nr_of_vobs > 0);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with a VOBS that has no cells. */
+ CHECK_VALUE(info_length % sizeof(cell_adr_t) == 0);
+
+ /* assert(info_length / sizeof(cell_adr_t) >= c_adt->nr_of_vobs);
+ Enemy of the State region 2 (de) has Titles where nr_of_vobs field
+ is to high, they high ones are never referenced though. */
+ if(info_length / sizeof(cell_adr_t) < c_adt->nr_of_vobs) {
+ Log1(ifop->ctx, "C_ADT nr_of_vobs > available info entries");
+ c_adt->nr_of_vobs = info_length / sizeof(cell_adr_t);
+ }
+
+ c_adt->cell_adr_table = calloc(1, info_length);
+ if(!c_adt->cell_adr_table)
+ return 0;
+
+ if(info_length &&
+ !(DVDReadBytes(ifop->file, c_adt->cell_adr_table, info_length))) {
+ free(c_adt->cell_adr_table);
+ return 0;
+ }
+
+ for(i = 0; i < info_length/sizeof(cell_adr_t); i++) {
+ B2N_16(c_adt->cell_adr_table[i].vob_id);
+ B2N_32(c_adt->cell_adr_table[i].start_sector);
+ B2N_32(c_adt->cell_adr_table[i].last_sector);
+
+ CHECK_ZERO(c_adt->cell_adr_table[i].zero_1);
+ CHECK_VALUE(c_adt->cell_adr_table[i].vob_id > 0);
+ CHECK_VALUE(c_adt->cell_adr_table[i].vob_id <= c_adt->nr_of_vobs);
+ CHECK_VALUE(c_adt->cell_adr_table[i].cell_id > 0);
+ CHECK_VALUE(c_adt->cell_adr_table[i].start_sector <
+ c_adt->cell_adr_table[i].last_sector);
+ }
+
+ return 1;
+}
+
+
+static void ifoFree_C_ADT_internal(c_adt_t *c_adt) {
+ if(c_adt) {
+ free(c_adt->cell_adr_table);
+ free(c_adt);
+ }
+}
+
+void ifoFree_C_ADT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_C_ADT_internal(ifofile->menu_c_adt);
+ ifofile->menu_c_adt = NULL;
+}
+
+void ifoFree_TITLE_C_ADT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_C_ADT_internal(ifofile->vts_c_adt);
+ ifofile->vts_c_adt = NULL;
+}
+
+int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_vobu_admap == 0) /* mandatory */
+ return 0;
+
+ ifofile->vts_vobu_admap = calloc(1, sizeof(vobu_admap_t));
+ if(!ifofile->vts_vobu_admap)
+ return 0;
+
+ if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->vts_vobu_admap,
+ ifofile->vtsi_mat->vts_vobu_admap)) {
+ free(ifofile->vts_vobu_admap);
+ ifofile->vts_vobu_admap = NULL;
+ return 0;
+ }
+
+ return 1;
+}
+
+int ifoRead_VOBU_ADMAP(ifo_handle_t *ifofile) {
+ unsigned int sector;
+
+ if(!ifofile)
+ return 0;
+
+ if(ifofile->vmgi_mat) {
+ if(ifofile->vmgi_mat->vmgm_vobu_admap == 0)
+ return 1;
+ sector = ifofile->vmgi_mat->vmgm_vobu_admap;
+ } else if(ifofile->vtsi_mat) {
+ if(ifofile->vtsi_mat->vtsm_vobu_admap == 0)
+ return 1;
+ sector = ifofile->vtsi_mat->vtsm_vobu_admap;
+ } else {
+ return 0;
+ }
+
+ ifofile->menu_vobu_admap = calloc(1, sizeof(vobu_admap_t));
+ if(!ifofile->menu_vobu_admap)
+ return 0;
+
+ if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->menu_vobu_admap, sector)) {
+ free(ifofile->menu_vobu_admap);
+ ifofile->menu_vobu_admap = NULL;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile,
+ vobu_admap_t *vobu_admap,
+ unsigned int sector) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ unsigned int i;
+ int info_length;
+
+ if(!DVDFileSeekForce_(ifop->file, sector * DVD_BLOCK_LEN, sector))
+ return 0;
+
+ if(!(DVDReadBytes(ifop->file, vobu_admap, VOBU_ADMAP_SIZE)))
+ return 0;
+
+ B2N_32(vobu_admap->last_byte);
+
+ info_length = vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE;
+ /* assert(info_length > 0);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with a VOBS that has no VOBUs. */
+ CHECK_VALUE(info_length % sizeof(uint32_t) == 0);
+
+ vobu_admap->vobu_start_sectors = calloc(1, info_length);
+ if(!vobu_admap->vobu_start_sectors) {
+ return 0;
+ }
+ if(info_length &&
+ !(DVDReadBytes(ifop->file,
+ vobu_admap->vobu_start_sectors, info_length))) {
+ free(vobu_admap->vobu_start_sectors);
+ return 0;
+ }
+
+ for(i = 0; i < info_length/sizeof(uint32_t); i++)
+ B2N_32(vobu_admap->vobu_start_sectors[i]);
+
+ return 1;
+}
+
+
+static void ifoFree_VOBU_ADMAP_internal(vobu_admap_t *vobu_admap) {
+ if(vobu_admap) {
+ free(vobu_admap->vobu_start_sectors);
+ free(vobu_admap);
+ }
+}
+
+void ifoFree_VOBU_ADMAP(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_VOBU_ADMAP_internal(ifofile->menu_vobu_admap);
+ ifofile->menu_vobu_admap = NULL;
+}
+
+void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_VOBU_ADMAP_internal(ifofile->vts_vobu_admap);
+ ifofile->vts_vobu_admap = NULL;
+}
+
+int ifoRead_PGCIT(ifo_handle_t *ifofile) {
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_pgcit == 0) /* mandatory */
+ return 0;
+
+ ifofile->vts_pgcit = calloc(1, sizeof(pgcit_t));
+ if(!ifofile->vts_pgcit)
+ return 0;
+
+ ifofile->vts_pgcit->ref_count = 1;
+ if(!ifoRead_PGCIT_internal(ifofile, ifofile->vts_pgcit,
+ ifofile->vtsi_mat->vts_pgcit * DVD_BLOCK_LEN)) {
+ free(ifofile->vts_pgcit);
+ ifofile->vts_pgcit = NULL;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int find_dup_pgc(pgci_srp_t *pgci_srp, uint32_t start_byte, int count) {
+ int i;
+
+ for(i = 0; i < count; i++) {
+ if(pgci_srp[i].pgc_start_byte == start_byte) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit,
+ unsigned int offset) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ int i, info_length;
+ uint8_t *data, *ptr;
+
+ if(!DVDFileSeek_(ifop->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifop->file, pgcit, PGCIT_SIZE)))
+ return 0;
+
+ B2N_16(pgcit->nr_of_pgci_srp);
+ B2N_32(pgcit->last_byte);
+
+ CHECK_ZERO(pgcit->zero_1);
+ /* assert(pgcit->nr_of_pgci_srp != 0);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with 0 PTTs. */
+ CHECK_VALUE(pgcit->nr_of_pgci_srp < 10000); /* ?? seen max of 1338 */
+
+ if (pgcit->nr_of_pgci_srp == 0) {
+ pgcit->pgci_srp = NULL;
+ return 1;
+ }
+
+ info_length = pgcit->nr_of_pgci_srp * PGCI_SRP_SIZE;
+ data = calloc(1, info_length);
+ if(!data)
+ return 0;
+
+ if(info_length && !(DVDReadBytes(ifop->file, data, info_length))) {
+ free(data);
+ return 0;
+ }
+
+ pgcit->pgci_srp = calloc(pgcit->nr_of_pgci_srp, sizeof(pgci_srp_t));
+ if(!pgcit->pgci_srp) {
+ free(data);
+ return 0;
+ }
+ ptr = data;
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ memcpy(&pgcit->pgci_srp[i], ptr, PGCI_SRP_SIZE);
+ ptr += PGCI_SRP_SIZE;
+ read_pgci_srp(&pgcit->pgci_srp[i]);
+ CHECK_VALUE(pgcit->pgci_srp[i].zero_1 == 0);
+ }
+ free(data);
+
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++)
+ CHECK_VALUE(pgcit->pgci_srp[i].pgc_start_byte + PGC_SIZE <= pgcit->last_byte+1);
+
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ int dup;
+ if((dup = find_dup_pgc(pgcit->pgci_srp, pgcit->pgci_srp[i].pgc_start_byte, i)) >= 0) {
+ pgcit->pgci_srp[i].pgc = pgcit->pgci_srp[dup].pgc;
+ pgcit->pgci_srp[i].pgc->ref_count++;
+ continue;
+ }
+ pgcit->pgci_srp[i].pgc = calloc(1, sizeof(pgc_t));
+ if(!pgcit->pgci_srp[i].pgc) {
+ int j;
+ for(j = 0; j < i; j++) {
+ ifoFree_PGC(&pgcit->pgci_srp[j].pgc);
+ }
+ goto fail;
+ }
+ pgcit->pgci_srp[i].pgc->ref_count = 1;
+ if(!ifoRead_PGC(ifofile, pgcit->pgci_srp[i].pgc,
+ offset + pgcit->pgci_srp[i].pgc_start_byte)) {
+ Log0(ifop->ctx, "Unable to read invalid PCG");
+ //E-One releases provide bogus PGC, ie: out of bound start_byte
+ free(pgcit->pgci_srp[i].pgc);
+ pgcit->pgci_srp[i].pgc = NULL;
+ }
+ }
+
+ return 1;
+fail:
+ free(pgcit->pgci_srp);
+ pgcit->pgci_srp = NULL;
+ return 0;
+}
+
+static void ifoFree_PGCIT_internal(pgcit_t **pgcit) {
+ if(pgcit && *pgcit && (--(*pgcit)->ref_count <= 0)) {
+ int i;
+ for(i = 0; i < (*pgcit)->nr_of_pgci_srp; i++)
+ {
+ ifoFree_PGC(&(*pgcit)->pgci_srp[i].pgc);
+ }
+ free((*pgcit)->pgci_srp);
+ free(*pgcit);
+ }
+ if (pgcit)
+ *pgcit = NULL;
+}
+
+void ifoFree_PGCIT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->vts_pgcit) {
+ ifoFree_PGCIT_internal(&ifofile->vts_pgcit);
+ }
+}
+
+static int find_dup_lut(pgci_lu_t *lu, uint32_t start_byte, int count) {
+ int i;
+
+ for(i = 0; i < count; i++) {
+ if(lu[i].lang_start_byte == start_byte) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int ifoRead_PGCI_UT(ifo_handle_t *ifofile) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ pgci_ut_t *pgci_ut;
+ unsigned int sector;
+ unsigned int i;
+ int info_length;
+ uint8_t *data, *ptr;
+
+ if(!ifofile)
+ return 0;
+
+ if(ifofile->vmgi_mat) {
+ if(ifofile->vmgi_mat->vmgm_pgci_ut == 0)
+ return 1;
+ sector = ifofile->vmgi_mat->vmgm_pgci_ut;
+ } else if(ifofile->vtsi_mat) {
+ if(ifofile->vtsi_mat->vtsm_pgci_ut == 0)
+ return 1;
+ sector = ifofile->vtsi_mat->vtsm_pgci_ut;
+ } else {
+ return 0;
+ }
+
+ ifofile->pgci_ut = calloc(1, sizeof(pgci_ut_t));
+ if(!ifofile->pgci_ut)
+ return 0;
+
+ if(!DVDFileSeek_(ifop->file, sector * DVD_BLOCK_LEN)) {
+ free(ifofile->pgci_ut);
+ ifofile->pgci_ut = NULL;
+ return 0;
+ }
+
+ if(!(DVDReadBytes(ifop->file, ifofile->pgci_ut, PGCI_UT_SIZE))) {
+ free(ifofile->pgci_ut);
+ ifofile->pgci_ut = NULL;
+ return 0;
+ }
+
+ pgci_ut = ifofile->pgci_ut;
+
+ B2N_16(pgci_ut->nr_of_lus);
+ B2N_32(pgci_ut->last_byte);
+
+ CHECK_ZERO(pgci_ut->zero_1);
+ CHECK_VALUE(pgci_ut->nr_of_lus != 0);
+ CHECK_VALUE(pgci_ut->nr_of_lus < 100); /* ?? 3-4 ? */
+ CHECK_VALUE((uint32_t)pgci_ut->nr_of_lus * PGCI_LU_SIZE < pgci_ut->last_byte);
+
+ info_length = pgci_ut->nr_of_lus * PGCI_LU_SIZE;
+ data = calloc(1, info_length);
+ if(!data) {
+ free(pgci_ut);
+ ifofile->pgci_ut = NULL;
+ return 0;
+ }
+ if(!(DVDReadBytes(ifop->file, data, info_length))) {
+ free(data);
+ free(pgci_ut);
+ ifofile->pgci_ut = NULL;
+ return 0;
+ }
+
+ pgci_ut->lu = calloc(pgci_ut->nr_of_lus, sizeof(pgci_lu_t));
+ if(!pgci_ut->lu) {
+ free(data);
+ free(pgci_ut);
+ ifofile->pgci_ut = NULL;
+ return 0;
+ }
+ ptr = data;
+ for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+ memcpy(&pgci_ut->lu[i], ptr, PGCI_LU_SIZE);
+ ptr += PGCI_LU_SIZE;
+ B2N_16(pgci_ut->lu[i].lang_code);
+ B2N_32(pgci_ut->lu[i].lang_start_byte);
+ }
+ free(data);
+
+ for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+ /* Maybe this is only defined for v1.1 and later titles? */
+ /* If the bits in 'lu[i].exists' are enumerated abcd efgh then:
+ VTS_x_yy.IFO VIDEO_TS.IFO
+ a == 0x83 "Root" 0x82 "Title"
+ b == 0x84 "Subpicture"
+ c == 0x85 "Audio"
+ d == 0x86 "Angle"
+ e == 0x87 "PTT"
+ */
+ CHECK_VALUE((pgci_ut->lu[i].exists & 0x07) == 0);
+ }
+
+ for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+ int dup;
+ if((dup = find_dup_lut(pgci_ut->lu, pgci_ut->lu[i].lang_start_byte, i)) >= 0) {
+ pgci_ut->lu[i].pgcit = pgci_ut->lu[dup].pgcit;
+ pgci_ut->lu[i].pgcit->ref_count++;
+ continue;
+ }
+ pgci_ut->lu[i].pgcit = calloc(1, sizeof(pgcit_t));
+ if(!pgci_ut->lu[i].pgcit) {
+ unsigned int j;
+ for(j = 0; j < i; j++) {
+ ifoFree_PGCIT_internal(&pgci_ut->lu[j].pgcit);
+ }
+ free(pgci_ut->lu);
+ free(pgci_ut);
+ ifofile->pgci_ut = NULL;
+ return 0;
+ }
+ pgci_ut->lu[i].pgcit->ref_count = 1;
+ if(!ifoRead_PGCIT_internal(ifofile, pgci_ut->lu[i].pgcit,
+ sector * DVD_BLOCK_LEN
+ + pgci_ut->lu[i].lang_start_byte)) {
+ unsigned int j;
+ for(j = 0; j <= i; j++) {
+ ifoFree_PGCIT_internal(&pgci_ut->lu[j].pgcit);
+ }
+ free(pgci_ut->lu);
+ free(pgci_ut);
+ ifofile->pgci_ut = NULL;
+ return 0;
+ }
+ /* FIXME: Iterate and verify that all menus that should exists accordingly
+ * to pgci_ut->lu[i].exists really do? */
+ }
+
+ return 1;
+}
+
+
+void ifoFree_PGCI_UT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->pgci_ut) {
+ unsigned int i;
+
+ for(i = 0; i < ifofile->pgci_ut->nr_of_lus; i++) {
+ ifoFree_PGCIT_internal(&ifofile->pgci_ut->lu[i].pgcit);
+ }
+ free(ifofile->pgci_ut->lu);
+ free(ifofile->pgci_ut);
+ ifofile->pgci_ut = NULL;
+ }
+}
+
+static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile,
+ vts_attributes_t *vts_attributes,
+ unsigned int offset) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ unsigned int i;
+
+ if(!DVDFileSeek_(ifop->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifop->file, vts_attributes, sizeof(vts_attributes_t))))
+ return 0;
+
+ read_video_attr(&vts_attributes->vtsm_vobs_attr);
+ read_video_attr(&vts_attributes->vtstt_vobs_video_attr);
+ read_audio_attr(&vts_attributes->vtsm_audio_attr);
+ for(i=0; i<8; i++)
+ read_audio_attr(&vts_attributes->vtstt_audio_attr[i]);
+ read_subp_attr(&vts_attributes->vtsm_subp_attr);
+ for(i=0; i<32; i++)
+ read_subp_attr(&vts_attributes->vtstt_subp_attr[i]);
+ B2N_32(vts_attributes->last_byte);
+ B2N_32(vts_attributes->vts_cat);
+
+ CHECK_ZERO(vts_attributes->zero_1);
+ CHECK_ZERO(vts_attributes->zero_2);
+ CHECK_ZERO(vts_attributes->zero_3);
+ CHECK_ZERO(vts_attributes->zero_4);
+ CHECK_ZERO(vts_attributes->zero_5);
+ CHECK_ZERO(vts_attributes->zero_6);
+ CHECK_ZERO(vts_attributes->zero_7);
+ CHECK_VALUE(vts_attributes->nr_of_vtsm_audio_streams <= 1);
+ CHECK_VALUE(vts_attributes->nr_of_vtsm_subp_streams <= 1);
+ CHECK_VALUE(vts_attributes->nr_of_vtstt_audio_streams <= 8);
+ for(i = vts_attributes->nr_of_vtstt_audio_streams; i < 8; i++)
+ CHECK_ZERO(vts_attributes->vtstt_audio_attr[i]);
+ CHECK_VALUE(vts_attributes->nr_of_vtstt_subp_streams <= 32);
+ {
+ unsigned int nr_coded;
+ CHECK_VALUE(vts_attributes->last_byte + 1 >= VTS_ATTRIBUTES_MIN_SIZE);
+ nr_coded = (vts_attributes->last_byte + 1 - VTS_ATTRIBUTES_MIN_SIZE)/6;
+ /* This is often nr_coded = 70, how do you know how many there really are? */
+ if(nr_coded > 32) { /* We haven't read more from disk/file anyway */
+ nr_coded = 32;
+ }
+ CHECK_VALUE(vts_attributes->nr_of_vtstt_subp_streams <= nr_coded);
+ for(i = vts_attributes->nr_of_vtstt_subp_streams; i < nr_coded; i++)
+ CHECK_ZERO(vts_attributes->vtstt_subp_attr[i]);
+ }
+
+ return 1;
+}
+
+
+
+int ifoRead_VTS_ATRT(ifo_handle_t *ifofile) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ vts_atrt_t *vts_atrt;
+ unsigned int i, info_length, sector;
+ uint32_t *data;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ if(ifofile->vmgi_mat->vts_atrt == 0) /* mandatory */
+ return 0;
+
+ sector = ifofile->vmgi_mat->vts_atrt;
+ if(!DVDFileSeek_(ifop->file, sector * DVD_BLOCK_LEN))
+ return 0;
+
+ vts_atrt = calloc(1, sizeof(vts_atrt_t));
+ if(!vts_atrt)
+ return 0;
+
+ ifofile->vts_atrt = vts_atrt;
+
+ if(!(DVDReadBytes(ifop->file, vts_atrt, VTS_ATRT_SIZE))) {
+ free(vts_atrt);
+ ifofile->vts_atrt = NULL;
+ return 0;
+ }
+
+ B2N_16(vts_atrt->nr_of_vtss);
+ B2N_32(vts_atrt->last_byte);
+
+ CHECK_ZERO(vts_atrt->zero_1);
+ CHECK_VALUE(vts_atrt->nr_of_vtss != 0);
+ CHECK_VALUE(vts_atrt->nr_of_vtss < 100); /* ?? */
+ CHECK_VALUE((uint32_t)vts_atrt->nr_of_vtss * (4 + VTS_ATTRIBUTES_MIN_SIZE) +
+ VTS_ATRT_SIZE < vts_atrt->last_byte + 1);
+
+ info_length = vts_atrt->nr_of_vtss * sizeof(uint32_t);
+ data = calloc(1, info_length);
+ if(!data) {
+ free(vts_atrt);
+ ifofile->vts_atrt = NULL;
+ return 0;
+ }
+
+ vts_atrt->vts_atrt_offsets = data;
+
+ if(!(DVDReadBytes(ifop->file, data, info_length))) {
+ free(data);
+ free(vts_atrt);
+ ifofile->vts_atrt = NULL;
+ return 0;
+ }
+
+ for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+ B2N_32(data[i]);
+ CHECK_VALUE(data[i] + VTS_ATTRIBUTES_MIN_SIZE < vts_atrt->last_byte + 1);
+ }
+
+ info_length = vts_atrt->nr_of_vtss * sizeof(vts_attributes_t);
+ vts_atrt->vts = calloc(1, info_length);
+ if(!vts_atrt->vts) {
+ free(data);
+ free(vts_atrt);
+ ifofile->vts_atrt = NULL;
+ return 0;
+ }
+ for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+ unsigned int offset = data[i];
+ if(!ifoRead_VTS_ATTRIBUTES(ifofile, &(vts_atrt->vts[i]),
+ (sector * DVD_BLOCK_LEN) + offset)) {
+ free(data);
+ free(vts_atrt);
+ ifofile->vts_atrt = NULL;
+ return 0;
+ }
+
+ /* This assert can't be in ifoRead_VTS_ATTRIBUTES */
+ CHECK_VALUE(offset + vts_atrt->vts[i].last_byte <= vts_atrt->last_byte + 1);
+ /* Is this check correct? */
+ }
+
+ return 1;
+}
+
+
+void ifoFree_VTS_ATRT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->vts_atrt) {
+ free(ifofile->vts_atrt->vts);
+ free(ifofile->vts_atrt->vts_atrt_offsets);
+ free(ifofile->vts_atrt);
+ ifofile->vts_atrt = NULL;
+ }
+}
+
+
+int ifoRead_TXTDT_MGI(ifo_handle_t *ifofile) {
+ struct ifo_handle_private_s *ifop = PRIV(ifofile);
+ txtdt_mgi_t *txtdt_mgi;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ /* Return successfully if there is nothing to read. */
+ if(ifofile->vmgi_mat->txtdt_mgi == 0)
+ return 1;
+
+ if(!DVDFileSeek_(ifop->file,
+ ifofile->vmgi_mat->txtdt_mgi * DVD_BLOCK_LEN))
+ return 0;
+
+ txtdt_mgi = calloc(1, sizeof(txtdt_mgi_t));
+ if(!txtdt_mgi) {
+ return 0;
+ }
+ ifofile->txtdt_mgi = txtdt_mgi;
+
+ if(!(DVDReadBytes(ifop->file, txtdt_mgi, TXTDT_MGI_SIZE))) {
+ Log0(ifop->ctx, "Unable to read TXTDT_MGI.");
+ free(txtdt_mgi);
+ ifofile->txtdt_mgi = NULL;
+ return 0;
+ }
+
+ /* Log1(ifop->ctx, "-- Not done yet --\n"); */
+ return 1;
+}
+
+void ifoFree_TXTDT_MGI(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->txtdt_mgi) {
+ free(ifofile->txtdt_mgi);
+ ifofile->txtdt_mgi = NULL;
+ }
+}
diff --git a/libdvdread-embedded/src/logger.c b/libdvdread-embedded/src/logger.c
new file mode 100644
index 0000000..c8a45f2
--- /dev/null
+++ b/libdvdread-embedded/src/logger.c
@@ -0,0 +1,41 @@
+/*
+ * This file is part of libdvdread.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "dvdread/dvd_reader.h"
+#include "logger.h"
+
+void DVDReadLog( void *priv, const dvd_logger_cb *logcb,
+ dvd_logger_level_t level, const char *fmt, ... )
+{
+ va_list list;
+ va_start(list, fmt);
+ if(logcb && logcb->pf_log)
+ logcb->pf_log(priv, level, fmt, list);
+ else
+ {
+ FILE *stream = (level == DVD_LOGGER_LEVEL_ERROR) ? stderr : stdout;
+ fprintf(stream, "libdvdread: ");
+ vfprintf(stream, fmt, list);
+ fprintf(stream, "\n");
+ }
+ va_end(list);
+}
diff --git a/libdvdread-embedded/src/logger.h b/libdvdread-embedded/src/logger.h
new file mode 100644
index 0000000..ea3245c
--- /dev/null
+++ b/libdvdread-embedded/src/logger.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of libdvdread.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ */
+
+#ifndef LIBDVDREAD_LOGGER_H
+#define LIBDVDREAD_LOGGER_H
+
+void DVDReadLog( void *priv, const dvd_logger_cb *logcb,
+ dvd_logger_level_t level, const char *fmt, ... );
+
+#define LOG(ctx, level, ...) \
+ DVDReadLog(ctx->priv, &ctx->logcb, level, __VA_ARGS__)
+#define Log0(ctx, ...) LOG(ctx, DVD_LOGGER_LEVEL_ERROR, __VA_ARGS__)
+#define Log1(ctx, ...) LOG(ctx, DVD_LOGGER_LEVEL_WARN, __VA_ARGS__)
+#define Log2(ctx, ...) LOG(ctx, DVD_LOGGER_LEVEL_INFO, __VA_ARGS__)
+#define Log3(ctx, ...) LOG(ctx, DVD_LOGGER_LEVEL_DEBUG, __VA_ARGS__)
+
+#endif
diff --git a/libdvdread-embedded/src/md5.c b/libdvdread-embedded/src/md5.c
new file mode 100644
index 0000000..54f4ed0
--- /dev/null
+++ b/libdvdread-embedded/src/md5.c
@@ -0,0 +1,346 @@
+/*****************************************************************************
+ * md5.c: not so strong MD5 hashing
+ *****************************************************************************
+ * Copyright (C) 1995,1996,1998,1999,2001,2002,
+ * 2003 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/* md5.c - MD5 Message-Digest Algorithm
+ *
+ * According to the definition of MD5 in RFC 1321 from April 1992.
+ * NOTE: This is *not* the same file as the one from glibc.
+ * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+ * heavily modified for GnuPG by Werner Koch <wk@gnupg.org>
+ */
+
+/* Test values:
+ * "" D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E
+ * "a" 0C C1 75 B9 C0 F1 B6 A8 31 C3 99 E2 69 77 26 61
+ * "abc 90 01 50 98 3C D2 4F B0 D6 96 3F 7D 28 E1 7F 72
+ * "message digest" F9 6B 69 7D 7C B7 93 8D 52 5A 2F 31 AA F1 61 D0
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "md5.h"
+
+typedef uint32_t u32;
+typedef uint8_t byte;
+#define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) )
+
+typedef struct md5_s MD5_CONTEXT;
+
+
+static void
+md5_init( void *context )
+{
+ MD5_CONTEXT *ctx = context;
+
+ ctx->A = 0x67452301;
+ ctx->B = 0xefcdab89;
+ ctx->C = 0x98badcfe;
+ ctx->D = 0x10325476;
+
+ ctx->nblocks = 0;
+ ctx->count = 0;
+}
+
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ and defined in the RFC 1321. The first function is a little bit optimized
+ (as found in Colin Plumbs public domain implementation). */
+/* #define FF(b, c, d) ((b & c) | (~b & d)) */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF (d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+
+/****************
+ * transform n*64 bytes
+ */
+static void
+transform ( MD5_CONTEXT *ctx, const unsigned char *data )
+{
+ u32 correct_words[16];
+ register u32 A = ctx->A;
+ register u32 B = ctx->B;
+ register u32 C = ctx->C;
+ register u32 D = ctx->D;
+ u32 *cwp = correct_words;
+
+#ifdef WORDS_BIGENDIAN
+ {
+ int i;
+ byte *p2, *p1;
+ for(i=0, p1=data, p2=(byte*)correct_words; i < 16; i++, p2 += 4 )
+ {
+ p2[3] = *p1++;
+ p2[2] = *p1++;
+ p2[1] = *p1++;
+ p2[0] = *p1++;
+ }
+ }
+#else
+ memcpy( correct_words, data, 64 );
+#endif
+
+
+#define OP(a, b, c, d, s, T) \
+ do \
+ { \
+ a += FF (b, c, d) + (*cwp++) + T; \
+ a = rol(a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* Before we start, one word about the strange constants.
+ They are defined in RFC 1321 as
+
+ T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+ */
+
+ /* Round 1. */
+ OP (A, B, C, D, 7, 0xd76aa478);
+ OP (D, A, B, C, 12, 0xe8c7b756);
+ OP (C, D, A, B, 17, 0x242070db);
+ OP (B, C, D, A, 22, 0xc1bdceee);
+ OP (A, B, C, D, 7, 0xf57c0faf);
+ OP (D, A, B, C, 12, 0x4787c62a);
+ OP (C, D, A, B, 17, 0xa8304613);
+ OP (B, C, D, A, 22, 0xfd469501);
+ OP (A, B, C, D, 7, 0x698098d8);
+ OP (D, A, B, C, 12, 0x8b44f7af);
+ OP (C, D, A, B, 17, 0xffff5bb1);
+ OP (B, C, D, A, 22, 0x895cd7be);
+ OP (A, B, C, D, 7, 0x6b901122);
+ OP (D, A, B, C, 12, 0xfd987193);
+ OP (C, D, A, B, 17, 0xa679438e);
+ OP (B, C, D, A, 22, 0x49b40821);
+
+#undef OP
+#define OP(f, a, b, c, d, k, s, T) \
+ do \
+ { \
+ a += f (b, c, d) + correct_words[k] + T; \
+ a = rol(a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* Round 2. */
+ OP (FG, A, B, C, D, 1, 5, 0xf61e2562);
+ OP (FG, D, A, B, C, 6, 9, 0xc040b340);
+ OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
+ OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+ OP (FG, A, B, C, D, 5, 5, 0xd62f105d);
+ OP (FG, D, A, B, C, 10, 9, 0x02441453);
+ OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
+ OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+ OP (FG, A, B, C, D, 9, 5, 0x21e1cde6);
+ OP (FG, D, A, B, C, 14, 9, 0xc33707d6);
+ OP (FG, C, D, A, B, 3, 14, 0xf4d50d87);
+ OP (FG, B, C, D, A, 8, 20, 0x455a14ed);
+ OP (FG, A, B, C, D, 13, 5, 0xa9e3e905);
+ OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+ OP (FG, C, D, A, B, 7, 14, 0x676f02d9);
+ OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+
+ /* Round 3. */
+ OP (FH, A, B, C, D, 5, 4, 0xfffa3942);
+ OP (FH, D, A, B, C, 8, 11, 0x8771f681);
+ OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
+ OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
+ OP (FH, A, B, C, D, 1, 4, 0xa4beea44);
+ OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+ OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+ OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
+ OP (FH, A, B, C, D, 13, 4, 0x289b7ec6);
+ OP (FH, D, A, B, C, 0, 11, 0xeaa127fa);
+ OP (FH, C, D, A, B, 3, 16, 0xd4ef3085);
+ OP (FH, B, C, D, A, 6, 23, 0x04881d05);
+ OP (FH, A, B, C, D, 9, 4, 0xd9d4d039);
+ OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
+ OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+ OP (FH, B, C, D, A, 2, 23, 0xc4ac5665);
+
+ /* Round 4. */
+ OP (FI, A, B, C, D, 0, 6, 0xf4292244);
+ OP (FI, D, A, B, C, 7, 10, 0x432aff97);
+ OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
+ OP (FI, B, C, D, A, 5, 21, 0xfc93a039);
+ OP (FI, A, B, C, D, 12, 6, 0x655b59c3);
+ OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+ OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
+ OP (FI, B, C, D, A, 1, 21, 0x85845dd1);
+ OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+ OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+ OP (FI, C, D, A, B, 6, 15, 0xa3014314);
+ OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
+ OP (FI, A, B, C, D, 4, 6, 0xf7537e82);
+ OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
+ OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+ OP (FI, B, C, D, A, 9, 21, 0xeb86d391);
+
+ /* Put checksum in context given as argument. */
+ ctx->A += A;
+ ctx->B += B;
+ ctx->C += C;
+ ctx->D += D;
+}
+
+
+
+/* The routine updates the message-digest context to
+ * account for the presence of each of the characters inBuf[0..inLen-1]
+ * in the message whose digest is being computed.
+ */
+static void
+md5_write( void *context, const void *inbuf_arg , size_t inlen)
+{
+ const unsigned char *inbuf = inbuf_arg;
+ MD5_CONTEXT *hd = context;
+
+ if( hd->count == 64 ) /* flush the buffer */
+ {
+ transform( hd, hd->buf );
+ hd->count = 0;
+ hd->nblocks++;
+ }
+ if( !inbuf )
+ return;
+
+ if( hd->count )
+ {
+ for( ; inlen && hd->count < 64; inlen-- )
+ hd->buf[hd->count++] = *inbuf++;
+ md5_write( hd, NULL, 0 );
+ if( !inlen )
+ return;
+ }
+
+ while( inlen >= 64 )
+ {
+ transform( hd, inbuf );
+ hd->count = 0;
+ hd->nblocks++;
+ inlen -= 64;
+ inbuf += 64;
+ }
+ for( ; inlen && hd->count < 64; inlen-- )
+ hd->buf[hd->count++] = *inbuf++;
+
+}
+
+
+
+/* The routine final terminates the message-digest computation and
+ * ends with the desired message digest in mdContext->digest[0...15].
+ * The handle is prepared for a new MD5 cycle.
+ * Returns 16 bytes representing the digest.
+ */
+
+static void
+md5_final( void *context)
+{
+ MD5_CONTEXT *hd = context;
+ u32 t, msb, lsb;
+ byte *p;
+
+ md5_write(hd, NULL, 0); /* flush */;
+
+ t = hd->nblocks;
+ /* multiply by 64 to make a byte count */
+ lsb = t << 6;
+ msb = t >> 26;
+ /* add the count */
+ t = lsb;
+ if( (lsb += hd->count) < t )
+ msb++;
+ /* multiply by 8 to make a bit count */
+ t = lsb;
+ lsb <<= 3;
+ msb <<= 3;
+ msb |= t >> 29;
+
+ if( hd->count < 56 ) /* enough room */
+ {
+ hd->buf[hd->count++] = 0x80; /* pad */
+ while( hd->count < 56 )
+ hd->buf[hd->count++] = 0; /* pad */
+ }
+ else /* need one extra block */
+ {
+ hd->buf[hd->count++] = 0x80; /* pad character */
+ while( hd->count < 64 )
+ hd->buf[hd->count++] = 0;
+ md5_write(hd, NULL, 0); /* flush */;
+ memset(hd->buf, 0, 56 ); /* fill next block with zeroes */
+ }
+ /* append the 64 bit count */
+ hd->buf[56] = lsb ;
+ hd->buf[57] = lsb >> 8;
+ hd->buf[58] = lsb >> 16;
+ hd->buf[59] = lsb >> 24;
+ hd->buf[60] = msb ;
+ hd->buf[61] = msb >> 8;
+ hd->buf[62] = msb >> 16;
+ hd->buf[63] = msb >> 24;
+ transform( hd, hd->buf );
+
+ p = hd->buf;
+#ifdef WORDS_BIGENDIAN
+#define X(a) do { *p++ = hd->a ; *p++ = hd->a >> 8; \
+ *p++ = hd->a >> 16; *p++ = hd->a >> 24; } while(0)
+#else /* little endian */
+#define X(a) do { *(u32*)p = (*hd).a ; p += 4; } while(0)
+#endif
+ X(A);
+ X(B);
+ X(C);
+ X(D);
+#undef X
+
+}
+
+#if 0
+static byte *
+md5_read( void *context )
+{
+ MD5_CONTEXT *hd = (MD5_CONTEXT *) context;
+ return hd->buf;
+}
+#endif
+
+void InitMD5( struct md5_s *h )
+{
+ md5_init( h );
+}
+
+void AddMD5( struct md5_s *h, const void *data, size_t len )
+{
+ md5_write( h, data, len );
+}
+
+void EndMD5( struct md5_s *h )
+{
+ md5_final( h );
+}
diff --git a/libdvdread-embedded/src/md5.h b/libdvdread-embedded/src/md5.h
new file mode 100644
index 0000000..50ce88c
--- /dev/null
+++ b/libdvdread-embedded/src/md5.h
@@ -0,0 +1,46 @@
+/*****************************************************************************
+ * md5.h: MD5 hash
+ *****************************************************************************
+ * Copyright © 2004-2011 VLC authors and VideoLAN
+ *
+ * Authors: Rémi Denis-Courmont
+ * Rafaël Carré
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef LIBDVDREAD_MD5_H
+#define LIBDVDREAD_MD5_H
+
+#include <stdint.h>
+
+/**
+ * \file
+ * This file defines functions and structures to compute MD5 digests
+ */
+
+struct md5_s
+{
+ uint32_t A, B, C, D; /* chaining variables */
+ uint32_t nblocks;
+ uint8_t buf[64];
+ int count;
+};
+
+void InitMD5( struct md5_s * );
+void AddMD5( struct md5_s *, const void *, size_t );
+void EndMD5( struct md5_s * );
+
+#endif
diff --git a/libdvdread-embedded/src/nav_print.c b/libdvdread-embedded/src/nav_print.c
new file mode 100644
index 0000000..d8f7b27
--- /dev/null
+++ b/libdvdread-embedded/src/nav_print.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Much of the contents in this file is based on VOBDUMP.
+ *
+ * VOBDUMP: a program for examining DVD .VOB files
+ *
+ * Copyright 1998, 1999 Eric Smith <eric@brouhaha.com>
+ *
+ * VOBDUMP is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 or version 3 as
+ * published by the Free Software Foundation. Note that I am not
+ * granting permission to redistribute or modify VOBDUMP under the
+ * terms of any later version of the General Public License.
+ *
+ * This program is distributed in the hope that it will be useful (or
+ * at least amusing), but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "dvdread/nav_types.h"
+#include "dvdread/nav_print.h"
+#include "dvdread/ifo_print.h"
+
+static void navPrint_PCI_GI(pci_gi_t *pci_gi) {
+ int i;
+
+ printf("pci_gi:\n");
+ printf("nv_pck_lbn 0x%08x\n", pci_gi->nv_pck_lbn);
+ printf("vobu_cat 0x%04x\n", pci_gi->vobu_cat);
+ printf("vobu_s_ptm 0x%08x\n", pci_gi->vobu_s_ptm);
+ printf("vobu_e_ptm 0x%08x\n", pci_gi->vobu_e_ptm);
+ printf("vobu_se_e_ptm 0x%08x\n", pci_gi->vobu_se_e_ptm);
+ printf("e_eltm ");
+ dvdread_print_time(&pci_gi->e_eltm);
+ printf("\n");
+
+ printf("vobu_isrc \"");
+ for(i = 0; i < 32; i++) {
+ char c = pci_gi->vobu_isrc[i];
+ if((c >= ' ') && (c <= '~'))
+ printf("%c", c);
+ else
+ printf(".");
+ }
+ printf("\"\n");
+}
+
+static void navPrint_NSML_AGLI(nsml_agli_t *nsml_agli) {
+ int i, j = 0;
+
+ for(i = 0; i < 9; i++)
+ j |= nsml_agli->nsml_agl_dsta[i];
+ if(j == 0)
+ return;
+
+ printf("nsml_agli:\n");
+ for(i = 0; i < 9; i++)
+ if(nsml_agli->nsml_agl_dsta[i])
+ printf("nsml_agl_c%d_dsta 0x%08x\n", i + 1,
+ nsml_agli->nsml_agl_dsta[i]);
+}
+
+static void navPrint_HL_GI(hl_gi_t *hl_gi, int *btngr_ns, int *btn_ns) {
+
+ if((hl_gi->hli_ss & 0x03) == 0)
+ return;
+
+ printf("hl_gi:\n");
+ printf("hli_ss 0x%01x\n", hl_gi->hli_ss & 0x03);
+ printf("hli_s_ptm 0x%08x\n", hl_gi->hli_s_ptm);
+ printf("hli_e_ptm 0x%08x\n", hl_gi->hli_e_ptm);
+ printf("btn_se_e_ptm 0x%08x\n", hl_gi->btn_se_e_ptm);
+
+ *btngr_ns = hl_gi->btngr_ns;
+ printf("btngr_ns %u\n", hl_gi->btngr_ns);
+ printf("btngr%d_dsp_ty 0x%02x\n", 1, hl_gi->btngr1_dsp_ty);
+ printf("btngr%d_dsp_ty 0x%02x\n", 2, hl_gi->btngr2_dsp_ty);
+ printf("btngr%d_dsp_ty 0x%02x\n", 3, hl_gi->btngr3_dsp_ty);
+
+ printf("btn_ofn %d\n", hl_gi->btn_ofn);
+ *btn_ns = hl_gi->btn_ns;
+ printf("btn_ns %d\n", hl_gi->btn_ns);
+ printf("nsl_btn_ns %d\n", hl_gi->nsl_btn_ns);
+ printf("fosl_btnn %d\n", hl_gi->fosl_btnn);
+ printf("foac_btnn %d\n", hl_gi->foac_btnn);
+}
+
+static void navPrint_BTN_COLIT(btn_colit_t *btn_colit) {
+ int i, j;
+
+ j = 0;
+ for(i = 0; i < 6; i++)
+ j |= btn_colit->btn_coli[i/2][i&1];
+ if(j == 0)
+ return;
+
+ printf("btn_colit:\n");
+ for(i = 0; i < 3; i++)
+ for(j = 0; j < 2; j++)
+ printf("btn_cqoli %d %s_coli: %08x\n",
+ i, (j == 0) ? "sl" : "ac",
+ btn_colit->btn_coli[i][j]);
+}
+
+static void navPrint_BTNIT(btni_t *btni_table, int btngr_ns, int btn_ns) {
+ int i, j;
+
+ printf("btnit:\n");
+ printf("btngr_ns: %i\n", btngr_ns);
+ printf("btn_ns: %i\n", btn_ns);
+
+ if(btngr_ns == 0)
+ return;
+
+ for(i = 0; i < btngr_ns; i++) {
+ for(j = 0; j < (36 / btngr_ns); j++) {
+ if(j < btn_ns) {
+ btni_t *btni = &btni_table[(36 / btngr_ns) * i + j];
+
+ printf("group %d btni %d: ", i+1, j+1);
+ printf("btn_coln %u, auto_action_mode %u\n",
+ btni->btn_coln, btni->auto_action_mode);
+ printf("coords (%u, %u) .. (%u, %u)\n",
+ btni->x_start, btni->y_start, btni->x_end, btni->y_end);
+
+ printf("up %u, ", btni->up);
+ printf("down %u, ", btni->down);
+ printf("left %u, ", btni->left);
+ printf("right %u\n", btni->right);
+
+ /* ifoPrint_COMMAND(&btni->cmd); */
+ printf("\n");
+ }
+ }
+ }
+}
+
+static void navPrint_HLI(hli_t *hli) {
+ int btngr_ns = 0, btn_ns = 0;
+
+ printf("hli:\n");
+ navPrint_HL_GI(&hli->hl_gi, & btngr_ns, & btn_ns);
+ navPrint_BTN_COLIT(&hli->btn_colit);
+ navPrint_BTNIT(hli->btnit, btngr_ns, btn_ns);
+}
+
+void navPrint_PCI(pci_t *pci) {
+ printf("pci packet:\n");
+ navPrint_PCI_GI(&pci->pci_gi);
+ navPrint_NSML_AGLI(&pci->nsml_agli);
+ navPrint_HLI(&pci->hli);
+}
+
+static void navPrint_DSI_GI(dsi_gi_t *dsi_gi) {
+ printf("dsi_gi:\n");
+ printf("nv_pck_scr 0x%08x\n", dsi_gi->nv_pck_scr);
+ printf("nv_pck_lbn 0x%08x\n", dsi_gi->nv_pck_lbn );
+ printf("vobu_ea 0x%08x\n", dsi_gi->vobu_ea);
+ printf("vobu_1stref_ea 0x%08x\n", dsi_gi->vobu_1stref_ea);
+ printf("vobu_2ndref_ea 0x%08x\n", dsi_gi->vobu_2ndref_ea);
+ printf("vobu_3rdref_ea 0x%08x\n", dsi_gi->vobu_3rdref_ea);
+ printf("vobu_vob_idn 0x%04x\n", dsi_gi->vobu_vob_idn);
+ printf("vobu_c_idn 0x%02x\n", dsi_gi->vobu_c_idn);
+ printf("c_eltm ");
+ dvdread_print_time(&dsi_gi->c_eltm);
+ printf("\n");
+}
+
+static void navPrint_SML_PBI(sml_pbi_t *sml_pbi) {
+ printf("sml_pbi:\n");
+ printf("category 0x%04x\n", sml_pbi->category);
+ if(sml_pbi->category & 0x8000)
+ printf("VOBU is in preunit\n");
+ if(sml_pbi->category & 0x4000)
+ printf("VOBU is in ILVU\n");
+ if(sml_pbi->category & 0x2000)
+ printf("VOBU at the beginning of ILVU\n");
+ if(sml_pbi->category & 0x1000)
+ printf("VOBU at end of PREU of ILVU\n");
+
+ printf("ilvu_ea 0x%08x\n", sml_pbi->ilvu_ea);
+ printf("nxt_ilvu_sa 0x%08x\n", sml_pbi->ilvu_sa);
+ printf("nxt_ilvu_size 0x%04x\n", sml_pbi->size);
+
+ printf("vob_v_s_s_ptm 0x%08x\n", sml_pbi->vob_v_s_s_ptm);
+ printf("vob_v_e_e_ptm 0x%08x\n", sml_pbi->vob_v_e_e_ptm);
+
+ /* $$$ more code needed here */
+}
+
+static void navPrint_SML_AGLI(sml_agli_t *sml_agli) {
+ int i;
+ printf("sml_agli:\n");
+ for(i = 0; i < 9; i++) {
+ printf("agl_c%d address: 0x%08x size 0x%04x\n", i,
+ sml_agli->data[i].address, sml_agli->data[i].size);
+ }
+}
+
+static void navPrint_VOBU_SRI(vobu_sri_t *vobu_sri) {
+ int i;
+ int stime[19] = { 240, 120, 60, 20, 15, 14, 13, 12, 11,
+ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+ printf("vobu_sri:\n");
+ printf("Next VOBU with Video %08x\n", vobu_sri->next_video);
+ for(i = 0; i < 19; i++) {
+ printf("%3.1f %08x ", stime[i]/2.0, vobu_sri->fwda[i]);
+ }
+ printf("\n");
+ printf("Next VOBU %08x\n", vobu_sri->next_vobu);
+ printf("--\n");
+ printf("Prev VOBU %08x\n", vobu_sri->prev_vobu);
+ for(i = 0; i < 19; i++) {
+ printf("%3.1f %08x ", stime[18 - i]/2.0, vobu_sri->bwda[i]);
+ }
+ printf("\n");
+ printf("Prev VOBU with Video %08x\n", vobu_sri->prev_video);
+}
+
+static void navPrint_SYNCI(synci_t *synci) {
+ int i;
+
+ printf("synci:\n");
+ /* $$$ more code needed here */
+ for(i = 0; i < 8; i++)
+ printf("%04x ", synci->a_synca[i]);
+ for(i = 0; i < 32; i++)
+ printf("%08x ", synci->sp_synca[i]);
+}
+
+void navPrint_DSI(dsi_t *dsi) {
+ printf("dsi packet:\n");
+ navPrint_DSI_GI(&dsi->dsi_gi);
+ navPrint_SML_PBI(&dsi->sml_pbi);
+ navPrint_SML_AGLI(&dsi->sml_agli);
+ navPrint_VOBU_SRI(&dsi->vobu_sri);
+ navPrint_SYNCI(&dsi->synci);
+}
diff --git a/libdvdread-embedded/src/nav_read.c b/libdvdread-embedded/src/nav_read.c
new file mode 100644
index 0000000..bb781cf
--- /dev/null
+++ b/libdvdread-embedded/src/nav_read.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This file is part of libdvdread.
+ *
+ * libdvdread is free software; you can 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.
+ *
+ * libdvdread is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdread; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "bswap.h"
+#include "dvdread/nav_types.h"
+#include "dvdread/nav_read.h"
+#include "dvdread_internal.h"
+#include "dvdread/bitreader.h"
+
+#define getbits_init dvdread_getbits_init
+#define getbits dvdread_getbits
+
+#define CHECK_VALUE(arg)\
+ if(!(arg)) {\
+ DVDReadLog(NULL, NULL, DVD_LOGGER_LEVEL_WARN,\
+ "CHECK_VALUE failed in %s:%i for %s",\
+ __FILE__, __LINE__, # arg );\
+ }
+
+void navRead_PCI(pci_t *pci, unsigned char *buffer) {
+ int32_t i, j;
+ getbits_state_t state;
+ if (!getbits_init(&state, buffer)) abort(); /* Passed NULL pointers */
+
+ /* pci pci_gi */
+ pci->pci_gi.nv_pck_lbn = getbits(&state, 32 );
+ pci->pci_gi.vobu_cat = getbits(&state, 16 );
+ pci->pci_gi.zero1 = getbits(&state, 16 );
+ pci->pci_gi.vobu_uop_ctl.zero = getbits(&state, 7 );
+ pci->pci_gi.vobu_uop_ctl.video_pres_mode_change = getbits(&state, 1 );
+
+ pci->pci_gi.vobu_uop_ctl.karaoke_audio_pres_mode_change = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.angle_change = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.subpic_stream_change = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.audio_stream_change = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.pause_on = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.still_off = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.button_select_or_activate = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.resume = getbits(&state, 1 );
+
+ pci->pci_gi.vobu_uop_ctl.chapter_menu_call = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.angle_menu_call = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.audio_menu_call = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.subpic_menu_call = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.root_menu_call = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.title_menu_call = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.backward_scan = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.forward_scan = getbits(&state, 1 );
+
+ pci->pci_gi.vobu_uop_ctl.next_pg_search = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.prev_or_top_pg_search = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.time_or_chapter_search = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.go_up = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.stop = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.title_play = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.chapter_search_or_play = getbits(&state, 1 );
+ pci->pci_gi.vobu_uop_ctl.title_or_time_play = getbits(&state, 1 );
+ pci->pci_gi.vobu_s_ptm = getbits(&state, 32 );
+ pci->pci_gi.vobu_e_ptm = getbits(&state, 32 );
+ pci->pci_gi.vobu_se_e_ptm = getbits(&state, 32 );
+ pci->pci_gi.e_eltm.hour = getbits(&state, 8 );
+ pci->pci_gi.e_eltm.minute = getbits(&state, 8 );
+ pci->pci_gi.e_eltm.second = getbits(&state, 8 );
+ pci->pci_gi.e_eltm.frame_u = getbits(&state, 8 );
+ for(i = 0; i < 32; i++)
+ pci->pci_gi.vobu_isrc[i] = getbits(&state, 8 );
+
+ /* pci nsml_agli */
+ for(i = 0; i < 9; i++)
+ pci->nsml_agli.nsml_agl_dsta[i] = getbits(&state, 32 );
+
+ /* pci hli hli_gi */
+ pci->hli.hl_gi.hli_ss = getbits(&state, 16 );
+ pci->hli.hl_gi.hli_s_ptm = getbits(&state, 32 );
+ pci->hli.hl_gi.hli_e_ptm = getbits(&state, 32 );
+ pci->hli.hl_gi.btn_se_e_ptm = getbits(&state, 32 );
+ pci->hli.hl_gi.zero1 = getbits(&state, 2 );
+ pci->hli.hl_gi.btngr_ns = getbits(&state, 2 );
+ pci->hli.hl_gi.zero2 = getbits(&state, 1 );
+ pci->hli.hl_gi.btngr1_dsp_ty = getbits(&state, 3 );
+ pci->hli.hl_gi.zero3 = getbits(&state, 1 );
+ pci->hli.hl_gi.btngr2_dsp_ty = getbits(&state, 3 );
+ pci->hli.hl_gi.zero4 = getbits(&state, 1 );
+ pci->hli.hl_gi.btngr3_dsp_ty = getbits(&state, 3 );
+ pci->hli.hl_gi.btn_ofn = getbits(&state, 8 );
+ pci->hli.hl_gi.btn_ns = getbits(&state, 8 );
+ pci->hli.hl_gi.nsl_btn_ns = getbits(&state, 8 );
+ pci->hli.hl_gi.zero5 = getbits(&state, 8 );
+ pci->hli.hl_gi.fosl_btnn = getbits(&state, 8 );
+ pci->hli.hl_gi.foac_btnn = getbits(&state, 8 );
+
+ /* pci hli btn_colit */
+ for(i = 0; i < 3; i++)
+ for(j = 0; j < 2; j++)
+ pci->hli.btn_colit.btn_coli[i][j] = getbits(&state, 32 );
+
+ /* NOTE: I've had to change the structure from the disk layout to get
+ * the packing to work with Sun's Forte C compiler. */
+
+ /* pci hli btni */
+ for(i = 0; i < 36; i++) {
+ pci->hli.btnit[i].btn_coln = getbits(&state, 2 );
+ pci->hli.btnit[i].x_start = getbits(&state, 10 );
+ pci->hli.btnit[i].zero1 = getbits(&state, 2 );
+ pci->hli.btnit[i].x_end = getbits(&state, 10 );
+
+ pci->hli.btnit[i].auto_action_mode = getbits(&state, 2 );
+ pci->hli.btnit[i].y_start = getbits(&state, 10 );
+ pci->hli.btnit[i].zero2 = getbits(&state, 2 );
+ pci->hli.btnit[i].y_end = getbits(&state, 10 );
+
+ pci->hli.btnit[i].zero3 = getbits(&state, 2 );
+ pci->hli.btnit[i].up = getbits(&state, 6 );
+ pci->hli.btnit[i].zero4 = getbits(&state, 2 );
+ pci->hli.btnit[i].down = getbits(&state, 6 );
+ pci->hli.btnit[i].zero5 = getbits(&state, 2 );
+ pci->hli.btnit[i].left = getbits(&state, 6 );
+ pci->hli.btnit[i].zero6 = getbits(&state, 2 );
+ pci->hli.btnit[i].right = getbits(&state, 6 );
+ /* pci vm_cmd */
+ for(j = 0; j < 8; j++)
+ pci->hli.btnit[i].cmd.bytes[j] = getbits(&state, 8 );
+ }
+
+
+#ifndef NDEBUG
+ /* Asserts */
+
+ /* pci pci gi */
+ CHECK_VALUE(pci->pci_gi.zero1 == 0);
+
+ /* pci hli hli_gi */
+ CHECK_VALUE(pci->hli.hl_gi.zero1 == 0);
+ CHECK_VALUE(pci->hli.hl_gi.zero2 == 0);
+ CHECK_VALUE(pci->hli.hl_gi.zero3 == 0);
+ CHECK_VALUE(pci->hli.hl_gi.zero4 == 0);
+ CHECK_VALUE(pci->hli.hl_gi.zero5 == 0);
+
+ /* Are there buttons defined here? */
+ if((pci->hli.hl_gi.hli_ss & 0x03) != 0) {
+ CHECK_VALUE(pci->hli.hl_gi.btn_ns != 0);
+ CHECK_VALUE(pci->hli.hl_gi.btngr_ns != 0);
+ } else {
+ CHECK_VALUE((pci->hli.hl_gi.btn_ns != 0 && pci->hli.hl_gi.btngr_ns != 0)
+ || (pci->hli.hl_gi.btn_ns == 0 && pci->hli.hl_gi.btngr_ns == 0));
+ }
+
+ /* pci hli btnit */
+ for(i = 0; i < pci->hli.hl_gi.btngr_ns; i++) {
+ for(j = 0; j < (36 / pci->hli.hl_gi.btngr_ns); j++) {
+ int n = (36 / pci->hli.hl_gi.btngr_ns) * i + j;
+ CHECK_VALUE(pci->hli.btnit[n].zero1 == 0);
+ CHECK_VALUE(pci->hli.btnit[n].zero2 == 0);
+ CHECK_VALUE(pci->hli.btnit[n].zero3 == 0);
+ CHECK_VALUE(pci->hli.btnit[n].zero4 == 0);
+ CHECK_VALUE(pci->hli.btnit[n].zero5 == 0);
+ CHECK_VALUE(pci->hli.btnit[n].zero6 == 0);
+
+ if (j < pci->hli.hl_gi.btn_ns) {
+ CHECK_VALUE(pci->hli.btnit[n].x_start <= pci->hli.btnit[n].x_end);
+ CHECK_VALUE(pci->hli.btnit[n].y_start <= pci->hli.btnit[n].y_end);
+ CHECK_VALUE(pci->hli.btnit[n].up <= pci->hli.hl_gi.btn_ns);
+ CHECK_VALUE(pci->hli.btnit[n].down <= pci->hli.hl_gi.btn_ns);
+ CHECK_VALUE(pci->hli.btnit[n].left <= pci->hli.hl_gi.btn_ns);
+ CHECK_VALUE(pci->hli.btnit[n].right <= pci->hli.hl_gi.btn_ns);
+ /* vmcmd_verify(pci->hli.btnit[n].cmd); */
+ } else {
+ int k;
+ CHECK_VALUE(pci->hli.btnit[n].btn_coln == 0);
+ CHECK_VALUE(pci->hli.btnit[n].auto_action_mode == 0);
+ CHECK_VALUE(pci->hli.btnit[n].x_start == 0);
+ CHECK_VALUE(pci->hli.btnit[n].y_start == 0);
+ CHECK_VALUE(pci->hli.btnit[n].x_end == 0);
+ CHECK_VALUE(pci->hli.btnit[n].y_end == 0);
+ CHECK_VALUE(pci->hli.btnit[n].up == 0);
+ CHECK_VALUE(pci->hli.btnit[n].down == 0);
+ CHECK_VALUE(pci->hli.btnit[n].left == 0);
+ CHECK_VALUE(pci->hli.btnit[n].right == 0);
+ for (k = 0; k < 8; k++)
+ CHECK_VALUE(pci->hli.btnit[n].cmd.bytes[k] == 0); /* CHECK_ZERO? */
+ }
+ }
+ }
+#endif /* !NDEBUG */
+}
+
+void navRead_DSI(dsi_t *dsi, unsigned char *buffer) {
+ int i;
+ getbits_state_t state;
+ if (!getbits_init(&state, buffer)) abort(); /* Passed NULL pointers */
+
+ /* dsi dsi gi */
+ dsi->dsi_gi.nv_pck_scr = getbits(&state, 32 );
+ dsi->dsi_gi.nv_pck_lbn = getbits(&state, 32 );
+ dsi->dsi_gi.vobu_ea = getbits(&state, 32 );
+ dsi->dsi_gi.vobu_1stref_ea = getbits(&state, 32 );
+ dsi->dsi_gi.vobu_2ndref_ea = getbits(&state, 32 );
+ dsi->dsi_gi.vobu_3rdref_ea = getbits(&state, 32 );
+ dsi->dsi_gi.vobu_vob_idn = getbits(&state, 16 );
+ dsi->dsi_gi.zero1 = getbits(&state, 8 );
+ dsi->dsi_gi.vobu_c_idn = getbits(&state, 8 );
+ dsi->dsi_gi.c_eltm.hour = getbits(&state, 8 );
+ dsi->dsi_gi.c_eltm.minute = getbits(&state, 8 );
+ dsi->dsi_gi.c_eltm.second = getbits(&state, 8 );
+ dsi->dsi_gi.c_eltm.frame_u = getbits(&state, 8 );
+
+ /* dsi sml pbi */
+ dsi->sml_pbi.category = getbits(&state, 16 );
+ dsi->sml_pbi.ilvu_ea = getbits(&state, 32 );
+ dsi->sml_pbi.ilvu_sa = getbits(&state, 32 );
+ dsi->sml_pbi.size = getbits(&state, 16 );
+ dsi->sml_pbi.vob_v_s_s_ptm = getbits(&state, 32 );
+ dsi->sml_pbi.vob_v_e_e_ptm = getbits(&state, 32 );
+ for(i = 0; i < 8; i++) {
+ dsi->sml_pbi.vob_a[i].stp_ptm1 = getbits(&state, 32 );
+ dsi->sml_pbi.vob_a[i].stp_ptm2 = getbits(&state, 32 );
+ dsi->sml_pbi.vob_a[i].gap_len1 = getbits(&state, 32 );
+ dsi->sml_pbi.vob_a[i].gap_len2 = getbits(&state, 32 );
+ }
+
+ /* dsi sml agli */
+ for(i = 0; i < 9; i++) {
+ dsi->sml_agli.data[ i ].address = getbits(&state, 32 );
+ dsi->sml_agli.data[ i ].size = getbits(&state, 16 );
+ }
+
+ /* dsi vobu sri */
+ dsi->vobu_sri.next_video = getbits(&state, 32 );
+ for(i = 0; i < 19; i++)
+ dsi->vobu_sri.fwda[i] = getbits(&state, 32 );
+ dsi->vobu_sri.next_vobu = getbits(&state, 32 );
+ dsi->vobu_sri.prev_vobu = getbits(&state, 32 );
+ for(i = 0; i < 19; i++)
+ dsi->vobu_sri.bwda[i] = getbits(&state, 32 );
+ dsi->vobu_sri.prev_video = getbits(&state, 32 );
+
+ /* dsi synci */
+ for(i = 0; i < 8; i++)
+ dsi->synci.a_synca[i] = getbits(&state, 16 );
+ for(i = 0; i < 32; i++)
+ dsi->synci.sp_synca[i] = getbits(&state, 32 );
+
+
+ /* Asserts */
+
+ /* dsi dsi gi */
+ CHECK_VALUE(dsi->dsi_gi.zero1 == 0);
+}