diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/include/lhasa | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/include/lhasa')
75 files changed, 13752 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/.travis.yml b/Src/external_dependencies/openmpt-trunk/include/lhasa/.travis.yml new file mode 100644 index 00000000..fff82e6d --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/.travis.yml @@ -0,0 +1,8 @@ +language: c + +compiler: + - clang + - gcc + +script: ./autogen.sh && make && make check + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/AUTHORS b/Src/external_dependencies/openmpt-trunk/include/lhasa/AUTHORS new file mode 100644 index 00000000..ed0ab083 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/AUTHORS @@ -0,0 +1 @@ +Simon Howard <fraggle@gmail.com> diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/COPYING b/Src/external_dependencies/openmpt-trunk/include/lhasa/COPYING new file mode 100644 index 00000000..b682a08e --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/COPYING @@ -0,0 +1,17 @@ + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/ChangeLog b/Src/external_dependencies/openmpt-trunk/include/lhasa/ChangeLog new file mode 100644 index 00000000..c2576d37 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/ChangeLog @@ -0,0 +1,13 @@ + +The full change history is maintained in git. Rather than reproduce +the change history history here, it's easier if you clone a copy of +the git repository and view it: + + git clone git://github.com/fragglet/lhasa.git lhasa + cd lhasa + git log + +Alternatively, the commit history can be viewed on github: + + https://github.com/fragglet/lhasa/commits/master + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/Makefile.am b/Src/external_dependencies/openmpt-trunk/include/lhasa/Makefile.am new file mode 100644 index 00000000..575548c2 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/Makefile.am @@ -0,0 +1,11 @@ + +AUX_DIST_GEN = $(ac_aux_dir) + +EXTRA_DIST = $(AUX_DIST_GEN) gencov rpm.spec +MAINTAINERCLEANFILES = $(AUX_DIST_GEN) + +pkgconfigdir = ${libdir}/pkgconfig +pkgconfig_DATA = liblhasa.pc + +SUBDIRS=doc lib pkg src test + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/NEWS b/Src/external_dependencies/openmpt-trunk/include/lhasa/NEWS new file mode 100644 index 00000000..d7294ed9 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/NEWS @@ -0,0 +1,158 @@ +v0.3.1 (2016-03-29): + + * This release fixes an integer underflow vulnerability in the + code for doing LZH level 3 header decodes (TALOS-CAN-0095). + Thanks go to Marcin Noga and Regina Wilson of Cisco TALOS for + reporting this vulnerability. + +v0.3.0 (2015-04-20): + + * PMarc -pm1- archives that contain truncated compressed data (the + decompressed length is longer than what can be read from the + compressed data) now decompress as intended. Certain archives + in the wild make the assumption that this can be done. + * LArc -lz5- archives that make use of the initial history buffer + now decompress correctly. + * The tests no longer use predictable temporary paths (thanks Jon + Dowland). + * Tests were fixed under OS X. + +v0.2.0 (2013-08-04): + + * Decompression of archives using the -lhx- file format supported by + unlha32.dll is now supported (thanks Multi for the patch). + * The -p (print to stdout) command line option is now supported. + * The test suite should now run correctly on Windows. + + Bugs fixed: + * Bug where archives read from pipes (eg. stdin) were not extracted + beyond the first file in the archive. + * Output when using the -w (extract directory) option now correctly + matches the output of Unix lha. + +v0.1.0 (2013-03-16): + + * There are now test archives for OS-9 and OS-9/68k (OSK) and a + workaround for a bug in the OSK lha tool on this platform. OSK level + 0 extended areas are also supported. + * Extracted files are now written using O_EXCL, which prevents + malicious symbolic links being used to redirect output. + * Directory paths containing '..' as a directory name are now + sanitized, to prevent malicious archives being able to overwrite + arbitrary files on the filesystem. + * Symbolic links are now extracted in a safer way, being created as + dummy files that are overwritten with proper symbolic links at the + end of extraction. This is the same behavior used by GNU tar to + prevent malicious use of symbolic links. + * Automake 1.13 is now properly supported (thanks Jan Engelhardt). + Processing of archives read from IPC pipes (including stdin) has + been fixed. + +v0.0.7 (2012-06-02): + + * Extraction and listing of Unix symbolic links is now supported. + * Decompression code for the "old" PMarc archive algorithm (-pm1-) has + been added. + * Support has been added for Unix LHA level 0 header extended areas + (so level 0 archives with Unix metadata are now listed and extracted + correctly). + * The Unix permissions field in the list output for directory entries + has been fixed. + * The library header files have been fixed so that they can be included + in C++ code. + * The LHADecoder interface, for extracting raw compressed data, has been + added to the public header files. + * The Unix LHA test archives have been regenerated and improved. + * A "ghost testing" tool has been added for testing ghost compression + algorithms such as -pm1-. + * The list output tests have been fixed to be repeatable regardless of + the current date. + * Build of the fuzzer tool has been fixed. + +v0.0.6 (2012-05-17): + + * When the -w option is used during extraction, the path specified + is now first created if it does not already exist. + * The command line tool now exits with a failure return code if an + error occurs during extraction. + * A "catch-all" header file (lhasa.h) has been added. + * The public header files installed with the library can now be + included and used externally. + * A pkgconfig file is now installed as part of the library + (thanks Jan Engelhardt). + * Make targets have been added for building Doxygen documentation + and including them as part of the distribution. + +v0.0.5 (2012-05-08): + + * Architecture-specific functions for running on Windows have now been + fully implemented, and the command line tool passes all tests in the + test suite on Windows (thanks roytam1 for bug reports). + * Bug fixed where the command line tool would enter an infinite loop + when extracting a truncated archive (thanks Jon Dowland). + * Support added for archives with level 0 headers and Unix path + separators (thanks roytam1). + * The test suite now runs correctly outside of the Europe/London time + zone (thanks Thomas Klausner). + * A .spec file is now included for building rpm packages. + +v0.0.4 (2012-05-01): + + * Special handling is now included for MacBinary headers generated + by MacLHA. + * The -w command line option was broken; it has been fixed. + * A bug has been fixed where the timestamp and other metadata was + not set properly for extracted directories. + * Failures to set the UID/GID of extracted files are now ignored, + rather than being treated as a fatal error. + * Self-extracting archive files with long headers (up to 64KiB) + are now supported. This fixes the handling with some Windows + archives. + * A Unix manpage has been added. + * It is now possible to extract an archive from stdin, by using '-' + as the filename. + * The shorthand command line syntax "lha foo.lzh" to list an archive + is now supported. + * A bug with the wildcard pattern matching code has been fixed. + * Proper regression tests have now been added for command line + archive extraction. + * A set of archives generated by LHmelt (Windows) have been added to + the test suite. + * The regression tests for testing file header parsing and CRC checks + have been rewritten. + +v0.0.3 (2012-04-22): + + Third beta release. + + * A fix has been added for a bug where missing parent directories + were not being created properly. + * Regression testing archives have been added from MacLHA v2.24. + * In order to support MacLHA archives, code has been added that + heuristically detects the MacBinary headers added by MacLHA + and strips them off. + +v0.0.2 (2012-04-17): + + Second beta release. + + * This version adds support for level 2 and 3 file headers. Lhasa + should now be capable of decompressing most, if not all archives + found in the wild. + * A fuzz testing framework has been added for testing the + decompression code. A couple of bugs have been fixed as a result + of this. + +v0.0.1 (2012-04-06): + + Initial version. This should be considered beta code, although this + first version should already be capable of extracting the majority of + archive files found in the wild. The main missing features are: + + * Lack of support for level 2 and 3 file headers. + * Inability to create archives (only extract them). + + These are features that I aim to add in future releases. Other future + features can be found in the TODO file. + +# vim: tw=75 diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/OpenMPT.txt b/Src/external_dependencies/openmpt-trunk/include/lhasa/OpenMPT.txt new file mode 100644 index 00000000..65374a14 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/OpenMPT.txt @@ -0,0 +1,10 @@ +lhasa LHA decompression library from https://github.com/fragglet/lhasa tag
+v0.3.1 (commit c6d6ca6df218a54c94443f8d15afe2869461506f).
+The following changes have been made:
+ * commit
+ https://github.com/fragglet/lhasa/commit/59c43fc841bfdba970a810e21d97f5e074cd286e
+ has been applied.
+ * Obviously, unnecessary folders and files have been removed.
+ * The test directory is not included in the OpenMPT repository.
+For building, premake is used to generate Visual Studio project files.
+See ../build/premake/ for details.
diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/README b/Src/external_dependencies/openmpt-trunk/include/lhasa/README new file mode 100644 index 00000000..caeaa6bc --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/README @@ -0,0 +1,17 @@ +Lhasa is a library for parsing LHA (.lzh) archives and a free +replacement for the Unix LHA tool. + +Currently it is only possible to read from (ie. decompress) archives; +generating (compressing) LHA archives may be an enhancement for future +versions. The aim is to be compatible with as many different variants +of the LHA file format as possible, including LArc (.lzs) and PMarc +(.pma). A suite of archives generated from different tools is +included for regression testing. Type 'make check' to run the tests. + +The command line tool aims to be interface-compatible with the +non-free Unix LHA tool (command line syntax and output), for backwards +compatibility with tools that expect particular output. + +Lhasa is licensed under the ISC license, which is a simplified version +of the MIT/X11 license that is functionally identical. + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/TODO b/Src/external_dependencies/openmpt-trunk/include/lhasa/TODO new file mode 100644 index 00000000..7a8e74f7 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/TODO @@ -0,0 +1,23 @@ +Library: + * Better error handling. + * Extract: + * Add options API to control whether permissions, timestamp are set. + * Creation of parent directories on extract (+optional) + * Add LHAFile convenience class. + * Compression and LHA file generation. + * Correctly handle LHmelt backwards directory ordering. + * Add test archives generated by: + * Microsoft LZH folder add-in for Windows (if possible?) + * UNLHA32 + * Decompressors for obscure algorithms: + * -lh2-, -lh3- (experimental LHA?) + * LHark -lh7- (modified -lh5-) + * -lx1- (unlha32 obscure/experimental?) + +Command line tool: + * Create/update/modify archives. + +Testing: + * Valgrind. + * Improve coverage. + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/autogen.sh b/Src/external_dependencies/openmpt-trunk/include/lhasa/autogen.sh new file mode 100644 index 00000000..fb71080a --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/autogen.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +mkdir -p autotools + +aclocal +libtoolize || glibtoolize +autoheader +automake -a +autoconf +automake -a + +./configure $@ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/configure.ac b/Src/external_dependencies/openmpt-trunk/include/lhasa/configure.ac new file mode 100644 index 00000000..5bf4c002 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/configure.ac @@ -0,0 +1,88 @@ +AC_INIT(Lhasa, 0.3.1, fraggle@gmail.com, lhasa) +AC_CONFIG_AUX_DIR(autotools) + +AM_INIT_AUTOMAKE([no-define]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +AM_PROG_CC_C_O + +AC_PROG_CXX +AC_PROG_RANLIB +AC_PROG_LIBTOOL +AC_PROG_INSTALL +AC_PROG_MAKE_SET + +if [[ "$GCC" = "yes" ]]; then + is_gcc=true +else + is_gcc=false +fi + +TEST_CFLAGS="-DTEST_BUILD" + +# Turn on all warnings for gcc. Turn off optimisation for the test build. + +if $is_gcc; then + WARNINGS="-Wall -Wsign-compare" + CFLAGS="$CFLAGS $WARNINGS" + TEST_CFLAGS="$TEST_CFLAGS $WARNINGS -O0" +fi + +# Support for coverage analysis via gcov: + +coverage=false +AC_ARG_ENABLE(coverage, +[ --enable-coverage Enable coverage testing. ], +[ coverage=true ]) + +if $coverage; then + if $is_gcc; then + TEST_CFLAGS="$TEST_CFLAGS -fprofile-arcs -ftest-coverage" + else + AC_MSG_ERROR([Can only enable coverage when using gcc.]) + fi +fi + +AM_CONDITIONAL(BUILD_COVERAGE, $coverage) + +# Support for running test cases using valgrind: + +use_valgrind=false +AC_ARG_ENABLE(valgrind, +[ --enable-valgrind Use valgrind when running unit tests. ], +[ use_valgrind=true ]) + +if [[ "$use_valgrind" = "true" ]]; then + AC_CHECK_PROG(HAVE_VALGRIND, valgrind, yes, no) + + if [[ "$HAVE_VALGRIND" = "no" ]]; then + AC_MSG_ERROR([Valgrind not found in PATH. ]) + fi +fi + +AM_CONDITIONAL(USE_VALGRIND, $use_valgrind) + +# Save the default CFLAGS and clear them, so that the test build +# of the library doesn't get the optimisation flags. + +MAIN_CFLAGS="$CFLAGS" +CFLAGS="" + +AC_SUBST(MAIN_CFLAGS) +AC_SUBST(TEST_CFLAGS) +AC_SUBST(ac_aux_dir) + +AC_CONFIG_HEADERS([config.h:config.hin]) + +AC_OUTPUT([ + liblhasa.pc + rpm.spec + Makefile + doc/Makefile + lib/Makefile + lib/public/Makefile + pkg/Makefile + pkg/config.make + src/Makefile + test/Makefile +]) + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/doc/Doxyfile b/Src/external_dependencies/openmpt-trunk/include/lhasa/doc/Doxyfile new file mode 100644 index 00000000..2eea4876 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/doc/Doxyfile @@ -0,0 +1,1360 @@ +# Doxyfile 1.5.5 + +# 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 = "Lhasa" + +# 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 = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = . + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, 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 = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = lib/public/ + +# 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 = YES + +# 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 + +# 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 = YES + +# 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 = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = 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 + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <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 = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = intro.h \ + ../lib/public + +# 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 = *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# 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 (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# 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 = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a 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 BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# 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 = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# 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 MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# 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 = 1000 + +# 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 = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/doc/Makefile.am b/Src/external_dependencies/openmpt-trunk/include/lhasa/doc/Makefile.am new file mode 100644 index 00000000..7a9bdc9b --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/doc/Makefile.am @@ -0,0 +1,7 @@ + +man_MANS=lha.1 +EXTRA_DIST=$(man_MANS) html Doxyfile intro.h + +html: + doxygen + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/doc/intro.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/doc/intro.h new file mode 100644 index 00000000..2c201345 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/doc/intro.h @@ -0,0 +1,54 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +/* This file is a dummy header used to generate the Doxygen intro. */ + +/** + * @mainpage Lhasa LZH file library + * + * @section Introduction + * + * Lhasa is a library for parsing LHA (.lzh) archive files. Included + * with the library is a Unix command line tool that is an interface + * compatible replacement for the non-free Unix LHA tool. + * + * The source code is licensed under the ISC license, a simplified + * version of the MIT/X11 license which is functionally identical, + * and compatible with the GNU GPL. + * As such, it may be reused in any project, either proprietary or + * open source. + * + * @section Main_interfaces Main interfaces + * + * @li @link lha_input_stream.h @endlink - abstracts + * the process of reading data from an LZH file; convenience + * functions are provided for reading data from a normal file or + * a Standard C FILE pointer. + * @li @link lha_reader.h @endlink - routines to decode ands + * extract the contents of an LZH file from a stream. + * @li @link lha_file_header.h @endlink - structure + * representing the decoded contents of an LZH file header. + * + * @section Additional_interfaces Additional interfaces + * + * @li @link lha_decoder.h @endlink - routines to decode raw LZH + * compressed data. + */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/doc/lha.1 b/Src/external_dependencies/openmpt-trunk/include/lhasa/doc/lha.1 new file mode 100644 index 00000000..3f63c0ef --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/doc/lha.1 @@ -0,0 +1,116 @@ +.TH lha 1 +.SH NAME +lha \- compression tool for .lzh archive files. +.SH SYNOPSIS +.B lha [-]{lvtxe[q{num}][finv]}[w=<dir>] archive_file [file...] +.SH DESCRIPTION +.PP +.B lha +is a tool for extracting .lzh archive files. It also supports variants +of the .lzh archive, such as .lzs and .pma. +.PP +This version of the lha tool is part of Lhasa, a free implementation +of the .lzh format. +.PP +.SH COMMAND SYNTAX +The lha tool has an unusual command syntax, compared to most other +Unix commands. The first parameter to the program specifies the command +to perform and all additional options. The second parameter specifies +the path to the archive file to operate on. Following this is a list +of wildcard patterns to match against the filenames of the archived +files. +.PP +The first character of the command parameter specifies the command to +perform, which is one of the following: +.TP +\fB-l\fR +List contents of the specified archive. +.TP +\fB-v\fR +Verbosely list contents of the specified archive. +.TP +\fB-t\fR +Test the integrity of the specified archive: decompress its contents and +check the CRC. +.TP +\fB-e\fR or \fB-x\fR +Extract archive. Files are extracted to the current working directory +unless the 'w' option is specified. +.PP +.SH OPTIONS +The remainder of the command parameter is used to specify additional +options: +.TP +\fBq[012]\fR +Quiet mode. Higher numbers suppress more output. Level 0 is normal +operation. If no number is specified, full suppression (level 2) +is used. The quiet option also turns on the force overwrite option +('f'). +.TP +\fBf\fR +Force overwrite of existing files: do not prompt. +.TP +\fBi\fR +Ignore paths of archived files: extract all archived files to the +same directory, ignoring subdirectories. +.TP +\fBn\fR +Do not perform any actual operations: instead, perform a dry run of +the requested operation and describe what would have been done on +standard output. +.TP +\fBv\fR +Verbose mode: causes extra information to be written to standard +output. +.TP +\fBw=dir\fR +Specify destination directory for extracting files. This must be +the last option of the first parameter. +.SH SEE ALSO +\fBunzip\fR(1), +\fBtar\fR(1), +\fBgzip\fR(1), +\fBbzip2\fR(1) +.SH HISTORY +The .lzh format originated with Kazuhiko Miki's MS\-DOS archive tool, +LArc, using the LZSS algorithm developed by Haruhiko Okumura, and +the .lzs filename extension. The container format was reused for +LHarc, by Haruyasu Yoshizaki (Yoshi), which used a new algorithm +named LZHUF and the .lzh extension. In later versions, LHarc was +renamed to LHA and extended with more effective compression algorithms. +.PP +Versions of the LHA tool were later ported to various different +operating systems, including the Amiga, Atari, MacOS, OS/2 and Unix. +A tool for MSX\-DOS named PMarc reused the container format with a new +compression algorithm (.pma extension). +.PP +The Unix version of the tool was developed by Masaru Oki, Nobutaka +Watazaki and Tsugio Okamoto, but was released under a software +license that does not conform to the Free Software or Open Source +Definitions. Lhasa was developed as a drop\-in replacement that is +Free Software and Open Source. +.SH BUGS +The current version does not allow the creation of new archive files. +.PP +Some obscure compression algorithms are not currently supported +(\-lh2\-, \-lh3\-, \-lx1\-, \-lhx\-). +.SH AUTHOR +Lhasa was written and is maintained by Simon Howard. +.SH COPYRIGHT +Copyright \(co 2011, 2012 Simon Howard. +.PP +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. +.PP +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/gencov b/Src/external_dependencies/openmpt-trunk/include/lhasa/gencov new file mode 100644 index 00000000..f6219b29 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/gencov @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# +# Copyright (c) 2011, 2012, Simon Howard +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +from re import match +from os import rename, popen, getcwd, chdir +from os.path import exists, dirname, basename +from os.path import join as path_join +from glob import glob +import sys + +def parse_stats(text): + m = match(r"(.*)% of (.*)", text) + + pct = m.group(1) + lines = int(m.group(2)) + cov_lines = int(float(pct) * lines / 100 + 0.5) + uncov_lines = lines - cov_lines + + return (pct + "%", uncov_lines, cov_lines, lines) + + +def gcov(filename): + + # gcov must be run from within the same directory as the source + # file that we are analysing. + + old_wd = getcwd() + src_dir = dirname(filename) + chdir(src_dir) + s = popen("gcov %s" % basename(filename)) + chdir(old_wd) + + # Process output from gcov. + # This may cover multiple files when one .c file includes another. + + results = {} + filename = None + + # File 'lha_file_header.c' + # Lines executed:88.97% of 136 + + for line in s: + m = match(r"File '(.*)'", line) + if m: + filename = m.group(1) + + m = match(r"Lines executed:(.*)", line) + if m: + full_path = path_join(src_dir, filename) + results[full_path] = parse_stats(m.group(1)) + + s.close() + + return results + +def format_output(filename, stats): + print " %-35s%7s%7s%7s%7s" % ((filename, ) + stats) + +print +format_output("Filename", ("Percent", "Uncov", "Cov", "Total")) +print " " + ("-" * 65) + +for filename in sorted(sys.argv[1:]): + gcno = filename.replace(".c", ".gcno") + gcda = filename.replace(".c", ".gcda") + + xgcno = glob(path_join(dirname(gcno), "*-" + basename(gcno))) + xgcda = glob(path_join(dirname(gcda), "*-" + basename(gcda))) + + # Must rename eg. liblhasatest_a-foo.gcno to foo.gcno: + + if len(xgcno) > 0: + rename(xgcno[0], gcno) + + if len(xgcda) > 0: + rename(xgcda[0], gcda) + + if not exists(gcno) or not exists(gcda): + continue + + # Run gcov and parse output: + + results = gcov(filename) + + if len(results) > 0: + if filename in results: + format_output(filename, results[filename]) + else: + format_output(filename, ("", "", "", "")) + + for subfile in sorted(results.keys()): + if subfile.endswith(".h"): + continue + + if subfile != filename: + format_output(" -> " + subfile, results[subfile]) + +print + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/Makefile.am b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/Makefile.am new file mode 100644 index 00000000..e70de16b --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/Makefile.am @@ -0,0 +1,43 @@ +SUBDIRS = public + +lib_LTLIBRARIES=liblhasa.la +check_LIBRARIES=liblhasatest.a + +EXTRA_DIST = \ + bit_stream_reader.c \ + lh_new_decoder.c \ + pma_common.c \ + tree_decode.c + +SRC = \ + crc16.c crc16.h \ + ext_header.c ext_header.h \ + lha_arch_unix.c lha_arch.h \ + lha_arch_win32.c \ + lha_decoder.c lha_decoder.h \ + lha_endian.c lha_endian.h \ + lha_file_header.c lha_file_header.h \ + lha_input_stream.c lha_input_stream.h \ + lha_basic_reader.c lha_basic_reader.h \ + lha_reader.c \ + macbinary.c macbinary.h \ + null_decoder.c \ + lh1_decoder.c \ + lh5_decoder.c \ + lh6_decoder.c \ + lh7_decoder.c \ + lhx_decoder.c \ + lz5_decoder.c \ + lzs_decoder.c \ + pm1_decoder.c \ + pm2_decoder.c + +liblhasatest_a_CFLAGS=$(TEST_CFLAGS) -DALLOC_TESTING -I../test -g +liblhasatest_a_SOURCES=$(SRC) $(HEADER_FILES) + +liblhasa_la_CFLAGS=$(MAIN_CFLAGS) +liblhasa_la_SOURCES=$(SRC) $(HEADER_FILES) + +clean-local: + rm -f *.gcno *.gcda *.c.gcov + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/bit_stream_reader.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/bit_stream_reader.c new file mode 100644 index 00000000..6f9edd41 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/bit_stream_reader.c @@ -0,0 +1,128 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// +// Data structure used to read bits from an input source as a stream. +// +// This file is designed to be #included by other source files to +// make a complete decoder. +// + +typedef struct { + + // Callback function to invoke to read more data from the + // input stream. + + LHADecoderCallback callback; + void *callback_data; + + // Bits from the input stream that are waiting to be read. + + uint32_t bit_buffer; + unsigned int bits; + +} BitStreamReader; + +// Initialize bit stream reader structure. + +static void bit_stream_reader_init(BitStreamReader *reader, + LHADecoderCallback callback, + void *callback_data) +{ + reader->callback = callback; + reader->callback_data = callback_data; + + reader->bits = 0; + reader->bit_buffer = 0; +} + +// Return the next n bits waiting to be read from the input stream, +// without removing any. Returns -1 for failure. + +static int peek_bits(BitStreamReader *reader, + unsigned int n) +{ + uint8_t buf[4]; + unsigned int fill_bytes; + size_t bytes; + + if (n == 0) { + return 0; + } + + // If there are not enough bits in the buffer to satisfy this + // request, we need to fill up the buffer with more bits. + + while (reader->bits < n) { + + // Maximum number of bytes we can fill? + + fill_bytes = (32 - reader->bits) / 8; + + // Read from input and fill bit_buffer. + + memset(buf, 0, sizeof(buf)); + bytes = reader->callback(buf, fill_bytes, + reader->callback_data); + + // End of file? + + if (bytes == 0) { + return -1; + } + + reader->bit_buffer |= (uint32_t) buf[0] << (24 - reader->bits); + reader->bit_buffer |= (uint32_t) buf[1] << (16 - reader->bits); + reader->bit_buffer |= (uint32_t) buf[2] << (8 - reader->bits); + reader->bit_buffer |= (uint32_t) buf[3]; + + reader->bits += bytes * 8; + } + + return (signed int) (reader->bit_buffer >> (32 - n)); +} + +// Read a bit from the input stream. +// Returns -1 for failure. + +static int read_bits(BitStreamReader *reader, + unsigned int n) +{ + int result; + + result = peek_bits(reader, n); + + if (result >= 0) { + reader->bit_buffer <<= n; + reader->bits -= n; + } + + return result; +} + + +// Read a bit from the input stream. +// Returns -1 for failure. + +static int read_bit(BitStreamReader *reader) +{ + return read_bits(reader, 1); +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/crc16.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/crc16.c new file mode 100644 index 00000000..0a385e90 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/crc16.c @@ -0,0 +1,73 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include "crc16.h" + +static unsigned int crc16_table[] = { + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, + 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, + 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, + 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, + 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, + 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, + 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, + 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, + 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, + 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, + 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, + 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, + 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, + 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, + 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, + 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, + 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, + 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, + 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, + 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, + 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, + 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, + 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, + 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, + 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, + 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, + 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, + 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, + 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, + 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, + 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, + 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 +}; + +void lha_crc16_buf(uint16_t *crc, uint8_t *buf, size_t buf_len) +{ + uint16_t tmp; + unsigned int index; + unsigned int i; + + tmp = *crc; + + for (i = 0; i < buf_len; ++i) { + index = (tmp ^ buf[i]) & 0xff; + tmp = ((tmp >> 8) ^ crc16_table[index]) & 0xffff; + } + + *crc = tmp; +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/crc16.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/crc16.h new file mode 100644 index 00000000..723decd1 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/crc16.h @@ -0,0 +1,30 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_LHA_CRC16_H +#define LHASA_LHA_CRC16_H + +#include <inttypes.h> +#include <stdlib.h> + +void lha_crc16_buf(uint16_t *crc, uint8_t *buf, size_t buf_len); + +#endif /* #ifndef LHASA_LHA_CRC16_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/ext_header.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/ext_header.c new file mode 100644 index 00000000..e7e92bd5 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/ext_header.c @@ -0,0 +1,437 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include <stdlib.h> +#include <string.h> + +#include "ext_header.h" +#include "lha_endian.h" + +// +// Extended header parsing. +// +// Extended headers were introduced with LHA v2 - various different +// tools support different extended headers. Some are operating system +// specific. +// + +// Extended header types: + +#define LHA_EXT_HEADER_COMMON 0x00 +#define LHA_EXT_HEADER_FILENAME 0x01 +#define LHA_EXT_HEADER_PATH 0x02 +#define LHA_EXT_HEADER_MULTI_DISC 0x39 +#define LHA_EXT_HEADER_COMMENT 0x3f + +#define LHA_EXT_HEADER_WINDOWS_TIMESTAMPS 0x41 + +#define LHA_EXT_HEADER_UNIX_PERMISSION 0x50 +#define LHA_EXT_HEADER_UNIX_UID_GID 0x51 +#define LHA_EXT_HEADER_UNIX_GROUP 0x52 +#define LHA_EXT_HEADER_UNIX_USER 0x53 +#define LHA_EXT_HEADER_UNIX_TIMESTAMP 0x54 + +#define LHA_EXT_HEADER_OS9 0xcc + +/** + * Structure representing an extended header type. + */ +typedef struct { + + /** + * Header number. + * + * Each extended header type has a unique byte value that represents + * it. + */ + uint8_t num; + + /** + * Callback function for parsing an extended header block. + * + * @param header The file header structure in which to store + * decoded data. + * @param data Pointer to the header data to decode. + * @param data_len Size of the header data, in bytes. + * @return Non-zero if successful, or zero for failure. + */ + int (*decoder)(LHAFileHeader *header, uint8_t *data, size_t data_len); + + /** Minimum length for a header of this type. */ + size_t min_len; + +} LHAExtHeaderType; + +// Common header (0x00). +// +// This contains a 16-bit CRC of the entire LHA header. + +static int ext_header_common_decoder(LHAFileHeader *header, + uint8_t *data, + size_t data_len) +{ + header->extra_flags |= LHA_FILE_COMMON_CRC; + header->common_crc = lha_decode_uint16(data); + + // There is a catch-22 in calculating the CRC, because the field + // containing the CRC is part of the data being CRC'd. The solution + // is that the CRC is calculated with the CRC field set to zero. + // Therefore, now that the CRC has been read, set the field to + // zero in the raw_data array so that the CRC can be calculated + // correctly. + + data[0] = 0x00; + data[1] = 0x00; + + // TODO: Some platforms (OS/2, Unix) put extra data in the common + // header which might also be decoded. + + return 1; +} + +static LHAExtHeaderType lha_ext_header_common = { + LHA_EXT_HEADER_COMMON, + ext_header_common_decoder, + 2 +}; + +// Filename header (0x01). +// +// This stores the filename for the file. This is essential on level 2/3 +// headers, as the filename field is no longer part of the standard +// header. + +static int ext_header_filename_decoder(LHAFileHeader *header, + uint8_t *data, + size_t data_len) +{ + char *new_filename; + unsigned int i; + + new_filename = malloc(data_len + 1); + + if (new_filename == NULL) { + return 0; + } + + memcpy(new_filename, data, data_len); + new_filename[data_len] = '\0'; + + // Sanitize the filename that was read. It is not allowed to + // contain a path separator, which could potentially be used + // to do something malicious. + + for (i = 0; new_filename[i] != '\0'; ++i) { + if (new_filename[i] == '/') { + new_filename[i] = '_'; + } + } + + free(header->filename); + header->filename = new_filename; + + return 1; +} + +static LHAExtHeaderType lha_ext_header_filename = { + LHA_EXT_HEADER_FILENAME, + ext_header_filename_decoder, + 1 +}; + +// Path header (0x02). +// +// This stores the directory path of the file. A value of 0xff is used +// as the path separator. It is supposed to include a terminating path +// separator as the last character. + +static int ext_header_path_decoder(LHAFileHeader *header, + uint8_t *data, + size_t data_len) +{ + unsigned int i; + uint8_t *new_path; + + new_path = malloc(data_len + 2); + + if (new_path == NULL) { + return 0; + } + + memcpy(new_path, data, data_len); + new_path[data_len] = '\0'; + + // Amiga LHA v1.22 generates path headers without a path + // separator at the end of the string. This is broken (and + // was fixed in a later version), but handle it correctly. + + if (new_path[data_len - 1] != 0xff) { + new_path[data_len] = 0xff; + new_path[data_len + 1] = '\0'; + ++data_len; + } + + free(header->path); + header->path = (char *) new_path; + + for (i = 0; i < data_len; ++i) { + if (new_path[i] == 0xff) { + new_path[i] = '/'; + } + } + + return 1; +} + +static LHAExtHeaderType lha_ext_header_path = { + LHA_EXT_HEADER_PATH, + ext_header_path_decoder, + 1 +}; + +// Windows timestamp header (0x41). +// +// This is a Windows-specific header that stores 64-bit timestamps in +// Windows FILETIME format. The timestamps have 100ns accuracy, which is +// much more accurate than the normal Unix time_t format. + +static int ext_header_windows_timestamps(LHAFileHeader *header, + uint8_t *data, + size_t data_len) +{ + header->extra_flags |= LHA_FILE_WINDOWS_TIMESTAMPS; + header->win_creation_time = lha_decode_uint64(data); + header->win_modification_time = lha_decode_uint64(data + 8); + header->win_access_time = lha_decode_uint64(data + 16); + + return 1; +} + +static LHAExtHeaderType lha_ext_header_windows_timestamps = { + LHA_EXT_HEADER_WINDOWS_TIMESTAMPS, + ext_header_windows_timestamps, + 24 +}; + + +// Unix permissions header (0x50). + +static int ext_header_unix_perms_decoder(LHAFileHeader *header, + uint8_t *data, + size_t data_len) +{ + header->extra_flags |= LHA_FILE_UNIX_PERMS; + header->unix_perms = lha_decode_uint16(data); + + return 1; +} + +static LHAExtHeaderType lha_ext_header_unix_perms = { + LHA_EXT_HEADER_UNIX_PERMISSION, + ext_header_unix_perms_decoder, + 2 +}; + +// Unix UID/GID header (0x51). + +static int ext_header_unix_uid_gid_decoder(LHAFileHeader *header, + uint8_t *data, + size_t data_len) +{ + header->extra_flags |= LHA_FILE_UNIX_UID_GID; + header->unix_gid = lha_decode_uint16(data); + header->unix_uid = lha_decode_uint16(data + 2); + + return 1; +} + +static LHAExtHeaderType lha_ext_header_unix_uid_gid = { + LHA_EXT_HEADER_UNIX_UID_GID, + ext_header_unix_uid_gid_decoder, + 4 +}; + +// Unix username header (0x53). +// +// This stores a string containing the username. There don't seem to be +// any tools that actually generate archives containing this header. + +static int ext_header_unix_username_decoder(LHAFileHeader *header, + uint8_t *data, + size_t data_len) +{ + char *username; + + username = malloc(data_len + 1); + + if (username == NULL) { + return 0; + } + + memcpy(username, data, data_len); + username[data_len] = '\0'; + + free(header->unix_username); + header->unix_username = username; + + return 1; +} + +static LHAExtHeaderType lha_ext_header_unix_username = { + LHA_EXT_HEADER_UNIX_USER, + ext_header_unix_username_decoder, + 1 +}; + +// Unix group header (0x52). +// +// This stores a string containing the Unix group name. As with the +// username header, there don't seem to be any tools that actually +// generate archives containing this header. + +static int ext_header_unix_group_decoder(LHAFileHeader *header, + uint8_t *data, + size_t data_len) +{ + char *group; + + group = malloc(data_len + 1); + + if (group == NULL) { + return 0; + } + + memcpy(group, data, data_len); + group[data_len] = '\0'; + + free(header->unix_group); + header->unix_group = group; + + return 1; +} + +static LHAExtHeaderType lha_ext_header_unix_group = { + LHA_EXT_HEADER_UNIX_GROUP, + ext_header_unix_group_decoder, + 1 +}; + +// Unix timestamp header (0x54). +// +// This stores a 32-bit Unix time_t timestamp representing the +// modification time of the file. + +static int ext_header_unix_timestamp_decoder(LHAFileHeader *header, + uint8_t *data, + size_t data_len) +{ + header->timestamp = lha_decode_uint32(data); + + return 1; +} + +static LHAExtHeaderType lha_ext_header_unix_timestamp = { + LHA_EXT_HEADER_UNIX_TIMESTAMP, + ext_header_unix_timestamp_decoder, + 4 +}; + +// OS-9 (6809) header (0xcc) +// +// This stores OS-9 filesystem metadata. + +static int ext_header_os9_decoder(LHAFileHeader *header, + uint8_t *data, + size_t data_len) +{ + // TODO: The OS-9 extended header contains various data, but + // it's not clear what it's all for. Just extract the + // permissions for now. + + header->os9_perms = lha_decode_uint16(data + 7); + header->extra_flags |= LHA_FILE_OS9_PERMS; + + return 1; +} + +static LHAExtHeaderType lha_ext_header_os9 = { + LHA_EXT_HEADER_OS9, + ext_header_os9_decoder, + 12 +}; + +// Table of extended headers. + +static const LHAExtHeaderType *ext_header_types[] = { + &lha_ext_header_common, + &lha_ext_header_filename, + &lha_ext_header_path, + &lha_ext_header_unix_perms, + &lha_ext_header_unix_uid_gid, + &lha_ext_header_unix_username, + &lha_ext_header_unix_group, + &lha_ext_header_unix_timestamp, + &lha_ext_header_windows_timestamps, + &lha_ext_header_os9, +}; + +#define NUM_HEADER_TYPES (sizeof(ext_header_types) / sizeof(*ext_header_types)) + +/** + * Look up the extended header parser for the specified header code. + * + * @param num Extended header type. + * @return Matching @ref LHAExtHeaderType structure, or NULL if + * not found for this header type. + */ + +static const LHAExtHeaderType *ext_header_for_num(uint8_t num) +{ + unsigned int i; + + for (i = 0; i < NUM_HEADER_TYPES; ++i) { + if (ext_header_types[i]->num == num) { + return ext_header_types[i]; + } + } + + return NULL; +} + +int lha_ext_header_decode(LHAFileHeader *header, + uint8_t num, + uint8_t *data, + size_t data_len) +{ + const LHAExtHeaderType *htype; + + htype = ext_header_for_num(num); + + if (htype == NULL) { + return 0; + } + + if (data_len < htype->min_len) { + return 0; + } + + return htype->decoder(header, data, data_len); +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/ext_header.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/ext_header.h new file mode 100644 index 00000000..5484f260 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/ext_header.h @@ -0,0 +1,44 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_EXT_HEADER_H +#define LHASA_EXT_HEADER_H + +#include <stdlib.h> + +#include "lha_file_header.h" + +/** + * Decode the specified extended header. + * + * @param header The file header in which to store decoded data. + * @param num Extended header type. + * @param data Pointer to the data to decode. + * @param data_len Size of the data to decode, in bytes. + * @return Non-zero for success, or zero if not decoded. + */ + +int lha_ext_header_decode(LHAFileHeader *header, + uint8_t num, + uint8_t *data, + size_t data_len); + +#endif /* #ifndef LHASA_EXT_HEADER_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh1_decoder.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh1_decoder.c new file mode 100644 index 00000000..2188f122 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh1_decoder.c @@ -0,0 +1,724 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> + +#include "lha_decoder.h" + +#include "bit_stream_reader.c" + +// Size of the ring buffer used to hold history: + +#define RING_BUFFER_SIZE 4096 /* bytes */ + +// When this limit is reached, the code tree is reordered. + +#define TREE_REORDER_LIMIT 32 * 1024 /* 32 kB */ + +// Number of codes ('byte' codes + 'copy' codes): + +#define NUM_CODES 314 + +// Number of nodes in the code tree. + +#define NUM_TREE_NODES (NUM_CODES * 2 - 1) + +// Number of possible offsets: + +#define NUM_OFFSETS 64 + +// Minimum length of the offset top bits: + +#define MIN_OFFSET_LENGTH 3 /* bits */ + +// Threshold for copying. The first copy code starts from here. + +#define COPY_THRESHOLD 3 /* bytes */ + +// Required size of the output buffer. At most, a single call to read() +// might result in a copy of the entire ring buffer. + +#define OUTPUT_BUFFER_SIZE RING_BUFFER_SIZE + +typedef struct { + + // If true, this node is a leaf node. + + unsigned int leaf :1; + + // If this is a leaf node, child_index is the code represented by + // this node. Otherwise, nodes[child_index] and nodes[child_index-1] + // are the children of this node. + + unsigned int child_index :15; + + // Index of the parent node of this node. + + uint16_t parent; + + // Frequency count for this node - number of times that it has + // received a hit. + + uint16_t freq; + + // Group that this node belongs to. + + uint16_t group; +} Node; + +typedef struct { + + // Input bit stream. + + BitStreamReader bit_stream_reader; + + // Ring buffer of past data. Used for position-based copies. + + uint8_t ringbuf[RING_BUFFER_SIZE]; + unsigned int ringbuf_pos; + + // Array of tree nodes. nodes[0] is the root node. The array + // is maintained in order by frequency. + + Node nodes[NUM_TREE_NODES]; + + // Indices of leaf nodes of the tree (map from code to leaf + // node index) + + uint16_t leaf_nodes[NUM_CODES]; + + // Groups list. Every node belongs to a group. All nodes within + // a group have the same frequency. There can be at most + // NUM_TREE_NODES groups (one for each node). num_groups is used + // to allocate and deallocate groups as needed. + + uint16_t groups[NUM_TREE_NODES]; + unsigned int num_groups; + + // Index of the "leader" of a group within the nodes[] array. + // The leader is the left-most node within a span of nodes with + // the same frequency. + + uint16_t group_leader[NUM_TREE_NODES]; + + // Offset lookup table. Maps from a byte value (sequence of next + // 8 bits from input stream) to an offset value. + + uint8_t offset_lookup[256]; + + // Length of offsets, in bits. + + uint8_t offset_lengths[NUM_OFFSETS]; +} LHALH1Decoder; + +// Frequency distribution used to calculate the offset codes. + +static const unsigned int offset_fdist[] = { + 1, // 3 bits + 3, // 4 bits + 8, // 5 bits + 12, // 6 bits + 24, // 7 bits + 16, // 8 bits +}; + +// Allocate a group from the free groups array. + +static uint16_t alloc_group(LHALH1Decoder *decoder) +{ + uint16_t result; + + result = decoder->groups[decoder->num_groups]; + ++decoder->num_groups; + + return result; +} + +// Free a group that is no longer in use. + +static void free_group(LHALH1Decoder *decoder, uint16_t group) +{ + --decoder->num_groups; + decoder->groups[decoder->num_groups] = group; +} + +// Initialize groups array. + +static void init_groups(LHALH1Decoder *decoder) +{ + unsigned int i; + + for (i = 0; i < NUM_TREE_NODES; ++i) { + decoder->groups[i] = (uint16_t) i; + } + + decoder->num_groups = 0; +} + +// Initialize the tree with its basic initial configuration. + +static void init_tree(LHALH1Decoder *decoder) +{ + unsigned int i, child; + int node_index; + uint16_t leaf_group; + Node *node; + + // Leaf nodes are placed at the end of the table. Start by + // initializing these, and working backwards. + + node_index = NUM_TREE_NODES - 1; + leaf_group = alloc_group(decoder); + + for (i = 0; i < NUM_CODES; ++i) { + node = &decoder->nodes[node_index]; + node->leaf = 1; + node->child_index = (unsigned short) i; + node->freq = 1; + node->group = leaf_group; + + decoder->group_leader[leaf_group] = (uint16_t) node_index; + decoder->leaf_nodes[i] = (uint16_t) node_index; + + --node_index; + } + + // Now build up the intermediate nodes, up to the root. Each + // node gets two nodes as children. + + child = NUM_TREE_NODES - 1; + + while (node_index >= 0) { + node = &decoder->nodes[node_index]; + node->leaf = 0; + + // Set child pointer and update the parent pointers of the + // children. + + node->child_index = child; + decoder->nodes[child].parent = (uint16_t) node_index; + decoder->nodes[child - 1].parent = (uint16_t) node_index; + + // The node's frequency is equal to the sum of the frequencies + // of its children. + + node->freq = (uint16_t) (decoder->nodes[child].freq + + decoder->nodes[child - 1].freq); + + // Is the frequency the same as the last node we processed? + // if so, we are in the same group. If not, we must + // allocate a new group. Either way, this node is now the + // leader of its group. + + if (node->freq == decoder->nodes[node_index + 1].freq) { + node->group = decoder->nodes[node_index + 1].group; + } else { + node->group = alloc_group(decoder); + } + + decoder->group_leader[node->group] = (uint16_t) node_index; + + // Process next node. + + --node_index; + child -= 2; + } +} + +// Fill in a range of values in the offset_lookup table, which have +// the bits from 'code' as the high bits, and the low bits can be +// any values in the range from 'mask'. Set these values to point +// to 'offset'. + +static void fill_offset_range(LHALH1Decoder *decoder, uint8_t code, + unsigned int mask, unsigned int offset) +{ + unsigned int i; + + // Set offset lookup table to map from all possible input values + // that fit within the mask to the target offset. + + for (i = 0; (i & ~mask) == 0; ++i) { + decoder->offset_lookup[code | i] = (uint8_t) offset; + } +} + +// Calculate the values for the offset_lookup and offset_lengths +// tables. + +static void init_offset_table(LHALH1Decoder *decoder) +{ + unsigned int i, j, len; + uint8_t code, iterbit, offset; + + code = 0; + offset = 0; + + // Iterate through each entry in the frequency distribution table, + // filling in codes in the lookup table as we go. + + for (i = 0; i < sizeof(offset_fdist) / sizeof(*offset_fdist); ++i) { + + // offset_fdist[0] is the number of codes of length + // MIN_OFFSET_LENGTH bits, increasing as we go. As the + // code increases in length, we must iterate progressively + // lower bits in the code (moving right - extending the + // code to be 1 bit longer). + + len = i + MIN_OFFSET_LENGTH; + iterbit = (uint8_t) (1 << (8 - len)); + + for (j = 0; j < offset_fdist[i]; ++j) { + + // Store lookup values for this offset in the + // lookup table, and save the code length. + // (iterbit - 1) turns into a mask for the lower + // bits that are not part of the code. + + fill_offset_range(decoder, code, + (uint8_t) (iterbit - 1), offset); + decoder->offset_lengths[offset] = (uint8_t) len; + + // Iterate to next code. + + code = (uint8_t) (code + iterbit); + ++offset; + } + } +} + +// Initialize the history ring buffer. + +static void init_ring_buffer(LHALH1Decoder *decoder) +{ + memset(decoder->ringbuf, ' ', RING_BUFFER_SIZE); + decoder->ringbuf_pos = 0; +} + +static int lha_lh1_init(void *data, LHADecoderCallback callback, + void *callback_data) +{ + LHALH1Decoder *decoder = data; + + // Initialize input stream reader. + + bit_stream_reader_init(&decoder->bit_stream_reader, + callback, callback_data); + + // Initialize data structures. + + init_groups(decoder); + init_tree(decoder); + init_offset_table(decoder); + init_ring_buffer(decoder); + + return 1; +} + +// Make the given node the leader of its group: swap it with the current +// leader so that it is in the left-most position. Returns the new index +// of the node. + +static uint16_t make_group_leader(LHALH1Decoder *decoder, + uint16_t node_index) +{ + Node *node, *leader; + uint16_t group; + uint16_t leader_index; + unsigned int tmp; + + group = decoder->nodes[node_index].group; + leader_index = decoder->group_leader[group]; + + // Already the leader? If so, there is nothing to do. + + if (leader_index == node_index) { + return node_index; + } + + node = &decoder->nodes[node_index]; + leader = &decoder->nodes[leader_index]; + + // Swap leaf and child indices in the two nodes: + + tmp = leader->leaf; + leader->leaf = node->leaf; + node->leaf = tmp; + + tmp = leader->child_index; + leader->child_index = node->child_index; + node->child_index = tmp; + + if (node->leaf) { + decoder->leaf_nodes[node->child_index] = node_index; + } else { + decoder->nodes[node->child_index].parent = node_index; + decoder->nodes[node->child_index - 1].parent = node_index; + } + + if (leader->leaf) { + decoder->leaf_nodes[leader->child_index] = leader_index; + } else { + decoder->nodes[leader->child_index].parent = leader_index; + decoder->nodes[leader->child_index - 1].parent = leader_index; + } + + return leader_index; +} + +// Increase the frequency count for a node, rearranging groups as +// appropriate. + +static void increment_node_freq(LHALH1Decoder *decoder, uint16_t node_index) +{ + Node *node, *other; + + node = &decoder->nodes[node_index]; + other = &decoder->nodes[node_index - 1]; + + ++node->freq; + + // If the node is part of a group containing other nodes, it + // must leave the group. + + if (node_index < NUM_TREE_NODES - 1 + && node->group == decoder->nodes[node_index + 1].group) { + + // Next node in the group now becomes the leader. + + ++decoder->group_leader[node->group]; + + // The node must now either join the group to its + // left, or start a new group. + + if (node->freq == other->freq) { + node->group = other->group; + } else { + node->group = alloc_group(decoder); + decoder->group_leader[node->group] = node_index; + } + + } else { + // The node is in a group of its own (single-node + // group). It might need to join the group of the + // node on its left if it has the same frequency. + + if (node->freq == other->freq) { + free_group(decoder, node->group); + node->group = other->group; + } + } +} + +// Reconstruct the code huffman tree to be more evenly distributed. +// Invoked periodically as data is processed. + +static void reconstruct_tree(LHALH1Decoder *decoder) +{ + Node *leaf; + unsigned int child; + unsigned int freq; + unsigned int group; + int i; + + // Gather all leaf nodes at the start of the table. + + leaf = decoder->nodes; + + for (i = 0; i < NUM_TREE_NODES; ++i) { + if (decoder->nodes[i].leaf) { + leaf->leaf = 1; + leaf->child_index = decoder->nodes[i].child_index; + + // Frequency of the nodes in the new tree is halved, + // this acts as a running average each time the + // tree is reconstructed. + + leaf->freq = (uint16_t) (decoder->nodes[i].freq + 1) / 2; + + ++leaf; + } + } + + // The leaf nodes are now all at the start of the table. Now + // reconstruct the tree, starting from the end of the table and + // working backwards, inserting branch nodes between the leaf + // nodes. Each branch node inherits the sum of the frequencies + // of its children, and must be placed to maintain the ordering + // within the table by decreasing frequency. + + leaf = &decoder->nodes[NUM_CODES - 1]; + child = NUM_TREE_NODES - 1; + i = NUM_TREE_NODES - 1; + + while (i >= 0) { + + // Before we can add a new branch node, we need at least + // two nodes to use as children. If we don't have this + // then we need to copy some from the leaves. + + while ((int) child - i < 2) { + decoder->nodes[i] = *leaf; + decoder->leaf_nodes[leaf->child_index] = (uint16_t) i; + + --i; + --leaf; + } + + // Now that we have at least two nodes to take as children + // of the new branch node, we can calculate the branch + // node's frequency. + + freq = (unsigned int) (decoder->nodes[child].freq + + decoder->nodes[child - 1].freq); + + // Now copy more leaf nodes until the correct place to + // insert the new branch node presents itself. + + while (leaf >= decoder->nodes && freq >= leaf->freq) { + decoder->nodes[i] = *leaf; + decoder->leaf_nodes[leaf->child_index] = (uint16_t) i; + + --i; + --leaf; + } + + // The new branch node can now be inserted. + + decoder->nodes[i].leaf = 0; + decoder->nodes[i].freq = (uint16_t) freq; + decoder->nodes[i].child_index = (uint16_t) child; + + decoder->nodes[child].parent = (uint16_t) i; + decoder->nodes[child - 1].parent = (uint16_t) i; + + --i; + + // Process the next pair of children. + + child -= 2; + } + + // Reconstruct the group data. Start by resetting group data. + + init_groups(decoder); + + // Assign a group to the first node. + + group = alloc_group(decoder); + decoder->nodes[0].group = (uint16_t) group; + decoder->group_leader[group] = 0; + + // Assign a group number to each node, nodes having the same + // group if the have the same frequency, and allocating new + // groups when a new frequency is found. + + for (i = 1; i < NUM_TREE_NODES; ++i) { + if (decoder->nodes[i].freq == decoder->nodes[i - 1].freq) { + decoder->nodes[i].group = decoder->nodes[i - 1].group; + } else { + group = alloc_group(decoder); + decoder->nodes[i].group = (uint16_t) group; + + // First node with a particular frequency is leader. + decoder->group_leader[group] = (uint16_t) i; + } + } +} + +// Increment the counter for the specific code, reordering the tree as +// necessary. + +static void increment_for_code(LHALH1Decoder *decoder, uint16_t code) +{ + uint16_t node_index; + + // When the limit is reached, we must reorder the code tree + // to better match the code frequencies: + + if (decoder->nodes[0].freq >= TREE_REORDER_LIMIT) { + reconstruct_tree(decoder); + } + + ++decoder->nodes[0].freq; + + // Dynamically adjust the tree. Start from the leaf node of + // the tree and walk back up, rearranging nodes to the root. + + node_index = decoder->leaf_nodes[code]; + + while (node_index != 0) { + + // Shift the node to the left side of its group, + // and bump the frequency count. + + node_index = make_group_leader(decoder, node_index); + + increment_node_freq(decoder, node_index); + + // Iterate up to the parent node. + + node_index = decoder->nodes[node_index].parent; + } +} + +// Read a code from the input stream. + +static int read_code(LHALH1Decoder *decoder, uint16_t *result) +{ + unsigned int node_index; + int bit; + + // Start from the root node, and traverse down until a leaf is + // reached. + + node_index = 0; + + //printf("<root "); + while (!decoder->nodes[node_index].leaf) { + bit = read_bit(&decoder->bit_stream_reader); + + if (bit < 0) { + return 0; + } + + //printf("<%i>", bit); + + // Choose one of the two children depending on the + // bit that was read. + + node_index = decoder->nodes[node_index].child_index + - (unsigned int) bit; + } + + *result = decoder->nodes[node_index].child_index; + //printf(" -> %i!>\n", *result); + + increment_for_code(decoder, *result); + + return 1; +} + +// Read an offset code from the input stream. + +static int read_offset(LHALH1Decoder *decoder, unsigned int *result) +{ + unsigned int offset; + int future, offset2; + + // The offset can be up to 8 bits long, but is likely not + // that long. Use the lookup table to find the offset + // and its length. + + future = peek_bits(&decoder->bit_stream_reader, 8); + + if (future < 0) { + return 0; + } + + offset = decoder->offset_lookup[future]; + + // Skip past the offset bits and also read the following + // lower-order bits. + + read_bits(&decoder->bit_stream_reader, + decoder->offset_lengths[offset]); + + offset2 = read_bits(&decoder->bit_stream_reader, 6); + + if (offset2 < 0) { + return 0; + } + + *result = (offset << 6) | (unsigned int) offset2; + + return 1; +} + +static void output_byte(LHALH1Decoder *decoder, uint8_t *buf, + size_t *buf_len, uint8_t b) +{ + buf[*buf_len] = b; + ++*buf_len; + + decoder->ringbuf[decoder->ringbuf_pos] = b; + decoder->ringbuf_pos = (decoder->ringbuf_pos + 1) % RING_BUFFER_SIZE; +} + +static size_t lha_lh1_read(void *data, uint8_t *buf) +{ + LHALH1Decoder *decoder = data; + size_t result; + uint16_t code; + + result = 0; + + // Read the next code from the input stream. + + if (!read_code(decoder, &code)) { + return 0; + } + + // The code either indicates a single byte to be output, or + // it indicates that a block should be copied from the ring + // buffer as it is a repeat of a sequence earlier in the + // stream. + + if (code < 0x100) { + output_byte(decoder, buf, &result, (uint8_t) code); + } else { + unsigned int count, start, i, pos, offset; + + // Read the offset into the history at which to start + // copying. + + if (!read_offset(decoder, &offset)) { + return 0; + } + + count = code - 0x100U + COPY_THRESHOLD; + start = decoder->ringbuf_pos - offset + RING_BUFFER_SIZE - 1; + + // Copy from history into output buffer: + + for (i = 0; i < count; ++i) { + pos = (start + i) % RING_BUFFER_SIZE; + + output_byte(decoder, buf, &result, + decoder->ringbuf[pos]); + } + } + + return result; +} + +LHADecoderType lha_lh1_decoder = { + lha_lh1_init, + NULL, + lha_lh1_read, + sizeof(LHALH1Decoder), + OUTPUT_BUFFER_SIZE, + RING_BUFFER_SIZE +}; + + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh5_decoder.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh5_decoder.c new file mode 100644 index 00000000..e48b1ccf --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh5_decoder.c @@ -0,0 +1,49 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// +// Decoder for the -lh5- algorithm. +// +// This is the "new" algorithm that appeared in LHA v2, replacing +// the older -lh1-. -lh4- seems to be identical to -lh5-. +// + +// 16 KiB history ring buffer: + +#define HISTORY_BITS 14 /* 2^14 = 16384 */ + +// Number of bits to encode HISTORY_BITS: + +#define OFFSET_BITS 4 + +// Name of the variable for the encoder: + +#define DECODER_NAME lha_lh5_decoder + +// Generate a second decoder for lh4 that just has a different +// block size. + +#define DECODER2_NAME lha_lh4_decoder + +// The actual algorithm code is contained in lh_new_decoder.c, which +// acts as a template for -lh4-, -lh5-, -lh6- and -lh7-. + +#include "lh_new_decoder.c" + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh6_decoder.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh6_decoder.c new file mode 100644 index 00000000..e392f4a8 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh6_decoder.c @@ -0,0 +1,43 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// +// Decoder for the -lh6- algorithm. +// +// -lh6- is an "extended" version of -lh5- introduced in LHA v2.66. +// + +// 64 KiB history ring buffer: + +#define HISTORY_BITS 16 /* 2^16 = 65536 */ + +// Number of bits to encode HISTORY_BITS: + +#define OFFSET_BITS 5 + +// Name of the variable for the encoder: + +#define DECODER_NAME lha_lh6_decoder + +// The actual algorithm code is contained in lh_new_decoder.c, which +// acts as a template for -lh4-, -lh5-, -lh6- and -lh7-. + +#include "lh_new_decoder.c" + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh7_decoder.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh7_decoder.c new file mode 100644 index 00000000..9867cd1e --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh7_decoder.c @@ -0,0 +1,44 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// +// Decoder for the -lh7- algorithm. +// +// -lh7- is an extension of the -lh5- algorithm introduced in +// LHA 2.67 beta. +// + +// 128 KiB history ring buffer: + +#define HISTORY_BITS 17 /* 2^17 = 131072 */ + +// Number of bits to encode HISTORY_BITS: + +#define OFFSET_BITS 5 + +// Name of the variable for the encoder: + +#define DECODER_NAME lha_lh7_decoder + +// The actual algorithm code is contained in lh_new_decoder.c, which +// acts as a template for -lh4-, -lh5-, -lh6- and -lh7-. + +#include "lh_new_decoder.c" + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh_new_decoder.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh_new_decoder.c new file mode 100644 index 00000000..56310c56 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lh_new_decoder.c @@ -0,0 +1,569 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// Decoder for "new-style" LHA algorithms, used with LHA v2 and onwards +// (-lh4-, -lh5-, -lh6-, -lh7-). +// +// This file is designed to be a template. It is #included by other +// files to generate an optimized decoder. + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> + +#include "lha_decoder.h" + +#include "bit_stream_reader.c" + +// Include tree decoder. + +typedef uint16_t TreeElement; +#include "tree_decode.c" + +// Threshold for copying. The first copy code starts from here. + +#define COPY_THRESHOLD 3 /* bytes */ + +// Ring buffer containing history has a size that is a power of two. +// The number of bits is specified. + +#define RING_BUFFER_SIZE (1 << HISTORY_BITS) + +// Required size of the output buffer. At most, a single call to read() +// might result in a copy of the entire ring buffer. + +#define OUTPUT_BUFFER_SIZE RING_BUFFER_SIZE + +// Number of different command codes. 0-255 range are literal byte +// values, while higher values indicate copy from history. + +#define NUM_CODES 510 + +// Number of possible codes in the "temporary table" used to encode the +// codes table. + +#define MAX_TEMP_CODES 20 + +typedef struct { + // Input bit stream. + + BitStreamReader bit_stream_reader; + + // Ring buffer of past data. Used for position-based copies. + + uint8_t ringbuf[RING_BUFFER_SIZE]; + unsigned int ringbuf_pos; + + // Number of commands remaining before we start a new block. + + unsigned int block_remaining; + + // Table used for the code tree. + + TreeElement code_tree[NUM_CODES * 2]; + + // Table used to encode the offset tree, used to read offsets + // into the history buffer. This same table is also used to + // encode the temp-table, which is bigger; hence the size. + + TreeElement offset_tree[MAX_TEMP_CODES * 2]; +} LHANewDecoder; + +// Initialize the history ring buffer. + +static void init_ring_buffer(LHANewDecoder *decoder) +{ + memset(decoder->ringbuf, ' ', RING_BUFFER_SIZE); + decoder->ringbuf_pos = 0; +} + +static int lha_lh_new_init(void *data, LHADecoderCallback callback, + void *callback_data) +{ + LHANewDecoder *decoder = data; + + // Initialize input stream reader. + + bit_stream_reader_init(&decoder->bit_stream_reader, + callback, callback_data); + + // Initialize data structures. + + init_ring_buffer(decoder); + + // First read starts the first block. + + decoder->block_remaining = 0; + + // Initialize tree tables to a known state. + + init_tree(decoder->code_tree, NUM_CODES * 2); + init_tree(decoder->offset_tree, MAX_TEMP_CODES * 2); + + return 1; +} + +// Read a length value - this is normally a value in the 0-7 range, but +// sometimes can be longer. + +static int read_length_value(LHANewDecoder *decoder) +{ + int i, len; + + len = read_bits(&decoder->bit_stream_reader, 3); + + if (len < 0) { + return -1; + } + + if (len == 7) { + // Read more bits to extend the length until we reach a '0'. + + for (;;) { + i = read_bit(&decoder->bit_stream_reader); + + if (i < 0) { + return -1; + } else if (i == 0) { + break; + } + + ++len; + } + } + + return len; +} + +// Read the values from the input stream that define the temporary table +// used for encoding the code table. + +static int read_temp_table(LHANewDecoder *decoder) +{ + int i, j, n, len, code; + uint8_t code_lengths[MAX_TEMP_CODES]; + + // How many codes? + + n = read_bits(&decoder->bit_stream_reader, 5); + + if (n < 0) { + return 0; + } + + // n=0 is a special case, meaning only a single code that + // is of zero length. + + if (n == 0) { + code = read_bits(&decoder->bit_stream_reader, 5); + + if (code < 0) { + return 0; + } + + set_tree_single(decoder->offset_tree, code); + return 1; + } + + // Enforce a hard limit on the number of codes. + + if (n > MAX_TEMP_CODES) { + n = MAX_TEMP_CODES; + } + + // Read the length of each code. + + for (i = 0; i < n; ++i) { + len = read_length_value(decoder); + + if (len < 0) { + return 0; + } + + code_lengths[i] = len; + + // After the first three lengths, there is a 2-bit + // field to allow skipping over up to a further three + // lengths. Not sure of the reason for this ... + + if (i == 2) { + len = read_bits(&decoder->bit_stream_reader, 2); + + if (len < 0) { + return 0; + } + + for (j = 0; j < len; ++j) { + ++i; + code_lengths[i] = 0; + } + } + } + + build_tree(decoder->offset_tree, MAX_TEMP_CODES * 2, code_lengths, n); + + return 1; +} + +// Code table codes can indicate that a sequence of codes should be +// skipped over. The number to skip is Huffman-encoded. Given a skip +// range (0-2), this reads the number of codes to skip over. + +static int read_skip_count(LHANewDecoder *decoder, int skiprange) +{ + int result; + + // skiprange=0 => 1 code. + + if (skiprange == 0) { + result = 1; + } + + // skiprange=1 => 3-18 codes. + + else if (skiprange == 1) { + result = read_bits(&decoder->bit_stream_reader, 4); + + if (result < 0) { + return -1; + } + + result += 3; + } + + // skiprange=2 => 20+ codes. + + else { + result = read_bits(&decoder->bit_stream_reader, 9); + + if (result < 0) { + return -1; + } + + result += 20; + } + + return result; +} + +static int read_code_table(LHANewDecoder *decoder) +{ + int i, j, n, skip_count, code; + uint8_t code_lengths[NUM_CODES]; + + // How many codes? + + n = read_bits(&decoder->bit_stream_reader, 9); + + if (n < 0) { + return 0; + } + + // n=0 implies a single code of zero length; all inputs + // decode to the same code. + + if (n == 0) { + code = read_bits(&decoder->bit_stream_reader, 9); + + if (code < 0) { + return 0; + } + + set_tree_single(decoder->code_tree, code); + + return 1; + } + + if (n > NUM_CODES) { + n = NUM_CODES; + } + + // Read the length of each code. + // The lengths are encoded using the temp-table previously read; + // offset_tree is reused temporarily to hold it. + + i = 0; + + while (i < n) { + code = read_from_tree(&decoder->bit_stream_reader, + decoder->offset_tree); + + if (code < 0) { + return 0; + } + + // The code that was read can have different meanings. + // If in the range 0-2, it indicates that a number of + // codes are unused and should be skipped over. + // Values greater than two represent a frequency count. + + if (code <= 2) { + skip_count = read_skip_count(decoder, code); + + if (skip_count < 0) { + return 0; + } + + for (j = 0; j < skip_count && i < n; ++j) { + code_lengths[i] = 0; + ++i; + } + } else { + code_lengths[i] = code - 2; + ++i; + } + } + + build_tree(decoder->code_tree, NUM_CODES * 2, code_lengths, n); + + return 1; +} + +static int read_offset_table(LHANewDecoder *decoder) +{ + int i, n, len, code; + uint8_t code_lengths[HISTORY_BITS]; + + // How many codes? + + n = read_bits(&decoder->bit_stream_reader, OFFSET_BITS); + + if (n < 0) { + return 0; + } + + // n=0 is a special case, meaning only a single code that + // is of zero length. + + if (n == 0) { + code = read_bits(&decoder->bit_stream_reader, OFFSET_BITS); + + if (code < 0) { + return 0; + } + + set_tree_single(decoder->offset_tree, code); + return 1; + } + + // Enforce a hard limit on the number of codes. + + if (n > HISTORY_BITS) { + n = HISTORY_BITS; + } + + // Read the length of each code. + + for (i = 0; i < n; ++i) { + len = read_length_value(decoder); + + if (len < 0) { + return 0; + } + + code_lengths[i] = len; + } + + build_tree(decoder->offset_tree, MAX_TEMP_CODES * 2, code_lengths, n); + + return 1; +} + +// Start reading a new block from the input stream. + +static int start_new_block(LHANewDecoder *decoder) +{ + int len; + + // Read length of new block (in commands). + + len = read_bits(&decoder->bit_stream_reader, 16); + + if (len < 0) { + return 0; + } + + decoder->block_remaining = (size_t) len; + + // Read the temporary decode table, used to encode the codes table. + // The position table data structure is reused for this. + + if (!read_temp_table(decoder)) { + return 0; + } + + // Read the code table; this is encoded *using* the temp table. + + if (!read_code_table(decoder)) { + return 0; + } + + // Read the offset table. + + if (!read_offset_table(decoder)) { + return 0; + } + + return 1; +} + +// Read the next code from the input stream. Returns the code, or -1 if +// an error occurred. + +static int read_code(LHANewDecoder *decoder) +{ + return read_from_tree(&decoder->bit_stream_reader, decoder->code_tree); +} + +// Read an offset distance from the input stream. +// Returns the code, or -1 if an error occurred. + +static int read_offset_code(LHANewDecoder *decoder) +{ + int bits, result; + + bits = read_from_tree(&decoder->bit_stream_reader, + decoder->offset_tree); + + if (bits < 0) { + return -1; + } + + // The code read indicates the length of the offset in bits. + // + // The returned value looks like this: + // bits = 0 -> 0 + // bits = 1 -> 1 + // bits = 2 -> 1x + // bits = 3 -> 1xx + // bits = 4 -> 1xxx + // etc. + + if (bits == 0) { + return 0; + } else if (bits == 1) { + return 1; + } else { + result = read_bits(&decoder->bit_stream_reader, bits - 1); + + if (result < 0) { + return -1; + } + + return result + (1 << (bits - 1)); + } +} + +// Add a byte value to the output stream. + +static void output_byte(LHANewDecoder *decoder, uint8_t *buf, + size_t *buf_len, uint8_t b) +{ + buf[*buf_len] = b; + ++*buf_len; + + decoder->ringbuf[decoder->ringbuf_pos] = b; + decoder->ringbuf_pos = (decoder->ringbuf_pos + 1) % RING_BUFFER_SIZE; +} + +// Copy a block from the history buffer. + +static void copy_from_history(LHANewDecoder *decoder, uint8_t *buf, + size_t *buf_len, size_t count) +{ + int offset; + unsigned int i, start; + + offset = read_offset_code(decoder); + + if (offset < 0) { + return; + } + + start = decoder->ringbuf_pos + RING_BUFFER_SIZE + - (unsigned int) offset - 1; + + for (i = 0; i < count; ++i) { + output_byte(decoder, buf, buf_len, + decoder->ringbuf[(start + i) % RING_BUFFER_SIZE]); + } +} + +static size_t lha_lh_new_read(void *data, uint8_t *buf) +{ + LHANewDecoder *decoder = data; + size_t result; + int code; + + // Start of new block? + + while (decoder->block_remaining == 0) { + if (!start_new_block(decoder)) { + return 0; + } + } + + --decoder->block_remaining; + + // Read next command from input stream. + + result = 0; + + code = read_code(decoder); + + if (code < 0) { + return 0; + } + + // The code may be either a literal byte value or a copy command. + + if (code < 256) { + output_byte(decoder, buf, &result, (uint8_t) code); + } else { + copy_from_history(decoder, buf, &result, + code - 256 + COPY_THRESHOLD); + } + + return result; +} + +LHADecoderType DECODER_NAME = { + lha_lh_new_init, + NULL, + lha_lh_new_read, + sizeof(LHANewDecoder), + OUTPUT_BUFFER_SIZE, + RING_BUFFER_SIZE / 2 +}; + +// This is a hack for -lh4-: + +#ifdef DECODER2_NAME +LHADecoderType DECODER2_NAME = { + lha_lh_new_init, + NULL, + lha_lh_new_read, + sizeof(LHANewDecoder), + OUTPUT_BUFFER_SIZE, + RING_BUFFER_SIZE / 4 +}; +#endif + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_arch.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_arch.h new file mode 100644 index 00000000..41b4e37c --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_arch.h @@ -0,0 +1,160 @@ +/* + +Copyright (c) 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_LHA_ARCH_H +#define LHASA_LHA_ARCH_H + +#include <stdio.h> +#include <stdarg.h> +#include <stdint.h> + +#define LHA_ARCH_UNIX 1 +#define LHA_ARCH_WINDOWS 2 + +#ifdef _WIN32 +#define LHA_ARCH LHA_ARCH_WINDOWS +#else +#define LHA_ARCH LHA_ARCH_UNIX +#endif + +typedef enum { + LHA_FILE_NONE, + LHA_FILE_FILE, + LHA_FILE_DIRECTORY, + LHA_FILE_ERROR, +} LHAFileType; + +/** + * Cross-platform version of vasprintf(). + * + * @param result Pointer to a variable to store the resulting string. + * @param fmt Format string. + * @param args Additional arguments for printf(). + * @return Number of characters in resulting string, or -1 if + * an error occurred in generating the string. + */ + +int lha_arch_vasprintf(char **result, char *fmt, va_list args); + +/** + * Change the mode of the specified FILE handle to be binary mode. + * + * @param handle The FILE handle. + */ + +void lha_arch_set_binary(FILE *handle); + +/** + * Create a directory. + * + * @param path Path to the directory to create. + * @param unix_perms Unix permissions for the directory to create. + * @return Non-zero if the directory was created successfully. + */ + +int lha_arch_mkdir(char *path, unsigned int unix_perms); + +/** + * Change the Unix ownership of the specified file or directory. + * If this is not a Unix system, do nothing. + * + * @param filename Path to the file or directory. + * @param unix_uid The UID to set. + * @param unix_gid The GID to set. + * @return Non-zero if set successfully. + */ + +int lha_arch_chown(char *filename, int unix_uid, int unix_gid); + +/** + * Change the Unix permissions on the specified file or directory. + * + * @param filename Path to the file or directory. + * @param unix_perms The permissions to set. + * @return Non-zero if set successfully. + */ + +int lha_arch_chmod(char *filename, int unix_perms); + +/** + * Set the file creation / modification time on the specified file or + * directory. + * + * @param filename Path to the file or directory. + * @param timestamp The Unix timestamp to set. + * @return Non-zero if set successfully. + */ + +int lha_arch_utime(char *filename, unsigned int timestamp); + +/** + * Set the file creation, modification and access times for the + * specified file or directory, using 64-bit Windows timestamps. + * + * @param filename Path to the file or directory. + * @param creation_time 64-bit Windows FILETIME value for the + * creation time of the file. + * @param modification_time Modification time of the file. + * @param access_time Last access time of the file. + * @return Non-zero if set successfully. + */ + +int lha_arch_set_windows_timestamps(char *filename, + uint64_t creation_time, + uint64_t modification_time, + uint64_t access_time); +/** + * Open a new file for writing. + * + * @param filename Path to the file or directory. + * @param unix_uid Unix UID to set for the new file, or -1 to not set. + * @param unix_gid Unix GID to set for the new file, or -1 to not set. + * @param unix_perms Unix permissions to set for the new file, or -1 to not + * set. + * @return Standard C file handle. + */ + +FILE *lha_arch_fopen(char *filename, int unix_uid, + int unix_gid, int unix_perms); + +/** + * Query whether the specified file exists. + * + * @param filename Path to the file. + * @return The type of file. + */ + +LHAFileType lha_arch_exists(char *filename); + +/** + * Create a symbolic link. + * + * If a file already exists at the location of the link to be created, it is + * overwritten. + * + * @param path Path to the symbolic link to create. + * @param target Target for the symbolic link. + * @return Non-zero for success. + */ + +int lha_arch_symlink(char *path, char *target); + +#endif /* ifndef LHASA_LHA_ARCH_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_arch_unix.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_arch_unix.c new file mode 100644 index 00000000..c8896374 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_arch_unix.c @@ -0,0 +1,173 @@ +/* + +Copyright (c) 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// +// Architecture-specific files for compilation on Unix. +// + +#define _GNU_SOURCE +#include "lha_arch.h" + +#if LHA_ARCH == LHA_ARCH_UNIX + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <utime.h> +#include <sys/stat.h> +#include <sys/types.h> + +// TODO: This file depends on vasprintf(), which is a non-standard +// function (_GNU_SOURCE above). Most modern Unix systems have an +// implementation of it, but develop a compatible workaround for +// operating systems that don't have it. + +int lha_arch_vasprintf(char **result, char *fmt, va_list args) +{ + return vasprintf(result, fmt, args); +} + +void lha_arch_set_binary(FILE *handle) +{ + // No-op on Unix systems: there is no difference between + // "text" and "binary" files. +} + +int lha_arch_mkdir(char *path, unsigned int unix_perms) +{ + return mkdir(path, unix_perms) == 0; +} + +int lha_arch_chown(char *filename, int unix_uid, int unix_gid) +{ + return chown(filename, unix_uid, unix_gid) == 0; +} + +int lha_arch_chmod(char *filename, int unix_perms) +{ + return chmod(filename, unix_perms) == 0; +} + +int lha_arch_utime(char *filename, unsigned int timestamp) +{ + struct utimbuf times; + + times.actime = (time_t) timestamp; + times.modtime = (time_t) timestamp; + + return utime(filename, ×) == 0; +} + +FILE *lha_arch_fopen(char *filename, int unix_uid, int unix_gid, int unix_perms) +{ + FILE *fstream; + int fileno; + + // The O_EXCL flag will cause the open() below to fail if the + // file already exists. Remove it first. + + unlink(filename); + + // If we have file permissions, they must be set after the + // file is created and UID/GID have been set. When open()ing + // the file, create it with minimal permissions granted only + // to the current user. + // Use O_EXCL so that symlinks are not followed; this prevents + // a malicious symlink from overwriting arbitrary filesystem + // locations. + + fileno = open(filename, O_CREAT|O_WRONLY|O_EXCL, 0600); + + if (fileno < 0) { + return NULL; + } + + // Set owner and group. + + if (unix_uid >= 0) { + if (fchown(fileno, unix_uid, unix_gid) != 0) { + // On most Unix systems, only root can change + // ownership. But if we can't change ownership, + // it isn't a fatal error. So ignore the failure + // and continue. + + // TODO: Implement some kind of alternate handling + // here? + + /* close(fileno); + remove(filename); + return NULL; */ + } + } + + // Set file permissions. + // File permissions must be set *after* owner and group have + // been set; otherwise, we might briefly be granting permissions + // to the wrong group. + + if (unix_perms >= 0) { + if (fchmod(fileno, unix_perms) != 0) { + close(fileno); + remove(filename); + return NULL; + } + } + + // Create stdc FILE handle. + + fstream = fdopen(fileno, "wb"); + + if (fstream == NULL) { + close(fileno); + remove(filename); + return NULL; + } + + return fstream; +} + +LHAFileType lha_arch_exists(char *filename) +{ + struct stat statbuf; + + if (stat(filename, &statbuf) != 0) { + if (errno == ENOENT) { + return LHA_FILE_NONE; + } else { + return LHA_FILE_ERROR; + } + } + + if (S_ISDIR(statbuf.st_mode)) { + return LHA_FILE_DIRECTORY; + } else { + return LHA_FILE_FILE; + } +} + +int lha_arch_symlink(char *path, char *target) +{ + unlink(path); + return symlink(target, path) == 0; +} + +#endif /* LHA_ARCH_UNIX */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_arch_win32.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_arch_win32.c new file mode 100644 index 00000000..ac8ee331 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_arch_win32.c @@ -0,0 +1,204 @@ +/* + +Copyright (c) 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// +// Architecture-specific files for compilation on Windows. +// A work in progress. +// + +#include "lha_arch.h" + +#if LHA_ARCH == LHA_ARCH_WINDOWS + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <fcntl.h> +#include <io.h> + +#include <stdlib.h> +#include <stdint.h> + +static uint64_t unix_epoch_offset = 0; + +int lha_arch_vasprintf(char **result, char *fmt, va_list args) +{ + int len; + + len = vsnprintf(NULL, 0, fmt, args); + if (len >= 0) { + *result = malloc(len + 1); + if (*result != NULL) { + return vsprintf(*result, fmt, args); + } + } + + *result = NULL; + return -1; +} + +void lha_arch_set_binary(FILE *handle) +{ + _setmode(_fileno(handle), _O_BINARY); +} + +int lha_arch_mkdir(char *path, unsigned int unix_mode) +{ + return CreateDirectoryA(path, NULL) != 0; +} + +int lha_arch_chown(char *filename, int unix_uid, int unix_gid) +{ + return 1; +} + +int lha_arch_chmod(char *filename, int unix_perms) +{ + return 1; +} + +static int set_timestamps(char *filename, + FILETIME *creation_time, + FILETIME *modification_time, + FILETIME *access_time) +{ + HANDLE file; + int result; + + // Open file handle to the file to change. + // The FILE_FLAG_BACKUP_SEMANTICS flag is needed so that we + // can obtain handles to directories as well as files. + + file = CreateFileA(filename, + FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + if (file == INVALID_HANDLE_VALUE) { + return 0; + } + + // Set the timestamp and close the file handle. + + result = SetFileTime(file, creation_time, + access_time, modification_time); + CloseHandle(file); + + return result != 0; +} + +int lha_arch_set_windows_timestamps(char *filename, + uint64_t creation_time, + uint64_t modification_time, + uint64_t access_time) +{ + FILETIME _creation_time; + FILETIME _modification_time; + FILETIME _access_time; + + _creation_time.dwHighDateTime + = (uint32_t) ((creation_time >> 32) & 0xffffffff); + _creation_time.dwLowDateTime + = (uint32_t) (creation_time & 0xffffffff); + + _modification_time.dwHighDateTime + = (uint32_t) ((modification_time >> 32) & 0xffffffff); + _modification_time.dwLowDateTime + = (uint32_t) (modification_time & 0xffffffff); + + _access_time.dwHighDateTime + = (uint32_t) ((access_time >> 32) & 0xffffffff); + _access_time.dwLowDateTime + = (uint32_t) (access_time & 0xffffffff); + + return set_timestamps(filename, &_creation_time, + &_modification_time, &_access_time); +} + +int lha_arch_utime(char *filename, unsigned int timestamp) +{ + SYSTEMTIME unix_epoch; + FILETIME filetime; + uint64_t ts_scaled; + + // Calculate offset between Windows FILETIME Jan 1, 1601 epoch + // and Unix Jan 1, 1970 offset. + + if (unix_epoch_offset == 0) { + unix_epoch.wYear = 1970; + unix_epoch.wMonth = 1; + unix_epoch.wDayOfWeek = 4; // Thursday + unix_epoch.wDay = 1; + unix_epoch.wHour = 0; // 00:00:00.0 + unix_epoch.wMinute = 0; + unix_epoch.wSecond = 0; + unix_epoch.wMilliseconds = 0; + + SystemTimeToFileTime(&unix_epoch, &filetime); + unix_epoch_offset = ((uint64_t) filetime.dwHighDateTime << 32) + + filetime.dwLowDateTime; + } + + // Convert to Unix FILETIME. + + ts_scaled = (uint64_t) timestamp * 10000000 + unix_epoch_offset; + filetime.dwHighDateTime = (uint32_t) ((ts_scaled >> 32) & 0xffffffff); + filetime.dwLowDateTime = (uint32_t) (ts_scaled & 0xffffffff); + + // Set all timestamps to the same value: + + return set_timestamps(filename, &filetime, &filetime, &filetime); +} + +FILE *lha_arch_fopen(char *filename, int unix_uid, int unix_gid, int unix_perms) +{ + return fopen(filename, "wb"); +} + +LHAFileType lha_arch_exists(char *filename) +{ + WIN32_FILE_ATTRIBUTE_DATA file_attr; + + // Read file attributes to determine the type of the file. + // If this fails, assume the file does not exist. + + if (GetFileAttributesExA(filename, GetFileExInfoStandard, + &file_attr)) { + if ((file_attr.dwFileAttributes + & FILE_ATTRIBUTE_DIRECTORY) != 0) { + return LHA_FILE_DIRECTORY; + } else { + return LHA_FILE_FILE; + } + } + + return LHA_FILE_NONE; +} + +int lha_arch_symlink(char *path, char *target) +{ + // No-op. + return 1; +} + +#endif /* LHA_ARCH_WINDOWS */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_basic_reader.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_basic_reader.c new file mode 100644 index 00000000..65c4c648 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_basic_reader.c @@ -0,0 +1,159 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "crc16.h" + +#include "lha_decoder.h" +#include "lha_basic_reader.h" + +struct _LHABasicReader { + LHAInputStream *stream; + LHAFileHeader *curr_file; + size_t curr_file_remaining; + int eof; +}; + +LHABasicReader *lha_basic_reader_new(LHAInputStream *stream) +{ + LHABasicReader *reader; + + reader = calloc(1, sizeof(LHABasicReader)); + + if (reader == NULL) { + return NULL; + } + + reader->stream = stream; + reader->curr_file = NULL; + reader->curr_file_remaining = 0; + reader->eof = 0; + + return reader; +} + +void lha_basic_reader_free(LHABasicReader *reader) +{ + if (reader->curr_file != NULL) { + lha_file_header_free(reader->curr_file); + } + + free(reader); +} + +LHAFileHeader *lha_basic_reader_curr_file(LHABasicReader *reader) +{ + return reader->curr_file; +} + +LHAFileHeader *lha_basic_reader_next_file(LHABasicReader *reader) +{ + // Free the current file header and skip over any remaining + // compressed data that hasn't been read yet. + + if (reader->curr_file != NULL) { + lha_file_header_free(reader->curr_file); + reader->curr_file = NULL; + + if (!lha_input_stream_skip(reader->stream, + reader->curr_file_remaining)) { + reader->eof = 1; + } + } + + if (reader->eof) { + return NULL; + } + + // Read the header for the next file. + + reader->curr_file = lha_file_header_read(reader->stream); + + if (reader->curr_file == NULL) { + reader->eof = 1; + return NULL; + } + + reader->curr_file_remaining = reader->curr_file->compressed_length; + + return reader->curr_file; +} + +size_t lha_basic_reader_read_compressed(LHABasicReader *reader, void *buf, + size_t buf_len) +{ + size_t bytes; + + if (reader->eof || reader->curr_file_remaining == 0) { + return 0; + } + + // Read up to the number of bytes of compressed data remaining. + + if (buf_len > reader->curr_file_remaining) { + bytes = reader->curr_file_remaining; + } else { + bytes = buf_len; + } + + if (!lha_input_stream_read(reader->stream, buf, bytes)) { + reader->eof = 1; + return 0; + } + + // Update counter and return success. + + reader->curr_file_remaining -= bytes; + + return bytes; +} + +static size_t decoder_callback(void *buf, size_t buf_len, void *user_data) +{ + return lha_basic_reader_read_compressed(user_data, buf, buf_len); +} + +// Create the decoder structure to decode the current file. + +LHADecoder *lha_basic_reader_decode(LHABasicReader *reader) +{ + LHADecoderType *dtype; + + if (reader->curr_file == NULL) { + return NULL; + } + + // Look up the decoder to use for this compression method. + + dtype = lha_decoder_for_name(reader->curr_file->compress_method); + + if (dtype == NULL) { + return NULL; + } + + // Create decoder. + + return lha_decoder_new(dtype, decoder_callback, reader, + reader->curr_file->length); +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_basic_reader.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_basic_reader.h new file mode 100644 index 00000000..90cfb88d --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_basic_reader.h @@ -0,0 +1,99 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_LHA_BASIC_READER_H +#define LHASA_LHA_BASIC_READER_H + +#include "lha_input_stream.h" +#include "lha_file_header.h" +#include "lha_decoder.h" + +/** + * Basic LHA stream reader. + * + * The basic reader structure just reads @ref LHAFileHeader structures + * from an input stream and decompresses files. The more elaborate + * @ref LHAReader builds upon this to offer more complicated functionality. + */ + +typedef struct _LHABasicReader LHABasicReader; + +/** + * Create a new LHA reader to read data from an input stream. + * + * @param stream The input stream to read from. + * @return Pointer to an LHABasicReader structure, or NULL for error. + */ + +LHABasicReader *lha_basic_reader_new(LHAInputStream *stream); + +/** + * Free an LHA reader. + * + * @param reader The LHABasicReader structure. + */ + +void lha_basic_reader_free(LHABasicReader *reader); + +/** + * Return the last file read by @ref lha_basic_reader_next_file. + * + * @param reader The LHABasicReader structure. + * @return Last file returned by @ref lha_basic_reader_next_file, + * or NULL if no file has been read yet. + */ + +LHAFileHeader *lha_basic_reader_curr_file(LHABasicReader *reader); + +/** + * Read the header of the next archived file from the input stream. + * + * @param reader The LHABasicReader structure. + * @return Pointer to an LHAFileHeader structure, or NULL if + * an error occurred. This pointer is only valid until + * the next time that lha_basic_reader_next_file is called. + */ + +LHAFileHeader *lha_basic_reader_next_file(LHABasicReader *reader); + +/** + * Read some of the compressed data for the current archived file. + * + * @param reader The LHABasicReader structure. + * @param buf Pointer to the buffer in which to store the data. + * @param buf_len Size of the buffer, in bytes. + */ + +size_t lha_basic_reader_read_compressed(LHABasicReader *reader, void *buf, + size_t buf_len); + +/** + * Create a decoder object to decompress the compressed data in the + * current file. + * + * @param reader The LHABasicReader structure. + * @return Pointer to a @ref LHADecoder structure to decompress + * the current file, or NULL for failure. + */ + +LHADecoder *lha_basic_reader_decode(LHABasicReader *reader); + +#endif /* #ifndef LHASA_LHA_BASIC_READER_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_decoder.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_decoder.c new file mode 100644 index 00000000..9216f002 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_decoder.c @@ -0,0 +1,256 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#include "crc16.h" +#include "lha_decoder.h" + +// Null decoder, used for -lz4-, -lh0-, -pm0-: +extern LHADecoderType lha_null_decoder; + +// LArc compression algorithms: +extern LHADecoderType lha_lz5_decoder; +extern LHADecoderType lha_lzs_decoder; + +// LHarc compression algorithms: +extern LHADecoderType lha_lh1_decoder; +extern LHADecoderType lha_lh4_decoder; +extern LHADecoderType lha_lh5_decoder; +extern LHADecoderType lha_lh6_decoder; +extern LHADecoderType lha_lh7_decoder; +extern LHADecoderType lha_lhx_decoder; + +// PMarc compression algorithms: +extern LHADecoderType lha_pm1_decoder; +extern LHADecoderType lha_pm2_decoder; + +static struct { + char *name; + LHADecoderType *dtype; +} decoders[] = { + { "-lz4-", &lha_null_decoder }, + { "-lz5-", &lha_lz5_decoder }, + { "-lzs-", &lha_lzs_decoder }, + { "-lh0-", &lha_null_decoder }, + { "-lh1-", &lha_lh1_decoder }, + { "-lh4-", &lha_lh4_decoder }, + { "-lh5-", &lha_lh5_decoder }, + { "-lh6-", &lha_lh6_decoder }, + { "-lh7-", &lha_lh7_decoder }, + { "-lhx-", &lha_lhx_decoder }, + { "-pm0-", &lha_null_decoder }, + { "-pm1-", &lha_pm1_decoder }, + { "-pm2-", &lha_pm2_decoder }, +}; + +LHADecoder *lha_decoder_new(LHADecoderType *dtype, + LHADecoderCallback callback, + void *callback_data, + size_t stream_length) +{ + LHADecoder *decoder; + void *extra_data; + + // Space is allocated together: the LHADecoder structure, + // then the private data area used by the algorithm, + // followed by the output buffer, + + decoder = calloc(1, sizeof(LHADecoder) + dtype->extra_size + + dtype->max_read); + + if (decoder == NULL) { + return NULL; + } + + decoder->dtype = dtype; + decoder->progress_callback = NULL; + decoder->last_block = UINT_MAX; + decoder->outbuf_pos = 0; + decoder->outbuf_len = 0; + decoder->stream_pos = 0; + decoder->stream_length = stream_length; + decoder->decoder_failed = 0; + decoder->crc = 0; + + // Private data area follows the structure. + + extra_data = decoder + 1; + decoder->outbuf = ((uint8_t *) extra_data) + dtype->extra_size; + + if (dtype->init != NULL + && !dtype->init(extra_data, callback, callback_data)) { + free(decoder); + return NULL; + } + + return decoder; +} + +LHADecoderType *lha_decoder_for_name(char *name) +{ + unsigned int i; + + for (i = 0; i < sizeof(decoders) / sizeof(*decoders); ++i) { + if (!strcmp(name, decoders[i].name)) { + return decoders[i].dtype; + } + } + + // Unknown? + + return NULL; +} + +void lha_decoder_free(LHADecoder *decoder) +{ + if (decoder->dtype->free != NULL) { + decoder->dtype->free(decoder + 1); + } + + free(decoder); +} + +// Check if the stream has progressed far enough that the progress callback +// should be invoked again. + +static void check_progress_callback(LHADecoder *decoder) +{ + unsigned int block; + + block = (decoder->stream_pos + decoder->dtype->block_size - 1) + / decoder->dtype->block_size; + + // If the stream has advanced by another block, invoke the callback + // function. Invoke it multiple times if it has advanced by + // more than one block. + + while (decoder->last_block != block) { + ++decoder->last_block; + decoder->progress_callback(decoder->last_block, + decoder->total_blocks, + decoder->progress_callback_data); + } +} + +void lha_decoder_monitor(LHADecoder *decoder, + LHADecoderProgressCallback callback, + void *callback_data) +{ + decoder->progress_callback = callback; + decoder->progress_callback_data = callback_data; + + decoder->total_blocks + = (decoder->stream_length + decoder->dtype->block_size - 1) + / decoder->dtype->block_size; + + check_progress_callback(decoder); +} + +size_t lha_decoder_read(LHADecoder *decoder, uint8_t *buf, size_t buf_len) +{ + size_t filled, bytes; + + // When we reach the end of the stream, we must truncate the + // decompressed data at exactly the right point (stream_length), + // or we may read a few extra false byte(s) by mistake. + // Reduce buf_len when we get to the end to limit it to the + // real number of remaining characters. + + if (decoder->stream_pos + buf_len > decoder->stream_length) { + buf_len = decoder->stream_length - decoder->stream_pos; + } + + // Try to fill up the buffer that has been passed with as much + // data as possible. Each call to read() will fill up outbuf + // with some data; this is then copied into buf, with some + // data left at the end for the next call. + + filled = 0; + + while (filled < buf_len) { + + // Try to empty out some of the output buffer first. + + bytes = decoder->outbuf_len - decoder->outbuf_pos; + + if (buf_len - filled < bytes) { + bytes = buf_len - filled; + } + + memcpy(buf + filled, decoder->outbuf + decoder->outbuf_pos, + bytes); + decoder->outbuf_pos += bytes; + filled += bytes; + + // If we previously encountered a failure reading from + // the decoder, don't try to call the read function again. + + if (decoder->decoder_failed) { + break; + } + + // If outbuf is now empty, we can process another run to + // re-fill it. + + if (decoder->outbuf_pos >= decoder->outbuf_len) { + decoder->outbuf_len + = decoder->dtype->read(decoder + 1, + decoder->outbuf); + decoder->outbuf_pos = 0; + } + + // No more data to be read? + + if (decoder->outbuf_len == 0) { + decoder->decoder_failed = 1; + break; + } + } + + // Update CRC. + + lha_crc16_buf(&decoder->crc, buf, filled); + + // Track stream position. + + decoder->stream_pos += filled; + + // Check progress callback, if one is set: + + if (decoder->progress_callback != NULL) { + check_progress_callback(decoder); + } + + return filled; +} + +uint16_t lha_decoder_get_crc(LHADecoder *decoder) +{ + return decoder->crc; +} + +size_t lha_decoder_get_length(LHADecoder *decoder) +{ + return decoder->stream_pos; +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_decoder.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_decoder.h new file mode 100644 index 00000000..41bbca1a --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_decoder.h @@ -0,0 +1,114 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_LHA_DECODER_H +#define LHASA_LHA_DECODER_H + +#include "public/lha_decoder.h" + +struct _LHADecoderType { + + /** + * Callback function to initialize the decoder. + * + * @param extra_data Pointer to the extra data area allocated for + * the decoder. + * @param callback Callback function to invoke to read more + * compressed data. + * @param callback_data Extra pointer to pass to the callback. + * @return Non-zero for success. + */ + + int (*init)(void *extra_data, + LHADecoderCallback callback, + void *callback_data); + + /** + * Callback function to free the decoder. + * + * @param extra_data Pointer to the extra data area allocated for + * the decoder. + */ + + void (*free)(void *extra_data); + + /** + * Callback function to read (ie. decompress) data from the + * decoder. + * + * @param extra_data Pointer to the decoder's custom data. + * @param buf Pointer to the buffer in which to store + * the decompressed data. The buffer is + * at least 'max_read' bytes in size. + * @return Number of bytes decompressed. + */ + + size_t (*read)(void *extra_data, uint8_t *buf); + + /** Number of bytes of extra data to allocate for the decoder. */ + + size_t extra_size; + + /** Maximum number of bytes that might be put into the buffer by + a single call to read() */ + + size_t max_read; + + /** Block size. Used for calculating number of blocks for + progress bar. */ + + size_t block_size; +}; + +struct _LHADecoder { + + /** Type of decoder (algorithm) */ + + LHADecoderType *dtype; + + /** Callback function to monitor decoder progress. */ + + LHADecoderProgressCallback progress_callback; + void *progress_callback_data; + + /** Last announced block position, for progress callback. */ + + unsigned int last_block, total_blocks; + + /** Current position in the decode stream, and total length. */ + + size_t stream_pos, stream_length; + + /** Output buffer, containing decoded data not yet returned. */ + + unsigned int outbuf_pos, outbuf_len; + uint8_t *outbuf; + + /** If true, the decoder read() function returned zero. */ + + unsigned int decoder_failed; + + /** Current CRC of the output stream. */ + + uint16_t crc; +}; + +#endif /* #ifndef LHASA_LHA_DECODER_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_endian.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_endian.c new file mode 100644 index 00000000..b9a7d81b --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_endian.c @@ -0,0 +1,60 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include "lha_endian.h" + +uint16_t lha_decode_uint16(uint8_t *buf) +{ + return (uint16_t) (buf[0] | (buf[1] << 8)); +} + +uint32_t lha_decode_uint32(uint8_t *buf) +{ + return ((uint32_t) buf[0]) + | ((uint32_t) buf[1] << 8) + | ((uint32_t) buf[2] << 16) + | ((uint32_t) buf[3] << 24); +} + +uint64_t lha_decode_uint64(uint8_t *buf) +{ + return ((uint64_t) buf[0]) + | ((uint64_t) buf[1] << 8) + | ((uint64_t) buf[2] << 16) + | ((uint64_t) buf[3] << 24) + | ((uint64_t) buf[4] << 32) + | ((uint64_t) buf[5] << 40) + | ((uint64_t) buf[6] << 48) + | ((uint64_t) buf[7] << 56); +} + +uint16_t lha_decode_be_uint16(uint8_t *buf) +{ + return (uint16_t) ((buf[0] << 8) | buf[1]); +} + +uint32_t lha_decode_be_uint32(uint8_t *buf) +{ + return ((uint32_t) buf[0] << 24) + | ((uint32_t) buf[1] << 16) + | ((uint32_t) buf[2] << 8) + | ((uint32_t) buf[3]); +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_endian.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_endian.h new file mode 100644 index 00000000..c421edb4 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_endian.h @@ -0,0 +1,72 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_LHA_ENDIAN_H +#define LHASA_LHA_ENDIAN_H + +#include <inttypes.h> + +/** + * Decode a 16-bit little-endian unsigned integer. + * + * @param buf Pointer to buffer containing value to decode. + * @return Decoded value. + */ + +uint16_t lha_decode_uint16(uint8_t *buf); + +/** + * Decode a 32-bit little-endian unsigned integer. + * + * @param buf Pointer to buffer containing value to decode. + * @return Decoded value. + */ + +uint32_t lha_decode_uint32(uint8_t *buf); + +/** + * Decode a 64-bit little-endian unsigned integer. + * + * @param buf Pointer to buffer containing value to decode. + * @return Decoded value. + */ + +uint64_t lha_decode_uint64(uint8_t *buf); + +/** + * Decode a 16-bit big-endian unsigned integer. + * + * @param buf Pointer to buffer containing value to decode. + * @return Decoded value. + */ + +uint16_t lha_decode_be_uint16(uint8_t *buf); + +/** + * Decode a 32-bit big-endian unsigned integer. + * + * @param buf Pointer to buffer containing value to decode. + * @return Decoded value. + */ + +uint32_t lha_decode_be_uint32(uint8_t *buf); + +#endif /* #ifndef LHASA_LHA_ENDIAN_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_file_header.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_file_header.c new file mode 100644 index 00000000..b06be91a --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_file_header.c @@ -0,0 +1,1081 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <time.h> + +#include "lha_endian.h" +#include "lha_file_header.h" +#include "ext_header.h" +#include "crc16.h" + +#define COMMON_HEADER_LEN 22 /* bytes */ + +// Minimum length of a level 0 header (with zero-length filename). +#define LEVEL_0_MIN_HEADER_LEN 22 /* bytes */ + +// Minimum length of a level 1 base header (with zero-length filename). +#define LEVEL_1_MIN_HEADER_LEN 25 /* bytes */ + +// Length of a level 2 base header. +#define LEVEL_2_HEADER_LEN 26 /* bytes */ + +// Length of a level 3 base header. +#define LEVEL_3_HEADER_LEN 32 /* bytes */ + +// Maximum length of a level 3 header (including extended headers). +#define LEVEL_3_MAX_HEADER_LEN (1024 * 1024) /* 1 MB */ + +// Length of a level 0 Unix extended area. +#define LEVEL_0_UNIX_EXTENDED_LEN 12 /* bytes */ + +// Length of a level 0 OS-9 extended area. +#define LEVEL_0_OS9_EXTENDED_LEN 22 /* bytes */ + +#define RAW_DATA(hdr_ptr, off) ((*hdr_ptr)->raw_data[off]) +#define RAW_DATA_LEN(hdr_ptr) ((*hdr_ptr)->raw_data_len) + +char *lha_file_header_full_path(LHAFileHeader *header) +{ + char *path; + char *filename; + char *result; + + if (header->path != NULL) { + path = header->path; + } else { + path = ""; + } + + if (header->filename != NULL) { + filename = header->filename; + } else { + filename = ""; + } + + result = malloc(strlen(path) + strlen(filename) + 1); + + if (result == NULL) { + return NULL; + } + + sprintf(result, "%s%s", path, filename); + + return result; +} + +/** + * Given a file header with the filename set, split it into separate + * path and filename components, if necessary. + * + * @param header Point to the file header structure. + * @return Non-zero for success, or zero for failure. + */ + +static int split_header_filename(LHAFileHeader *header) +{ + char *sep; + char *new_filename; + + // Is there a directory separator in the path? If so, we need to + // split into directory name and filename. + + sep = strrchr(header->filename, '/'); + + if (sep != NULL) { + new_filename = strdup(sep + 1); + + if (new_filename == NULL) { + return 0; + } + + *(sep + 1) = '\0'; + header->path = header->filename; + header->filename = new_filename; + } + + return 1; +} + +// Perform checksum of header contents. + +static int check_l0_checksum(uint8_t *header, size_t header_len, size_t csum) +{ + unsigned int result; + unsigned int i; + + result = 0; + + for (i = 0; i < header_len; ++i) { + result += header[i]; + } + + return (result & 0xff) == csum; +} + +// Perform full-header CRC check, based on CRC from "common" extended header. + +static int check_common_crc(LHAFileHeader *header) +{ + uint16_t crc; + + crc = 0; + lha_crc16_buf(&crc, header->raw_data, header->raw_data_len); + + return crc == header->common_crc; +} + +// Decode MS-DOS timestamp. + +static unsigned int decode_ftime(uint8_t *buf) +{ + int raw; + struct tm datetime; + + raw = (int) lha_decode_uint32(buf); + + if (raw == 0) { + return 0; + } + + // Deconstruct the contents of the MS-DOS time value and populate the + // 'datetime' structure. Note that 'mktime' generates a timestamp for + // the local time zone: this is unfortunate, but probably the best + // that can be done, due to the limited data stored in MS-DOS time + // values. + + memset(&datetime, 0, sizeof(struct tm)); + + datetime.tm_sec = (raw << 1) & 0x3e; + datetime.tm_min = (raw >> 5) & 0x3f; + datetime.tm_hour = (raw >> 11) & 0x1f; + datetime.tm_mday = (raw >> 16) & 0x1f; + datetime.tm_mon = ((raw >> 21) & 0xf) - 1; + datetime.tm_year = 80 + ((raw >> 25) & 0x7f); + datetime.tm_wday = 0; + datetime.tm_yday = 0; + datetime.tm_isdst = -1; + + return (unsigned int) mktime(&datetime); +} + +// MS-DOS archives (and archives from similar systems) may have paths and +// filenames that are in all-caps. Detect these and convert them to +// lower-case. + +static void fix_msdos_allcaps(LHAFileHeader *header) +{ + unsigned int i; + int is_allcaps; + + // Check both path and filename to see if there are any lower-case + // characters. + + is_allcaps = 1; + + if (header->path != NULL) { + for (i = 0; header->path[i] != '\0'; ++i) { + if (islower((unsigned) header->path[i])) { + is_allcaps = 0; + break; + } + } + } + + if (is_allcaps && header->filename != NULL) { + for (i = 0; header->filename[i] != '\0'; ++i) { + if (islower((unsigned) header->filename[i])) { + is_allcaps = 0; + break; + } + } + } + + // If both are all-caps, convert them all to lower-case. + + if (is_allcaps) { + if (header->path != NULL) { + for (i = 0; header->path[i] != '\0'; ++i) { + header->path[i] + = tolower((unsigned) header->path[i]); + } + } + if (header->filename != NULL) { + for (i = 0; header->filename[i] != '\0'; ++i) { + header->filename[i] + = tolower((unsigned) header->filename[i]); + } + } + } +} + +// Process the OS-9 permissions field and translate into the equivalent +// Unix permissions. + +static void os9_to_unix_permissions(LHAFileHeader *header) +{ + unsigned int or, ow, oe, pr, pw, pe, d; + + // Translate into equivalent Unix permissions. OS-9 just has + // owner and public, so double up public for the owner field. + + or = (header->os9_perms & 0x01) != 0; + ow = (header->os9_perms & 0x02) != 0; + oe = (header->os9_perms & 0x04) != 0; + pr = (header->os9_perms & 0x08) != 0; + pw = (header->os9_perms & 0x10) != 0; + pe = (header->os9_perms & 0x20) != 0; + d = (header->os9_perms & 0x80) != 0; + + header->extra_flags |= LHA_FILE_UNIX_PERMS; + header->unix_perms = (d << 14) + | (or << 8) | (ow << 7) | (oe << 6) // owner + | (pr << 5) | (pw << 4) | (pe << 3) // group + | (pr << 2) | (pw << 1) | (pe << 0); // everyone +} + +// Parse a Unix symbolic link. These are stored in the format: +// filename = symlink|target + +static int parse_symlink(LHAFileHeader *header) +{ + char *fullpath; + char *p; + + // Although the format is always the same, some files have + // symlink headers where the path is split between the path + // and filename headers. For example: + // path = etc|../../ + // filename = etc + + fullpath = lha_file_header_full_path(header); + + if (fullpath == NULL) { + return 0; + } + + p = strchr(fullpath, '|'); + + if (p == NULL) { + free(fullpath); + return 0; + } + + header->symlink_target = strdup(p + 1); + + if (header->symlink_target == NULL) { + free(fullpath); + return 0; + } + + // Cut the string in half at the separator. Keep the left side + // as the value for filename. + + *p = '\0'; + + free(header->path); + free(header->filename); + header->path = NULL; + header->filename = fullpath; + + // Having joined path and filename together during processing, + // we now have the opposite problem: header->filename might + // contain a full path rather than just a filename. Split back + // into two again. + + return split_header_filename(header); +} + +// Decode the path field in the header. + +static int process_level0_path(LHAFileHeader *header, uint8_t *data, + size_t data_len) +{ + unsigned int i; + + // Zero-length filename probably means that this is a directory + // entry. Leave the filename field as NULL - this makes us + // consistent with level 2/3 headers. + + if (data_len == 0) { + return 1; + } + + header->filename = malloc(data_len + 1); + + if (header->filename == NULL) { + return 0; + } + + memcpy(header->filename, data, data_len); + header->filename[data_len] = '\0'; + + // Convert MS-DOS path separators to Unix path separators. + + for (i = 0; i < data_len; ++i) { + if (header->filename[i] == '\\') { + header->filename[i] = '/'; + } + } + + return split_header_filename(header); +} + +// Read some more data from the input stream, extending the raw_data +// array (and the size of the header). + +static uint8_t *extend_raw_data(LHAFileHeader **header, + LHAInputStream *stream, + size_t nbytes) +{ + LHAFileHeader *new_header; + size_t new_raw_len; + uint8_t *result; + + if (nbytes > LEVEL_3_MAX_HEADER_LEN) { + return NULL; + } + + // Reallocate the header and raw_data area to be larger. + + new_raw_len = RAW_DATA_LEN(header) + nbytes; + new_header = realloc(*header, sizeof(LHAFileHeader) + new_raw_len); + + if (new_header == NULL) { + return NULL; + } + + // Update the header pointer to point to the new area. + + *header = new_header; + new_header->raw_data = (uint8_t *) (new_header + 1); + result = new_header->raw_data + new_header->raw_data_len; + + // Read data from stream into new area. + + if (!lha_input_stream_read(stream, result, nbytes)) { + return NULL; + } + + new_header->raw_data_len = new_raw_len; + + return result; +} + +// Starting at the specified offset in the raw_data array, walk +// through the list of extended headers and parse them. + +static int decode_extended_headers(LHAFileHeader **header, + unsigned int offset) +{ + unsigned int field_size; + uint8_t *ext_header; + size_t ext_header_len; + size_t available_length; + + // Level 3 headers use 32-bit length fields; all others use + // 16-bit fields. + + if ((*header)->header_level == 3) { + field_size = 4; + } else { + field_size = 2; + } + + available_length = RAW_DATA_LEN(header) - offset - field_size; + + while (offset <= RAW_DATA_LEN(header) - field_size) { + ext_header = &RAW_DATA(header, offset + field_size); + + if (field_size == 4) { + ext_header_len + = lha_decode_uint32(&RAW_DATA(header, offset)); + } else { + ext_header_len + = lha_decode_uint16(&RAW_DATA(header, offset)); + } + + // Header length zero indicates end of chain. Otherwise, sanity + // check the header length is valid. + + if (ext_header_len == 0) { + break; + } else if (ext_header_len < field_size + 1 + || ext_header_len > available_length) { + return 0; + } + + // Process header: + + lha_ext_header_decode(*header, ext_header[0], ext_header + 1, + ext_header_len - field_size - 1); + + // Advance to next header. + + offset += ext_header_len; + available_length -= ext_header_len; + } + + return 1; +} + +static int read_next_ext_header(LHAFileHeader **header, + LHAInputStream *stream, + uint8_t **ext_header, + size_t *ext_header_len) +{ + // Last two bytes of the header raw data contain the size + // of the next header. + + *ext_header_len + = lha_decode_uint16(&RAW_DATA(header, RAW_DATA_LEN(header) - 2)); + + // No more headers? + + if (*ext_header_len == 0) { + *ext_header = NULL; + return 1; + } + + *ext_header = extend_raw_data(header, stream, *ext_header_len); + + return *ext_header != NULL; +} + +// Read extended headers for a level 1 header, extending the +// raw_data block to include them. + +static int read_l1_extended_headers(LHAFileHeader **header, + LHAInputStream *stream) +{ + uint8_t *ext_header; + size_t ext_header_len; + + for (;;) { + // Try to read the next header. + + if (!read_next_ext_header(header, stream, + &ext_header, &ext_header_len)) { + return 0; + } + + // Last header? + + if (ext_header_len == 0) { + break; + } + + // For backwards compatibility with level 0 headers, + // the compressed length field is actually "compressed + // length + length of all extended headers": + + if ((*header)->compressed_length < ext_header_len) { + return 0; + } + + (*header)->compressed_length -= ext_header_len; + + // Must be at least 3 bytes - 1 byte header type + // + 2 bytes for next header length + + if (ext_header_len < 3) { + return 0; + } + } + + return 1; +} + +// Process a level 0 Unix extended area. + +static void process_level0_unix_area(LHAFileHeader *header, + uint8_t *data, size_t data_len) +{ + // A typical Unix extended area: + // + // 00000000 55 00 00 3b 3d 4b 80 81 e8 03 e8 03 + + // Sanity check. + + if (data_len < LEVEL_0_UNIX_EXTENDED_LEN || data[1] != 0x00) { + return; + } + + // OS-9/68k generates an extended area that is broadly compatible + // with the Unix one. + + // Fill in the header fields from the data from the extended area. + // There's one minor point to note here: OS-9/68k LHA includes the + // timestamp twice - I have no idea why. In order to support both + // variants, read the end fields from the end of the extended area. + + header->os_type = data[0]; + header->timestamp = lha_decode_uint32(data + 2); + + header->unix_perms = lha_decode_uint16(data + data_len - 6); + header->unix_uid = lha_decode_uint16(data + data_len - 4); + header->unix_gid = lha_decode_uint16(data + data_len - 2); + + header->extra_flags |= LHA_FILE_UNIX_PERMS | LHA_FILE_UNIX_UID_GID; +} + +// Process a level 0 OS-9 extended area. + +static void process_level0_os9_area(LHAFileHeader *header, + uint8_t *data, size_t data_len) +{ + // A typical OS-9 extended area: + // + // 00000000 39 13 00 00 c3 16 00 0f 00 cc 18 07 09 03 01 16 + // 00000010 00 13 00 00 00 00 + + // Sanity checks: + + if (data_len < LEVEL_0_OS9_EXTENDED_LEN + || data[9] != 0xcc || data[1] != data[17] || data[2] != data[18]) { + return; + } + + // The contents resemble the contents of the OS-9 extended header. + // We just want the permissions field. + + header->os_type = LHA_OS_TYPE_OS9; + header->os9_perms = lha_decode_uint16(data + 1); + header->extra_flags |= LHA_FILE_OS9_PERMS; +} + +// Handling for level 0 extended areas. + +static void process_level0_extended_area(LHAFileHeader *header, + uint8_t *data, size_t data_len) +{ + // PMarc archives can include comments that are stored in the + // extended area. It is possible that this could conflict with + // the logic below, so specifically exclude them. + + if (!strncmp(header->compress_method, "-pm", 3)) { + return; + } + + // Different tools include different extended areas. Try to + // identify which tool generated this one, based on the first + // byte. + + switch (data[0]) { + case LHA_OS_TYPE_UNIX: + case LHA_OS_TYPE_OS9_68K: + process_level0_unix_area(header, data, data_len); + break; + + case LHA_OS_TYPE_OS9: + process_level0_os9_area(header, data, data_len); + break; + + default: + break; + } +} + +// Decode a level 0 or 1 header. + +static int decode_level0_header(LHAFileHeader **header, LHAInputStream *stream) +{ + uint8_t header_len; + uint8_t header_csum; + size_t path_len; + size_t min_len; + + header_len = RAW_DATA(header, 0); + header_csum = RAW_DATA(header, 1); + + // Sanity check header length. This is the minimum header length + // for a header that has a zero-length path. + + switch ((*header)->header_level) { + case 0: + min_len = LEVEL_0_MIN_HEADER_LEN; + break; + case 1: + min_len = LEVEL_1_MIN_HEADER_LEN; + break; + + default: + return 0; + } + + if (header_len < min_len) { + return 0; + } + + // We only have a partial header so far. Read the full header. + + if (!extend_raw_data(header, stream, + header_len + 2 - RAW_DATA_LEN(header))) { + return 0; + } + + // Checksum the header. + + if (!check_l0_checksum(&RAW_DATA(header, 2), + RAW_DATA_LEN(header) - 2, + header_csum)) { + return 0; + } + + // Compression method: + + memcpy((*header)->compress_method, &RAW_DATA(header, 2), 5); + (*header)->compress_method[5] = '\0'; + + // File lengths: + + (*header)->compressed_length = lha_decode_uint32(&RAW_DATA(header, 7)); + (*header)->length = lha_decode_uint32(&RAW_DATA(header, 11)); + + // Timestamp: + + (*header)->timestamp = decode_ftime(&RAW_DATA(header, 15)); + + // Read path. Check path length field - is the header long enough + // to hold this full path? + + path_len = RAW_DATA(header, 21); + + if (min_len + path_len > header_len) { + return 0; + } + + // OS type? + + if ((*header)->header_level == 0) { + (*header)->os_type = LHA_OS_TYPE_UNKNOWN; + } else { + (*header)->os_type = RAW_DATA(header, 24 + path_len); + } + + // Read filename field: + + if (!process_level0_path(*header, &RAW_DATA(header, 22), path_len)) { + return 0; + } + + // CRC field. + + (*header)->crc = lha_decode_uint16(&RAW_DATA(header, 22 + path_len)); + + // Level 0 headers can contain extended data through different schemes + // to the extended header system used in level 1+. + + if ((*header)->header_level == 0 + && header_len > LEVEL_0_MIN_HEADER_LEN + path_len) { + process_level0_extended_area(*header, + &RAW_DATA(header, LEVEL_0_MIN_HEADER_LEN + 2 + path_len), + header_len - LEVEL_0_MIN_HEADER_LEN - path_len); + } + + return 1; +} + +static int decode_level1_header(LHAFileHeader **header, LHAInputStream *stream) +{ + unsigned int ext_header_start; + + if (!decode_level0_header(header, stream)) { + return 0; + } + + // Level 1 headers can have extended headers, so parse them. + + ext_header_start = RAW_DATA_LEN(header) - 2; + + if (!read_l1_extended_headers(header, stream) + || !decode_extended_headers(header, ext_header_start)) { + return 0; + } + + return 1; +} + +static int decode_level2_header(LHAFileHeader **header, LHAInputStream *stream) +{ + unsigned int header_len; + + header_len = lha_decode_uint16(&RAW_DATA(header, 0)); + + if (header_len < LEVEL_2_HEADER_LEN) { + return 0; + } + + // Read the full header. + + if (!extend_raw_data(header, stream, + header_len - RAW_DATA_LEN(header))) { + return 0; + } + + // Compression method: + + memcpy((*header)->compress_method, &RAW_DATA(header, 2), 5); + (*header)->compress_method[5] = '\0'; + + // File lengths: + + (*header)->compressed_length = lha_decode_uint32(&RAW_DATA(header, 7)); + (*header)->length = lha_decode_uint32(&RAW_DATA(header, 11)); + + // Timestamp. Unlike level 0/1, this is a Unix-style timestamp. + + (*header)->timestamp = lha_decode_uint32(&RAW_DATA(header, 15)); + + // CRC. + + (*header)->crc = lha_decode_uint16(&RAW_DATA(header, 21)); + + // OS type: + + (*header)->os_type = RAW_DATA(header, 23); + + // LHA for OS-9/68k generates broken level 2 archives: the header + // length field is the length of the remainder of the header, not + // the complete header length. As a result it's two bytes too + // short. We can use the OS type field to detect these archives + // and compensate. + + if ((*header)->os_type == LHA_OS_TYPE_OS9_68K) { + if (!extend_raw_data(header, stream, 2)) { + return 0; + } + } + + if (!decode_extended_headers(header, 24)) { + return 0; + } + + return 1; +} + +static int decode_level3_header(LHAFileHeader **header, LHAInputStream *stream) +{ + unsigned int header_len; + + // The first field at the start of a level 3 header is supposed to + // indicate word size, with the idea being that the header format + // can be extended beyond 32-bit words in the future. In practise, + // nothing supports anything other than 32-bit (4 bytes), and neither + // do we. + + if (lha_decode_uint16(&RAW_DATA(header, 0)) != 4) { + return 0; + } + + // Read the full header. + + if (!extend_raw_data(header, stream, + LEVEL_3_HEADER_LEN - RAW_DATA_LEN(header))) { + return 0; + } + + // Read the header length field (including extended headers), and + // extend to this full length. Because this is a 32-bit value, + // we must place a sensible limit on the amount of data that will + // be read, to avoid possibly allocating gigabytes of memory. + + header_len = lha_decode_uint32(&RAW_DATA(header, 24)); + + if (header_len > LEVEL_3_MAX_HEADER_LEN + || header_len < RAW_DATA_LEN(header)) { + return 0; + } + + if (!extend_raw_data(header, stream, + header_len - RAW_DATA_LEN(header))) { + return 0; + } + + // Compression method: + + memcpy((*header)->compress_method, &RAW_DATA(header, 2), 5); + (*header)->compress_method[5] = '\0'; + + // File lengths: + + (*header)->compressed_length = lha_decode_uint32(&RAW_DATA(header, 7)); + (*header)->length = lha_decode_uint32(&RAW_DATA(header, 11)); + + // Unix-style timestamp. + + (*header)->timestamp = lha_decode_uint32(&RAW_DATA(header, 15)); + + // CRC. + + (*header)->crc = lha_decode_uint16(&RAW_DATA(header, 21)); + + // OS type: + + (*header)->os_type = RAW_DATA(header, 23); + + if (!decode_extended_headers(header, 28)) { + return 0; + } + + return 1; +} + + +// "Collapse" a path down, by removing all instances of "." and ".." +// paths. This is to protect against malicious archives that might include +// ".." in a path to break out of the extract directory. + +static void collapse_path(char *filename) +{ + unsigned int currpath_len; + char *currpath; + char *r, *w; + + // If the path starts with a /, it is an absolute path; skip over + // that first character and don't remove it. + + if (filename[0] == '/') { + ++filename; + } + + // Step through each character, copying it from 'r' to 'w'. It + // is always the case that w <= r, and the final string will + // be equal in length or shorter than the original. + + currpath = filename; + w = filename; + + for (r = filename; *r != '\0'; ++r) { + *w++ = *r; + + // Each time a new path separator is found, examine the + // path that was just written. + + if (*r == '/') { + + currpath_len = w - currpath - 1; + + // Empty path (//) or current directory (.)? + + if (currpath_len == 0 + || (currpath_len == 1 && currpath[0] == '.')) { + w = currpath; + + // Parent directory (..)? + + } else if (currpath_len == 2 + && currpath[0] == '.' && currpath[1] == '.') { + + // Walk back up by one directory. Don't go + // past the start of the string. + + if (currpath == filename) { + w = filename; + } else { + w = currpath - 1; + + while (w > filename) { + if (*(w - 1) == '/') { + break; + } + --w; + } + + currpath = w; + } + + // Save for next time we start a new path. + + } else { + currpath = w; + } + } + } + + *w = '\0'; +} + +LHAFileHeader *lha_file_header_read(LHAInputStream *stream) +{ + LHAFileHeader *header; + int success; + + // We cannot decode the file header until we identify the + // header level (as different header level formats are + // decoded in different ways. The header level field is + // located at byte offset 20 within the header, so we + // must read the first 21 bytes to read it (actually this + // reads one byte more, so that we get the filename length + // byte for level 1 headers as well). + + // Allocate result structure. + + header = calloc(1, sizeof(LHAFileHeader) + COMMON_HEADER_LEN); + + if (header == NULL) { + return NULL; + } + + memset(header, 0, sizeof(LHAFileHeader)); + + header->_refcount = 1; + + // Read first chunk of header. + + header->raw_data = (uint8_t *) (header + 1); + header->raw_data_len = COMMON_HEADER_LEN; + + if (!lha_input_stream_read(stream, header->raw_data, + header->raw_data_len)) { + goto fail; + } + + // Identify header level, and decode header depending on + // the value encountered. + + header->header_level = header->raw_data[20]; + + switch (header->header_level) { + case 0: + success = decode_level0_header(&header, stream); + break; + + case 1: + success = decode_level1_header(&header, stream); + break; + + case 2: + success = decode_level2_header(&header, stream); + break; + + case 3: + success = decode_level3_header(&header, stream); + break; + + default: + success = 0; + break; + } + + if (!success) { + goto fail; + } + + // Sanity check that we got some headers, at least. + // Directory entries must have a path, and files must have a + // filename. Symlinks are stored using the same compression method + // field string (-lhd-) as directories. + + if (strcmp(header->compress_method, LHA_COMPRESS_TYPE_DIR) != 0) { + if (header->filename == NULL) { + goto fail; + } + } else if (!strcmp(header->compress_method, LHA_COMPRESS_TYPE_DIR) + && LHA_FILE_HAVE_EXTRA(header, LHA_FILE_UNIX_PERMS) + && (header->path != NULL || header->filename != NULL) + && (header->unix_perms & 0170000) == 0120000) { + + if (!parse_symlink(header)) { + goto fail; + } + + } else { + if (header->path == NULL) { + goto fail; + } + } + + // Is the path an all-caps filename? If so, it is a DOS path that + // should be translated to lower case. + + if (header->os_type == LHA_OS_TYPE_UNKNOWN + || header->os_type == LHA_OS_TYPE_MSDOS + || header->os_type == LHA_OS_TYPE_ATARI + || header->os_type == LHA_OS_TYPE_OS2) { + fix_msdos_allcaps(header); + } + + // Collapse special directory paths to ensure the path is clean. + + if (header->path != NULL) { + collapse_path(header->path); + } + + // Is this header generated by OS-9/68k LHA? If so, any Unix + // permissions are actually OS-9 permissions. + + if (header->os_type == LHA_OS_TYPE_OS9_68K + && LHA_FILE_HAVE_EXTRA(header, LHA_FILE_UNIX_PERMS)) { + header->os9_perms = header->unix_perms; + header->extra_flags |= LHA_FILE_OS9_PERMS; + } + + // If OS-9 permissions were read, translate into Unix permissions. + + if (LHA_FILE_HAVE_EXTRA(header, LHA_FILE_OS9_PERMS)) { + os9_to_unix_permissions(header); + } + + // Was the "common" extended header read, which contains a CRC of + // the full header? If so, perform a CRC check now. + + if (LHA_FILE_HAVE_EXTRA(header, LHA_FILE_COMMON_CRC) + && !check_common_crc(header)) { + goto fail; + } + + return header; +fail: + lha_file_header_free(header); + return NULL; +} + +void lha_file_header_free(LHAFileHeader *header) +{ + // Sanity check: + + if (header->_refcount == 0) { + return; + } + + // Count down references and only free when all have been removed. + + --header->_refcount; + + if (header->_refcount > 0) { + return; + } + + free(header->filename); + free(header->path); + free(header->symlink_target); + free(header->unix_username); + free(header->unix_group); + free(header); +} + +void lha_file_header_add_ref(LHAFileHeader *header) +{ + ++header->_refcount; +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_file_header.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_file_header.h new file mode 100644 index 00000000..2111f9e7 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_file_header.h @@ -0,0 +1,68 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + + +#ifndef LHASA_LHA_FILE_HEADER_H +#define LHASA_LHA_FILE_HEADER_H + +#include "public/lha_file_header.h" +#include "lha_input_stream.h" + +/** + * Read a file header from the input stream. + * + * @param stream The input stream to read from. + * @return Pointer to a new LHAFileHeader structure, or NULL + * if an error occurred or a valid header could not + * be read. + */ + +LHAFileHeader *lha_file_header_read(LHAInputStream *stream); + +/** + * Free a file header structure. + * + * @param header The file header to free. + */ + +void lha_file_header_free(LHAFileHeader *header); + +/** + * Add a reference to the specified file header, to stop it from being + * freed. + * + * @param header The file header to add a reference to. + */ + +void lha_file_header_add_ref(LHAFileHeader *header); + +/** + * Get the full path for the given file header. + * + * @param header Pointer to the file header structure. + * @return Pointer to an allocated string containing the full + * file or directory path, or NULL for failure. The + * string must be freed by the caller. + */ + +char *lha_file_header_full_path(LHAFileHeader *header); + +#endif /* #ifndef LHASA_LHA_FILE_HEADER_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_input_stream.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_input_stream.c new file mode 100644 index 00000000..2fe8d96c --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_input_stream.c @@ -0,0 +1,403 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include "lha_arch.h" +#include "lha_input_stream.h" + +// Maximum length of the self-extractor header. +// If we don't find an LHA file header after this many bytes, give up. + +#define MAX_SFX_HEADER_LEN 65536 + +// Size of the lead-in buffer used to skip the self-extractor. + +#define LEADIN_BUFFER_LEN 24 + +// Magic string to detect an Amiga LhASFX self-extracting file. +// This type of self-extractor is special because the program itself +// contains a mini-LHA file that must be skipped over to get to +// the real one. + +#define AMIGA_LHASFX_ID "LhASFX V1.2," + +typedef enum { + LHA_INPUT_STREAM_INIT, + LHA_INPUT_STREAM_READING, + LHA_INPUT_STREAM_FAIL +} LHAInputStreamState; + +struct _LHAInputStream { + const LHAInputStreamType *type; + void *handle; + LHAInputStreamState state; + uint8_t leadin[LEADIN_BUFFER_LEN]; + size_t leadin_len; +}; + +LHAInputStream *lha_input_stream_new(const LHAInputStreamType *type, + void *handle) +{ + LHAInputStream *result; + + result = calloc(1, sizeof(LHAInputStream)); + + if (result == NULL) { + return NULL; + } + + result->type = type; + result->handle = handle; + result->leadin_len = 0; + result->state = LHA_INPUT_STREAM_INIT; + + return result; +} + +void lha_input_stream_free(LHAInputStream *stream) +{ + // Close the input stream. + + if (stream->type->close != NULL) { + stream->type->close(stream->handle); + } + + free(stream); +} + +// Check if the specified buffer is the start of a file header. + +static int file_header_match(uint8_t *buf) +{ + if (buf[2] != '-' || buf[6] != '-') { + return 0; + } + + // LHA algorithm? + + if (buf[3] == 'l' && buf[4] == 'h') { + return 1; + } + + // LArc algorithm (lz4, lz5, lzs)? + + if (buf[3] == 'l' && buf[4] == 'z' + && (buf[5] == '4' || buf[5] == '5' || buf[5] == 's')) { + return 1; + } + + // PMarc algorithm? (pm0, pm2) + // Note: PMarc SFX archives have a -pms- string in them that must + // be ignored. + + if (buf[3] == 'p' && buf[4] == 'm' && buf[5] != 's') { + return 1; + } + + return 0; +} + +// Empty some of the bytes from the start of the lead-in buffer. + +static void empty_leadin(LHAInputStream *stream, size_t bytes) +{ + memmove(stream->leadin, stream->leadin + bytes, + stream->leadin_len - bytes); + stream->leadin_len -= bytes; +} + +// Read bytes from the input stream into the specified buffer. + +static int do_read(LHAInputStream *stream, void *buf, size_t buf_len) +{ + return stream->type->read(stream->handle, buf, buf_len); +} + +// Skip the self-extractor header at the start of the file. +// Returns non-zero if a header was found. + +static int skip_sfx(LHAInputStream *stream) +{ + size_t filepos; + unsigned int i; + int skip_files; + int read; + + filepos = 0; + skip_files = 0; + + while (filepos < MAX_SFX_HEADER_LEN) { + + // Add some more bytes to the lead-in buffer: + + read = do_read(stream, stream->leadin + stream->leadin_len, + LEADIN_BUFFER_LEN - stream->leadin_len); + + if (read <= 0) { + break; + } + + stream->leadin_len += (unsigned int) read; + + // Check the lead-in buffer for a file header. + + for (i = 0; i + 12 < stream->leadin_len; ++i) { + if (file_header_match(stream->leadin + i)) { + if (skip_files == 0) { + empty_leadin(stream, i); + return 1; + } else { + --skip_files; + } + } + + // Detect Amiga self-extractor. + + if (!memcmp(stream->leadin + i, AMIGA_LHASFX_ID, + strlen(AMIGA_LHASFX_ID))) { + skip_files = 1; + } + } + + empty_leadin(stream, i); + filepos += i; + } + + return 0; +} + +int lha_input_stream_read(LHAInputStream *stream, void *buf, size_t buf_len) +{ + size_t total_bytes, n; + int result; + + // Start of the stream? Skip self-extract header, if there is one. + + if (stream->state == LHA_INPUT_STREAM_INIT) { + if (skip_sfx(stream)) { + stream->state = LHA_INPUT_STREAM_READING; + } else { + stream->state = LHA_INPUT_STREAM_FAIL; + } + } + + if (stream->state == LHA_INPUT_STREAM_FAIL) { + return 0; + } + + // Now fill the result buffer. Start by emptying the lead-in buffer. + + total_bytes = 0; + + if (stream->leadin_len > 0) { + if (buf_len < stream->leadin_len) { + n = buf_len; + } else { + n = stream->leadin_len; + } + + memcpy(buf, stream->leadin, n); + empty_leadin(stream, n); + total_bytes += n; + } + + // Read from the input stream. + + if (total_bytes < buf_len) { + result = do_read(stream, (uint8_t *) buf + total_bytes, + buf_len - total_bytes); + + if (result > 0) { + total_bytes += (unsigned int) result; + } + } + + // Only successful if the complete buffer is filled. + + return total_bytes == buf_len; +} + +int lha_input_stream_skip(LHAInputStream *stream, size_t bytes) +{ + // If we have a dedicated skip function, use it; otherwise, + // the read function can be used to perform a skip. + + if (stream->type->skip != NULL) { + return stream->type->skip(stream->handle, bytes); + } else { + uint8_t data[32]; + unsigned int len; + int result; + + while (bytes > 0) { + + // Read as many bytes left as possible to fit in + // the buffer: + + if (bytes > sizeof(data)) { + len = sizeof(data); + } else { + len = bytes; + } + + result = do_read(stream, data, len); + + if (result < 0) { + return 0; + } + + bytes -= (unsigned int) result; + } + + return 1; + } +} + +// Read data from a FILE * source. + +static int file_source_read(void *handle, void *buf, size_t buf_len) +{ + size_t bytes_read; + FILE *fh = handle; + + bytes_read = fread(buf, 1, buf_len, fh); + + // If an error occurs, zero is returned; however, it may also + // indicate end of file. + + if (bytes_read == 0 && !feof(fh)) { + return -1; + } + + return (int) bytes_read; +} + +// "Fallback" skip for file source that uses fread(), for unseekable +// streams. + +static int file_source_skip_fallback(FILE *handle, size_t bytes) +{ + uint8_t data[32]; + unsigned int len; + int result; + + while (bytes > 0) { + if (bytes > sizeof(data)) { + len = sizeof(data); + } else { + len = bytes; + } + + result = fread(data, 1, len, handle); + + if (result != (int) len) { + return 0; + } + + bytes -= len; + } + + return 1; +} + +// Seek forward in a FILE * input stream. + +static int file_source_skip(void *handle, size_t bytes) +{ + int result; + + // If this is an unseekable stream of some kind, always use the + // fallback behavior, as at least this is guaranteed to work. + // This is to work around problems on Windows, where fseek() can + // seek half-way on a stream and *then* fail, leaving us in an + // unworkable situation. + + if (ftell(handle) < 0) { + return file_source_skip_fallback(handle, bytes); + } + + result = fseek(handle, (long) bytes, SEEK_CUR); + + if (result < 0) { + if (errno == EBADF || errno == ESPIPE) { + return file_source_skip_fallback(handle, bytes); + } else { + return 0; + } + } + + return 1; +} + +// Close a FILE * input stream. + +static void file_source_close(void *handle) +{ + fclose(handle); +} + +// "Owned" file source - the stream will be closed when the input +// stream is freed. + +static const LHAInputStreamType file_source_owned = { + file_source_read, + file_source_skip, + file_source_close +}; + +// "Unowned" file source - the stream is owned by the calling code. + +static const LHAInputStreamType file_source_unowned = { + file_source_read, + file_source_skip, + NULL +}; + +LHAInputStream *lha_input_stream_from(char *filename) +{ + LHAInputStream *result; + FILE *fstream; + + fstream = fopen(filename, "rb"); + + if (fstream == NULL) { + return NULL; + } + + result = lha_input_stream_new(&file_source_owned, fstream); + + if (result == NULL) { + fclose(fstream); + } + + return result; +} + +LHAInputStream *lha_input_stream_from_FILE(FILE *stream) +{ + lha_arch_set_binary(stream); + return lha_input_stream_new(&file_source_unowned, stream); +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_input_stream.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_input_stream.h new file mode 100644 index 00000000..645825c1 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_input_stream.h @@ -0,0 +1,51 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_LHA_INPUT_STREAM_H +#define LHASA_LHA_INPUT_STREAM_H + +#include <inttypes.h> +#include "public/lha_input_stream.h" + +/** + * Read a block of data from the LHA stream, of the specified number + * of bytes. + * + * @param stream The input stream. + * @param buf Pointer to buffer in which to store read data. + * @param buf_len Size of buffer, in bytes. + * @return Non-zero if buffer was filled, or zero if an + * error occurred, or end of file was reached. + */ + +int lha_input_stream_read(LHAInputStream *stream, void *buf, size_t buf_len); + +/** + * Skip over the specified number of bytes. + * + * @param stream The input stream. + * @param bytes Number of bytes to skip. + * @return Non-zero for success, zero for failure. + */ + +int lha_input_stream_skip(LHAInputStream *stream, size_t bytes); + +#endif /* #ifndef LHASA_LHA_INPUT_STREAM_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_reader.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_reader.c new file mode 100644 index 00000000..267b68f5 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lha_reader.c @@ -0,0 +1,886 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lha_arch.h" +#include "lha_decoder.h" +#include "lha_basic_reader.h" +#include "public/lha_reader.h" +#include "macbinary.h" + +typedef enum { + + // Initial state at start of stream: + + CURR_FILE_START, + + // Current file is a "normal" file (or directory) read from + // the input stream. + + CURR_FILE_NORMAL, + + // Current file is a directory that has been popped from the + // directory stack. + + CURR_FILE_FAKE_DIR, + + // Current file is a deferred symbolic link that has been left + // to the end of the input stream to be created. + + CURR_FILE_DEFERRED_SYMLINK, + + // End of input stream has been reached. + + CURR_FILE_EOF, +} CurrFileType; + +struct _LHAReader { + LHABasicReader *reader; + + // The current file that we are processing (last file returned + // by lha_reader_next_file). + + LHAFileHeader *curr_file; + CurrFileType curr_file_type; + + // Pointer to decoder being used to decompress the current file, + // or NULL if we have not yet started decompression. + + LHADecoder *decoder; + + // Pointer to "inner" decoder. Most of the time, + // decoder == inner_decoder, but when decoding an archive + // generated by MacLHA, inner_decoder points to the actual + // decompressor. + + LHADecoder *inner_decoder; + + // Policy used to extract directories. + + LHAReaderDirPolicy dir_policy; + + // Directories that have been created by lha_reader_extract but + // have not yet had their metadata set. This is a linked list + // using the _next field in LHAFileHeader. + // In the case of LHA_READER_DIR_END_OF_DIR this is a stack; + // in the case of LHA_READER_DIR_END_OF_FILE it is a list. + + LHAFileHeader *dir_stack; + + // Symbolic links containing absolute paths or '..' are not + // created immediately - instead, "placeholder" files are created + // in their place, and the symbolic links created at the end + // of extraction. + + LHAFileHeader *deferred_symlinks; +}; + +/** + * Free the current decoder structure. + * + * If the reader has an allocated decoder being used to decompress the + * current file, the decoder is freed and the decoder pointer reset + * to NULL. + * + * @param reader Pointer to the LHA reader structure. + */ + +static void close_decoder(LHAReader *reader) +{ + if (reader->decoder != NULL) { + if (reader->inner_decoder == reader->decoder) { + reader->inner_decoder = NULL; + } + + lha_decoder_free(reader->decoder); + reader->decoder = NULL; + } + + if (reader->inner_decoder != NULL) { + lha_decoder_free(reader->inner_decoder); + reader->inner_decoder = NULL; + } +} + +/** + * Create the decoder structure to decompress the data from the + * current file. + * + * @param reader Pointer to the LHA reader structure. + * @param callback Callback function to invoke to track progress. + * @param callback_data Extra pointer to pass to the callback function. + * @return Non-zero for success, zero for failure. + */ + +static int open_decoder(LHAReader *reader, + LHADecoderProgressCallback callback, + void *callback_data) +{ + // Can only read from a normal file. + + if (reader->curr_file_type != CURR_FILE_NORMAL) { + return 0; + } + + reader->inner_decoder = lha_basic_reader_decode(reader->reader); + + if (reader->inner_decoder == NULL) { + return 0; + } + + // Set progress callback for decoder. + + if (callback != NULL) { + lha_decoder_monitor(reader->inner_decoder, + callback, callback_data); + } + + // Some archives generated by MacLHA have a MacBinary header + // attached to the start, which contains MacOS-specific + // metadata about the compressed file. These are identified + // and stripped off, using a "passthrough" decoder. + + if (reader->curr_file->os_type == LHA_OS_TYPE_MACOS) { + reader->decoder = lha_macbinary_passthrough( + reader->inner_decoder, reader->curr_file); + + if (reader->decoder == NULL) { + return 0; + } + } else { + reader->decoder = reader->inner_decoder; + } + + return 1; +} + +LHAReader *lha_reader_new(LHAInputStream *stream) +{ + LHABasicReader *basic_reader; + LHAReader *reader; + + reader = calloc(1, sizeof(LHAReader)); + + if (reader == NULL) { + return NULL; + } + + basic_reader = lha_basic_reader_new(stream); + + if (basic_reader == NULL) { + free(reader); + return NULL; + } + + reader->reader = basic_reader; + reader->curr_file = NULL; + reader->curr_file_type = CURR_FILE_START; + reader->decoder = NULL; + reader->inner_decoder = NULL; + reader->dir_stack = NULL; + reader->dir_policy = LHA_READER_DIR_END_OF_DIR; + reader->deferred_symlinks = NULL; + + return reader; +} + +void lha_reader_free(LHAReader *reader) +{ + LHAFileHeader *header; + + // Shut down the current decoder, if there is one. + + close_decoder(reader); + + // Free any file headers in the stack. + + while (reader->dir_stack != NULL) { + header = reader->dir_stack; + reader->dir_stack = header->_next; + lha_file_header_free(header); + } + + lha_basic_reader_free(reader->reader); + free(reader); +} + +void lha_reader_set_dir_policy(LHAReader *reader, + LHAReaderDirPolicy policy) +{ + reader->dir_policy = policy; +} + +/** + * Check if the directory at the top of the stack should be popped. + * + * Extracting a directory is a two stage process; after the directory + * is created, it is pushed onto the directory stack. Later the + * directory must be popped off the stack and its metadata applied. + * + * @param reader Pointer to the LHA reader structure. + * @return Non-zero if there is a directory at the top of + * the stack that should be popped. + */ + +static int end_of_top_dir(LHAReader *reader) +{ + LHAFileHeader *input; + + // No directories to pop? + + if (reader->dir_stack == NULL) { + return 0; + } + + // Once the end of the input stream is reached, all that is + // left to do is pop off the remaining directories. + + input = lha_basic_reader_curr_file(reader->reader); + + if (input == NULL) { + return 1; + } + + switch (reader->dir_policy) { + + // Shouldn't happen? + + case LHA_READER_DIR_PLAIN: + default: + return 1; + + // Don't process directories until we reach the end of + // the input stream. + + case LHA_READER_DIR_END_OF_FILE: + return 0; + + // Once we reach a file from the input that is not within + // the directory at the top of the stack, we have reached + // the end of that directory, so we can pop it off. + + case LHA_READER_DIR_END_OF_DIR: + return input->path == NULL + || strncmp(input->path, + reader->dir_stack->path, + strlen(reader->dir_stack->path)) != 0; + } +} + +// Read the next file from the input stream. + +LHAFileHeader *lha_reader_next_file(LHAReader *reader) +{ + // Free the current decoder if there is one. + + close_decoder(reader); + + // No point continuing once the end of the input stream has + // been reached. + + if (reader->curr_file_type == CURR_FILE_EOF) { + return NULL; + } + + // Advance to the next file from the input stream? + // Don't advance until we've done the fake directories first. + + if (reader->curr_file_type == CURR_FILE_START + || reader->curr_file_type == CURR_FILE_NORMAL) { + lha_basic_reader_next_file(reader->reader); + } + + // If the last file we returned was a 'fake' directory, we must + // now unreference it. + + if (reader->curr_file_type == CURR_FILE_FAKE_DIR) { + lha_file_header_free(reader->curr_file); + } + + // Pop off all appropriate directories from the stack first. + + if (end_of_top_dir(reader)) { + reader->curr_file = reader->dir_stack; + reader->dir_stack = reader->dir_stack->_next; + reader->curr_file_type = CURR_FILE_FAKE_DIR; + } else { + reader->curr_file = lha_basic_reader_curr_file(reader->reader); + reader->curr_file_type = CURR_FILE_NORMAL; + } + + // Once we reach the end of the file, there may be deferred + // symbolic links still to extract, so process those before + // giving up and declaring end of file. + + if (reader->curr_file == NULL) { + if (reader->deferred_symlinks != NULL) { + reader->curr_file = reader->deferred_symlinks; + reader->curr_file_type = CURR_FILE_DEFERRED_SYMLINK; + + reader->deferred_symlinks = + reader->deferred_symlinks->_next; + reader->curr_file->_next = NULL; + } else { + reader->curr_file_type = CURR_FILE_EOF; + } + } + + return reader->curr_file; +} + +size_t lha_reader_read(LHAReader *reader, void *buf, size_t buf_len) +{ + // The first time that we try to read the current file, we + // must create the decoder to decompress it. + + if (reader->decoder == NULL) { + if (!open_decoder(reader, NULL, NULL)) { + return 0; + } + } + + // Read from decoder and return the result. + + return lha_decoder_read(reader->decoder, buf, buf_len); +} + +/** + * Decompress the current file. + * + * Assumes that @param open_decoder has already been called to + * start the decode process. + * + * @param reader Pointer to the LHA reader structure. + * @param output FILE handle to write decompressed data, or NULL + * if the decompressed data should be discarded. + * @return Non-zero if the file decompressed successfully. + */ + +static int do_decode(LHAReader *reader, FILE *output) +{ + uint8_t buf[64]; + unsigned int bytes; + + // Decompress the current file. + + do { + bytes = lha_reader_read(reader, buf, sizeof(buf)); + + if (output != NULL) { + if (fwrite(buf, 1, bytes, output) < bytes) { + return 0; + } + } + + } while (bytes > 0); + + // Decoder stores output position and performs running CRC. + // At the end of the stream these should match the header values. + + return lha_decoder_get_length(reader->inner_decoder) + == reader->curr_file->length + && lha_decoder_get_crc(reader->inner_decoder) + == reader->curr_file->crc; +} + +int lha_reader_check(LHAReader *reader, + LHADecoderProgressCallback callback, + void *callback_data) +{ + if (reader->curr_file_type != CURR_FILE_NORMAL) { + return 0; + } + + // CRC checking of directories is not necessary. + + if (!strcmp(reader->curr_file->compress_method, + LHA_COMPRESS_TYPE_DIR)) { + return 1; + } + + // Decode file. + + return open_decoder(reader, callback, callback_data) + && do_decode(reader, NULL); +} + +/** + * Open an output stream into which to decompress the current file. + * + * @param reader Pointer to the LHA reader structure. + * @param filename Name of the file to open. + * @return FILE handle of the opened file, or NULL in + * case of failure. + */ + +static FILE *open_output_file(LHAReader *reader, char *filename) +{ + int unix_uid = -1, unix_gid = -1, unix_perms = -1; + + if (LHA_FILE_HAVE_EXTRA(reader->curr_file, LHA_FILE_UNIX_UID_GID)) { + unix_uid = reader->curr_file->unix_uid; + unix_gid = reader->curr_file->unix_gid; + } + + if (LHA_FILE_HAVE_EXTRA(reader->curr_file, LHA_FILE_UNIX_PERMS)) { + unix_perms = reader->curr_file->unix_perms; + } + + return lha_arch_fopen(filename, unix_uid, unix_gid, unix_perms); +} + +/** + * Set file timestamps for the specified file. + * + * If possible, the more accurate Windows timestamp values are used; + * otherwise normal Unix timestamps are used. + * + * @param path Path to the file or directory to set. + * @param header Pointer to file header structure containing the + * timestamps to set. + * @return Non-zero if the timestamps were set successfully, + * or zero for failure. + */ + +static int set_timestamps_from_header(char *path, LHAFileHeader *header) +{ +#if LHA_ARCH == LHA_ARCH_WINDOWS + if (LHA_FILE_HAVE_EXTRA(header, LHA_FILE_WINDOWS_TIMESTAMPS)) { + return lha_arch_set_windows_timestamps( + path, + header->win_creation_time, + header->win_modification_time, + header->win_access_time + ); + } else // .... +#endif + if (header->timestamp != 0) { + return lha_arch_utime(path, header->timestamp); + } else { + return 1; + } +} + +/** + * Set directory metadata. + * + * This is the second stage of directory extraction. Metadata (timestamps + * and permissions) should be set on a dictory after the contents of + * the directory has been extracted. + * + * @param header Pointer to file header structure containing the + * metadata to set. + * @param path Path to the directory on which to set the metadata. + * @return Non-zero for success, or zero for failure. + */ + +static int set_directory_metadata(LHAFileHeader *header, char *path) +{ + // Set timestamp: + + set_timestamps_from_header(path, header); + + // Set owner and group: + + if (LHA_FILE_HAVE_EXTRA(header, LHA_FILE_UNIX_UID_GID)) { + if (!lha_arch_chown(path, header->unix_uid, + header->unix_gid)) { + // On most Unix systems, only root can change + // ownership. But if we can't change ownership, + // it isn't a fatal error. Ignore the failure + // and continue. + + // TODO: Implement some kind of alternate handling + // here? + /* return 0; */ + } + } + + // Set permissions on directory: + + if (LHA_FILE_HAVE_EXTRA(header, LHA_FILE_UNIX_PERMS)) { + if (!lha_arch_chmod(path, header->unix_perms)) { + return 0; + } + } + + return 1; +} + +/** + * "Extract" (create) a directory. + * + * The current file is assumed to be a directory. This is the first + * stage in extracting a directory; after the directory is created, + * it is added to the directory stack so that the metadata apply stage + * runs later. (If the LHA_READER_DIR_PLAIN policy is used, metadata + * is just applied now). + * + * @param reader Pointer to the LHA reader structure. + * @param path Path to the directory, or NULL to use the path from + * the file header. + * @return Non-zero for success, or zero for failure. + */ + +static int extract_directory(LHAReader *reader, char *path) +{ + LHAFileHeader *header; + unsigned int mode; + + header = reader->curr_file; + + // If path is not specified, use the path from the file header. + + if (path == NULL) { + path = header->path; + } + + // Create directory. If there are permissions to be set, create + // the directory with minimal permissions limited to the running + // user. Otherwise use the default umask. + + if (LHA_FILE_HAVE_EXTRA(header, LHA_FILE_UNIX_PERMS)) { + mode = 0700; + } else { + mode = 0777; + } + + if (!lha_arch_mkdir(path, mode)) { + + // If the attempt to create the directory failed, it may + // be because the directory already exists. Return success + // if this is the case; it isn't really an error. + + return lha_arch_exists(path) == LHA_FILE_DIRECTORY; + } + + // The directory has been created, but the metadata has not yet + // been applied. It depends on the directory policy how this + // is handled. If we are using LHA_READER_DIR_PLAIN, set + // metadata now. Otherwise, save the directory for later. + + if (reader->dir_policy == LHA_READER_DIR_PLAIN) { + set_directory_metadata(header, path); + } else { + lha_file_header_add_ref(header); + header->_next = reader->dir_stack; + reader->dir_stack = header; + } + + return 1; +} + +/** + * Extract the current file. + * + * @param reader Pointer to the LHA reader structure. + * @param filename Filename into which to extract the file, or NULL + * to use the filename from the file header. + * @param callback Callback function to invoke to track progress. + * @param callback_data Extra pointer to pass to the callback function. + * @return Non-zero if the file was successfully extracted, + * or zero for failure. + */ + +static int extract_file(LHAReader *reader, char *filename, + LHADecoderProgressCallback callback, + void *callback_data) +{ + FILE *fstream; + char *tmp_filename = NULL; + int result; + + // Construct filename? + + if (filename == NULL) { + tmp_filename = lha_file_header_full_path(reader->curr_file); + + if (tmp_filename == NULL) { + return 0; + } + + filename = tmp_filename; + } + + // Create decoder. If the file cannot be created, there is no + // need to even create an output file. If successful, open the + // output file and decode. + + result = 0; + + if (open_decoder(reader, callback, callback_data)) { + + fstream = open_output_file(reader, filename); + + if (fstream != NULL) { + result = do_decode(reader, fstream); + fclose(fstream); + } + } + + // Set timestamp on file: + + if (result) { + set_timestamps_from_header(filename, reader->curr_file); + } + + free(tmp_filename); + + return result; +} + +/** + * Determine whether a header contains a "dangerous" symbolic link. + * + * Symbolic links that begin with '/' or contain '..' as a path are + * Potentially dangerous and could potentially be used to overwrite + * arbitrary files on the filesystem. They therefore need to be + * treated specially. + * + * @param header Pointer to a header structure defining a symbolic + * link. + * @return Non-zero if the symbolic link is potentially + * dangerous. + */ + +static int is_dangerous_symlink(LHAFileHeader *header) +{ + char *path_start; + char *p; + + if (header->symlink_target == NULL) { + return 0; + } + + // Absolute path symlinks could be used to point to arbitrary + // filesystem locations. + + if (header->symlink_target[0] == '/') { + return 1; + } + + // Check for paths containing '..'. + + path_start = header->symlink_target; + + for (p = header->symlink_target; *p != '\0'; ++p) { + if (*p == '/') { + if ((p - path_start) == 2 + && path_start[0] == '.' && path_start[1] == '.') { + return 1; + } + + path_start = p + 1; + } + } + + // The path might also end with '..' (no terminating /) + + if ((p - path_start) == 2 + && path_start[0] == '.' && path_start[1] == '.') { + return 1; + } + + return 0; +} + +/** + * Get the length of a path defined by a file header. + * + * @param header The file header structure. + * @return Length of the header in bytes. + */ + +static size_t file_header_path_len(LHAFileHeader *header) +{ + size_t result; + + result = 0; + + if (header->path != NULL) { + result += strlen(header->path); + } + if (header->filename != NULL) { + result += strlen(header->filename); + } + + return result; +} + +/** + * Create a "placeholder" symbolic link. + * + * When a "dangerous" symbolic link is extracted, instead of creating it + * immediately, create a "placeholder" empty file to go in its place, and + * place it into the deferred_symlinks list to be created later. + * + * @param reader Pointer to the LHA reader structure. + * @param filename Filename into which to extract the symlink. + * @return Non-zero if the symlink was extracted successfully, + * or zero for failure. + */ + +static int extract_placeholder_symlink(LHAReader *reader, char *filename) +{ + LHAFileHeader **rover; + FILE *f; + + f = lha_arch_fopen(filename, -1, -1, 0600); + + if (f == NULL) { + return 0; + } + + fclose(f); + + // Insert this header into the list of deferred symbolic links. + // The list must be maintained in order of decreasing path length, + // so that one symbolic link cannot depend on another. For example: + // + // etc -> /etc + // etc/passwd -> /malicious_path/passwd + + rover = &reader->deferred_symlinks; + + while (*rover != NULL + && file_header_path_len(*rover) + > file_header_path_len(reader->curr_file)) { + rover = &(*rover)->_next; + } + + reader->curr_file->_next = *rover; + *rover = reader->curr_file; + + // Save reference to the header so it won't be freed. + + lha_file_header_add_ref(reader->curr_file); + + return 1; +} + +/** + * Extract a Unix symbolic link. + * + * @param reader Pointer to the LHA reader structure. + * @param filename Filename into which to extract the symlink, or NULL + * to use the filename from the file header. + * @return Non-zero if the symlink was extracted successfully, + * or zero for failure. + */ + +static int extract_symlink(LHAReader *reader, char *filename) +{ + char *tmp_filename = NULL; + int result; + + // Construct filename? + + if (filename == NULL) { + tmp_filename = lha_file_header_full_path(reader->curr_file); + + if (tmp_filename == NULL) { + return 0; + } + + filename = tmp_filename; + } + + if (reader->curr_file_type == CURR_FILE_NORMAL + && is_dangerous_symlink(reader->curr_file)) { + return extract_placeholder_symlink(reader, filename); + } + + result = lha_arch_symlink(filename, reader->curr_file->symlink_target); + + // TODO: Set symlink timestamp. + + free(tmp_filename); + + return result; +} + +/** + * Extract a "normal" file. + * + * This just extracts the file header most recently read by the + * BasicReader. + * + * @param reader Pointer to the LHA reader structure. + * @param filename Filename into which to extract the file, or NULL + * to use the filename from the file header. + * @param callback Callback function to invoke to track progress. + * @param callback_data Extra pointer to pass to the callback function. + * @return Non-zero if the file was successfully extracted, + * or zero for failure. + */ + +static int extract_normal(LHAReader *reader, + char *filename, + LHADecoderProgressCallback callback, + void *callback_data) +{ + if (strcmp(reader->curr_file->compress_method, + LHA_COMPRESS_TYPE_DIR) != 0) { + return extract_file(reader, filename, callback, callback_data); + } else if (reader->curr_file->symlink_target != NULL) { + return extract_symlink(reader, filename); + } else { + return extract_directory(reader, filename); + } +} + +int lha_reader_extract(LHAReader *reader, + char *filename, + LHADecoderProgressCallback callback, + void *callback_data) +{ + switch (reader->curr_file_type) { + + case CURR_FILE_NORMAL: + return extract_normal(reader, filename, callback, + callback_data); + + case CURR_FILE_FAKE_DIR: + if (filename == NULL) { + filename = reader->curr_file->path; + } + set_directory_metadata(reader->curr_file, filename); + return 1; + + case CURR_FILE_DEFERRED_SYMLINK: + return extract_symlink(reader, filename); + + case CURR_FILE_START: + case CURR_FILE_EOF: + break; + } + + return 0; +} + +int lha_reader_current_is_fake(LHAReader *reader) +{ + return reader->curr_file_type == CURR_FILE_FAKE_DIR + || reader->curr_file_type == CURR_FILE_DEFERRED_SYMLINK; +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lhx_decoder.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lhx_decoder.c new file mode 100644 index 00000000..1bbf6c54 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lhx_decoder.c @@ -0,0 +1,45 @@ +/* + +Copyright (c) 2011, 2012, 2013, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// +// Decoder for the -lhx- algorithm. Provided by Multi. +// +// -lhx- is Unlha32.dll's original extension. Some unique archivers +// support it. +// + +// 128 KiB history ring buffer: +// -lhx-'s maximum dictionary size is 2^19. 2x ring buffer is required. + +#define HISTORY_BITS 20 /* 2^20 = 1048576. */ + +// Number of bits to encode HISTORY_BITS: + +#define OFFSET_BITS 5 + +// Name of the variable for the encoder: + +#define DECODER_NAME lha_lhx_decoder + +// The actual algorithm code is contained in lh_new_decoder.c, which +// acts as a template for -lh4-, -lh5-, -lh6-, -lh7- and -lhx-. + +#include "lh_new_decoder.c" + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lz5_decoder.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lz5_decoder.c new file mode 100644 index 00000000..bddcdc1b --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lz5_decoder.c @@ -0,0 +1,198 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> + +#include "lha_decoder.h" + +// Parameters for ring buffer, used for storing history. This acts +// as the dictionary for copy operations. + +#define RING_BUFFER_SIZE 4096 +#define START_OFFSET 18 + +// Threshold offset. In the copy operation, the copy length is a 4-bit +// value, giving a range 0..15. The threshold offsets this so that it +// is interpreted as 3..18 - a more useful range. + +#define THRESHOLD 3 + +// Size of output buffer. Must be large enough to hold the results of +// a complete "run" (see below). + +#define OUTPUT_BUFFER_SIZE (15 + THRESHOLD) * 8 + +// Decoder for the -lz5- compression method used by LArc. +// +// This processes "runs" of eight commands, each of which is either +// "output a character" or "copy block". The result of that run +// is written into the output buffer. + +typedef struct { + uint8_t ringbuf[RING_BUFFER_SIZE]; + unsigned int ringbuf_pos; + LHADecoderCallback callback; + void *callback_data; +} LHALZ5Decoder; + +static void fill_initial(LHALZ5Decoder *decoder) +{ + unsigned int i, j; + uint8_t *p; + + p = decoder->ringbuf; + + // For each byte value, the history buffer includes a run of 13 + // bytes all with that value. This is useful eg. for text files + // that include a long run like this (eg. ===========). + for (i = 0; i < 256; ++i) { + for (j = 0; j < 13; ++j) { + *p++ = (uint8_t) i; + } + } + + // Next we include all byte values ascending and descending. + for (i = 0; i < 256; ++i) { + *p++ = (uint8_t) i; + } + for (i = 0; i < 256; ++i) { + *p++ = (uint8_t) (255 - i); + } + + // Block of zeros, and then ASCII space characters. I think these are + // towards the end of the range because they're most likely to be + // useful and therefore last to get overwritten? + for (i = 0; i < 128; ++i) { + *p++ = 0; + } + for (i = 0; i < 110; ++i) { + *p++ = ' '; + } + + // Final 18 characters are all zeros, probably because of START_OFFSET. + for (i = 0; i < 18; ++i) { + *p++ = 0; + } +} + +static int lha_lz5_init(void *data, LHADecoderCallback callback, + void *callback_data) +{ + LHALZ5Decoder *decoder = data; + + fill_initial(decoder); + decoder->ringbuf_pos = RING_BUFFER_SIZE - START_OFFSET; + decoder->callback = callback; + decoder->callback_data = callback_data; + + return 1; +} + +// Add a single byte to the output buffer. + +static void output_byte(LHALZ5Decoder *decoder, uint8_t *buf, + size_t *buf_len, uint8_t b) +{ + buf[*buf_len] = b; + ++*buf_len; + + decoder->ringbuf[decoder->ringbuf_pos] = b; + decoder->ringbuf_pos = (decoder->ringbuf_pos + 1) % RING_BUFFER_SIZE; +} + +// Output a "block" of data from the specified range in the ring buffer. + +static void output_block(LHALZ5Decoder *decoder, + uint8_t *buf, + size_t *buf_len, + unsigned int start, + unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; ++i) { + output_byte(decoder, buf, buf_len, + decoder->ringbuf[(start + i) % RING_BUFFER_SIZE]); + } +} + +// Process a "run" of LZ5-compressed data (a control byte followed by +// eight "commands"). + +static size_t lha_lz5_read(void *data, uint8_t *buf) +{ + LHALZ5Decoder *decoder = data; + uint8_t bitmap; + unsigned int bit; + size_t result; + + // Start from an empty buffer. + + result = 0; + + // Read the bitmap byte first. + + if (!decoder->callback(&bitmap, 1, decoder->callback_data)) { + return 0; + } + + // Each bit in the bitmap is a command. + // If the bit is set, it is an "output byte" command. + // If it is not set, it is a "copy block" command. + + for (bit = 0; bit < 8; ++bit) { + if ((bitmap & (1 << bit)) != 0) { + uint8_t b; + + if (!decoder->callback(&b, 1, decoder->callback_data)) { + break; + } + + output_byte(decoder, buf, &result, b); + } else { + uint8_t cmd[2]; + unsigned int seqstart, seqlen; + + if (!decoder->callback(cmd, 2, decoder->callback_data)) { + break; + } + + seqstart = (((unsigned int) cmd[1] & 0xf0) << 4) + | cmd[0]; + seqlen = ((unsigned int) cmd[1] & 0x0f) + THRESHOLD; + + output_block(decoder, buf, &result, seqstart, seqlen); + } + } + + return result; +} + +LHADecoderType lha_lz5_decoder = { + lha_lz5_init, + NULL, + lha_lz5_read, + sizeof(LHALZ5Decoder), + OUTPUT_BUFFER_SIZE, + RING_BUFFER_SIZE +}; + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lzs_decoder.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lzs_decoder.c new file mode 100644 index 00000000..87efc929 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/lzs_decoder.c @@ -0,0 +1,156 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> + +#include "lha_decoder.h" + +#include "bit_stream_reader.c" + +// Parameters for ring buffer, used for storing history. This acts +// as the dictionary for copy operations. + +#define RING_BUFFER_SIZE 2048 +#define START_OFFSET 17 + +// Threshold offset. In the copy operation, the copy length is a 4-bit +// value, giving a range 0..15. The threshold offsets this so that it +// is interpreted as 2..17 - a more useful range. + +#define THRESHOLD 2 + +// Size of output buffer. Must be large enough to hold the results of +// the maximum copy operation. + +#define OUTPUT_BUFFER_SIZE (15 + THRESHOLD) + +// Decoder for the -lzs- compression method used by old versions of LArc. +// +// The input stream consists of commands, each of which is either "output +// a literal byte value" or "copy block". A bit at the start of each +// command signals which command it is. + +typedef struct { + BitStreamReader bit_stream_reader; + uint8_t ringbuf[RING_BUFFER_SIZE]; + unsigned int ringbuf_pos; +} LHALZSDecoder; + +static int lha_lzs_init(void *data, LHADecoderCallback callback, + void *callback_data) +{ + LHALZSDecoder *decoder = data; + + memset(decoder->ringbuf, ' ', RING_BUFFER_SIZE); + decoder->ringbuf_pos = RING_BUFFER_SIZE - START_OFFSET; + bit_stream_reader_init(&decoder->bit_stream_reader, callback, + callback_data); + + return 1; +} + +// Add a single byte to the output buffer. + +static void output_byte(LHALZSDecoder *decoder, uint8_t *buf, + size_t *buf_len, uint8_t b) +{ + buf[*buf_len] = b; + ++*buf_len; + + decoder->ringbuf[decoder->ringbuf_pos] = b; + decoder->ringbuf_pos = (decoder->ringbuf_pos + 1) % RING_BUFFER_SIZE; +} + +// Output a "block" of data from the specified range in the ring buffer. + +static void output_block(LHALZSDecoder *decoder, + uint8_t *buf, + size_t *buf_len, + unsigned int start, + unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; ++i) { + output_byte(decoder, buf, buf_len, + decoder->ringbuf[(start + i) % RING_BUFFER_SIZE]); + } +} + +// Process a single command from the LZS input stream. + +static size_t lha_lzs_read(void *data, uint8_t *buf) +{ + LHALZSDecoder *decoder = data; + int bit; + size_t result; + + // Start from an empty buffer. + + result = 0; + + // Each command starts with a bit that signals the type: + + bit = read_bit(&decoder->bit_stream_reader); + + if (bit < 0) { + return 0; + } + + // What type of command is this? + + if (bit) { + int b; + + b = read_bits(&decoder->bit_stream_reader, 8); + + if (b < 0) { + return 0; + } + + output_byte(decoder, buf, &result, (uint8_t) b); + } else { + int pos, len; + + pos = read_bits(&decoder->bit_stream_reader, 11); + len = read_bits(&decoder->bit_stream_reader, 4); + + if (pos < 0 || len < 0) { + return 0; + } + + output_block(decoder, buf, &result, (unsigned int) pos, + (unsigned int) len + THRESHOLD); + } + + return result; +} + +LHADecoderType lha_lzs_decoder = { + lha_lzs_init, + NULL, + lha_lzs_read, + sizeof(LHALZSDecoder), + OUTPUT_BUFFER_SIZE, + RING_BUFFER_SIZE +}; + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/macbinary.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/macbinary.c new file mode 100644 index 00000000..be2ba532 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/macbinary.c @@ -0,0 +1,451 @@ +/* + +Copyright (c) 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// Code for handling MacBinary headers. +// +// Classic Mac OS attaches more metadata to files than other operating +// systems. For example, each file has a file type that is used to +// determine the application to open it with. Files can also have both +// a data fork and a resource fork. Because of this design, when +// transferring a file between computers (eg. over a network), all of +// the data associated with the file must be bundled up together to +// preserve the file. +// +// MacLHA uses the MacBinary container format to do this. Within the +// compressed data, the file contents are preceded by a 128 byte +// header that contains the metadata. The data from the data fork can +// also be followed by the data from the resource fork. +// +// Because this is incompatible with .lzh archives from other operating +// systems, MacLHA has two menu items to create new archives - one +// creates a "Mac" archive, while the other creates a "non-Mac" +// (standard) archive that contains just the file contents. This quote +// from the documentation (MacLHAE.doc) describes what is stored when +// the latter option is used: +// +// > If a file has only either Data Fork or Resource Fork, it's stored +// > into archives. In case a file has both Data Fork and Resource Fork, +// > only the Data Fork is stored. +// +// -- +// +// Mac OS X has essentially abandoned this practise of using filesystem +// metadata and other systems do not use it, either. It is therefore +// sensible and desirable to strip off the MacBinary header (if present) +// and extract just the normal file contents. It makes sense to use the +// same strategy quoted above. +// +// The possible presence of a MacBinary header can be inferred using the +// OS type field from the LHA header - a value of 'm' indicates that it +// was generated by MacLHA. However, there are some issues with this: +// +// 1. This type is set regardless of whether a MacBinary header is +// attached or not. There is no other field to indicate the +// difference, and MacBinary headers do not have a magic number, so +// the presence of one must be determined heuristically. +// Realistically, though, this can be done without too much +// difficulty, by strictly checking all the fields in the MacBinary +// header. If an invalid header is seen, it can be rejected and +// assumed to be a normal file. +// +// 2. MacBinary is a standard container format for transferring files +// between Macs and not used just by MacLHA. Therefore, it is +// plausible that a .lzh archive might "deliberately" contain a +// MacBinary file, in which case it would be a mistake to strip +// off the header. +// +// This is an unlikely but still a plausible scenario. It can be +// mitigated by comparing the MacBinary header values against the +// values from the .lzh header. A header added by MacLHA will have +// a filename that matches the .lzh header's filename (MacBinary +// files usually have a .bin extension appended, so the filenames +// would not match. Also, the modification timestamp should match +// the timestamp from the .lzh header. +// +// 3. Comparing the MacBinary header modification timestamp with the +// .lzh header modification timestamp is complicated by the fact +// that the former is stored as a Macintosh 1904-based timestamp +// in the local timezone, while the latter is stored as a Unix +// timestamp in UTC time. Although converting timestamp formats +// is trivial, the two do not compare exactly due to the timezone +// offset. +// +// -- +// +// Summary of MacBinary header fields and policy for each +// (Z = check zero, C = check value, I = ignore): +// +// 0x00 - Z - "Old version number", must be zero for compatibility +// 0x01 - C - Filename length, must match .lzh header filename. +// 0x02-0x40 - C - Filename, must match .lzh header filename. +// Z - Remainder following filename contents must be zero +// 0x41-0x44 - I - File type +// 0x45-0x48 - I - File creator +// 0x49 - I - Finder flags +// 0x4a - Z - "Must be zero for compatibility" +// 0x4b-0x4c - I - Icon vertical position +// 0x4d-0x4e - I - Icon horizonal position +// 0x4f-0x50 - I - Window ID +// 0x51 - I - "Protected" flag +// 0x52 - Z - "Must be zero for compatibility" +// 0x53-0x56 - C - Data fork length }- added together, equal uncompressed +// 0x57-0x5a - C - Resource fork length }- data length rounded up to 256 +// 0x5b-0x5e - I - File creation date +// 0x5f-0x62 - C - File modification date - should match .lzh header +// 0x63-0x64 - Z - Finder "Get Info" comment length - unused by MacLHA +// 0x65-0x7f - Z - MacBinary II data - unused by MacLHA + +#include <stdlib.h> +#include <string.h> + +#include "lha_decoder.h" +#include "lha_endian.h" +#include "lha_file_header.h" + +#define OUTPUT_BUFFER_SIZE 4096 /* bytes */ + +// Classic Mac OS represents time in seconds since 1904, instead of +// Unix time's 1970 epoch. This is the difference between the two. + +#define MAC_TIME_OFFSET 2082844800 /* seconds */ + +// Size of the MacBinary header. + +#define MBHDR_SIZE 128 /* bytes */ + +// Offsets of fields in MacBinary header (and their sizes): + +#define MBHDR_OFF_VERSION 0x00 +#define MBHDR_OFF_FILENAME_LEN 0x01 +#define MBHDR_OFF_FILENAME 0x02 +#define MBHDR_LEN_FILENAME 63 +#define MBHDR_OFF_ZERO_COMPAT1 0x4a +#define MBHDR_OFF_ZERO_COMPAT2 0x52 +#define MBHDR_OFF_DATA_FORK_LEN 0x53 +#define MBHDR_OFF_RES_FORK_LEN 0x57 +#define MBHDR_OFF_FILE_MOD_DATE 0x5f +#define MBHDR_OFF_COMMENT_LEN 0x63 +#define MBHDR_OFF_MACBINARY2_DATA 0x65 +#define MBHDR_LEN_MACBINARY2_DATA (MBHDR_SIZE - MBHDR_OFF_MACBINARY2_DATA) + +// Check that the given block of data contains only zero bytes. + +static int block_is_zero(uint8_t *data, size_t data_len) +{ + unsigned int i; + + for (i = 0; i < data_len; ++i) { + if (data[i] != 0) { + return 0; + } + } + + return 1; +} + +// Check that the specified modification time matches the modification +// time from the file header. + +static int check_modification_time(unsigned int mod_time, + LHAFileHeader *header) +{ + unsigned int time_diff; + + // In an ideal world, mod_time should match header->timestamp + // exactly. However, there's an additional complication + // because mod_time is local time, not UTC time, so there is + // a timezone difference. + + if (header->timestamp > mod_time) { + time_diff = header->timestamp - mod_time; + } else { + time_diff = mod_time - header->timestamp; + } + + // The maximum UTC timezone difference is UTC+14, used in + // New Zealand and some other islands in the Pacific. + + if (time_diff > 14 * 60 * 60) { + return 0; + } + + // If the world was simpler, all time zones would be exact + // hour offsets, but in fact, some regions use half or + // quarter hour offsets. So the difference should be a + // multiple of 15 minutes. Actually, the control panel in + // Mac OS allows any minute offset to be configured, but if + // people are crazy enough to do that, they deserve the + // brokenness they get as a result. It's preferable to use + // a 15 minute check rather than a 1 minute check, because + // this allows MacLHA-added MacBinary headers to be + // distinguished from archived MacBinary files more reliably. + + //return (time_diff % (15 * 60)) == 0; + + // It turns out the assumption above doesn't hold, and MacLHA + // does generate archives where the timestamps don't always + // exactly match. Oh well. + + return 1; +} + +// Given the specified data buffer, check whether it has a MacBinary +// header with contents that match the specified .lzh header. + +static int is_macbinary_header(uint8_t *data, LHAFileHeader *header) +{ + unsigned int filename_len; + unsigned int data_fork_len, res_fork_len, expected_len; + unsigned int mod_time; + + // Check fields in the header that should be zero. + + if (data[MBHDR_OFF_VERSION] != 0 + || data[MBHDR_OFF_ZERO_COMPAT1] != 0 + || data[MBHDR_OFF_ZERO_COMPAT2] != 0 + || !block_is_zero(&data[MBHDR_OFF_COMMENT_LEN], 2) + || !block_is_zero(&data[MBHDR_OFF_MACBINARY2_DATA], + MBHDR_LEN_MACBINARY2_DATA)) { + return 0; + } + + // Check that the filename matches the filename from the + // lzh header. + + filename_len = data[MBHDR_OFF_FILENAME_LEN]; + + if (filename_len > MBHDR_LEN_FILENAME + || filename_len != strlen(header->filename) + || memcmp(&data[MBHDR_OFF_FILENAME], + header->filename, filename_len) != 0) { + return 0; + } + + // Data following the filename must be zero as well. + + if (!block_is_zero(data + MBHDR_OFF_FILENAME + filename_len, + MBHDR_LEN_FILENAME - filename_len)) { + return 0; + } + + // Decode data fork / resource fork lengths. Their combined + // lengths, plus the MacBinary header, should match the + // compressed data length (rounded up to the nearest 128). + + data_fork_len = lha_decode_be_uint32(&data[MBHDR_OFF_DATA_FORK_LEN]); + res_fork_len = lha_decode_be_uint32(&data[MBHDR_OFF_RES_FORK_LEN]); + + expected_len = (data_fork_len + res_fork_len + MBHDR_SIZE); + + if (header->length != ((expected_len + 0x7f) & ~0x7f)) { + return 0; + } + + // Check modification time. + + mod_time = lha_decode_be_uint32(&data[MBHDR_OFF_FILE_MOD_DATE]); + + if (mod_time < MAC_TIME_OFFSET + || !check_modification_time(mod_time - MAC_TIME_OFFSET, header)) { + return 0; + } + + return 1; +} + +// +// MacBinary "decoder". This reuses the LHADecoder framework to provide +// a "pass-through" decoder that detects and strips the MacBinary header. +// + +typedef struct { + + // When the decoder is initialized, the first 128 bytes of + // data are read into this buffer and analysed. If it is + // not a MacBinary header, the data must be kept so that it + // can be returned in the first call to .read(). + // mb_header_bytes contains the number of bytes still to read. + + uint8_t mb_header[MBHDR_SIZE]; + size_t mb_header_bytes; + + // The "inner" decoder used to read the compressed data. + + LHADecoder *decoder; + + // Number of bytes still to read before decode should be + // terminated. + + size_t stream_remaining; +} MacBinaryDecoder; + +// Structure used when initializing a MacBinaryDecoder. + +typedef struct { + LHADecoder *decoder; + LHAFileHeader *header; +} MacBinaryDecoderClosure; + +static int read_macbinary_header(MacBinaryDecoder *decoder, + LHAFileHeader *header) +{ + unsigned int data_fork_len, res_fork_len; + size_t n, bytes; + + bytes = 0; + + while (bytes < MBHDR_SIZE) { + n = lha_decoder_read(decoder->decoder, + decoder->mb_header + bytes, + MBHDR_SIZE - bytes); + + // Unexpected EOF? + + if (n == 0) { + return 0; + } + + bytes += n; + } + + // Check if the data that was read corresponds to a MacBinary + // header that matches the .lzh header. If not, just decode it + // as a normal stream. + + if (!is_macbinary_header(decoder->mb_header, header)) { + decoder->mb_header_bytes = bytes; + return 1; + } + + // We have a MacBinary header, so skip over it. Decide how + // long the data stream is (see policy in comment at start + // of file). + + decoder->mb_header_bytes = 0; + + data_fork_len = lha_decode_be_uint32( + &decoder->mb_header[MBHDR_OFF_DATA_FORK_LEN]); + res_fork_len = lha_decode_be_uint32( + &decoder->mb_header[MBHDR_OFF_RES_FORK_LEN]); + + if (data_fork_len > 0) { + decoder->stream_remaining = data_fork_len; + } else { + decoder->stream_remaining = res_fork_len; + } + + return 1; +} + +static int macbinary_decoder_init(void *_decoder, + LHADecoderCallback callback, + void *_closure) +{ + MacBinaryDecoder *decoder = _decoder; + MacBinaryDecoderClosure *closure = _closure; + + decoder->decoder = closure->decoder; + decoder->mb_header_bytes = 0; + decoder->stream_remaining = closure->header->length; + + if (closure->header->length >= MBHDR_SIZE + && !read_macbinary_header(decoder, closure->header)) { + return 0; + } + + return 1; +} + +static void decode_to_end(LHADecoder *decoder) +{ + uint8_t buf[128]; + size_t n; + + do { + n = lha_decoder_read(decoder, buf, sizeof(buf)); + } while (n > 0); +} + +static size_t macbinary_decoder_read(void *_decoder, uint8_t *buf) +{ + MacBinaryDecoder *decoder = _decoder; + size_t result; + size_t to_read; + size_t n; + + result = 0; + + // If there is data from the mb_header buffer waiting to be + // read, add it first. + + if (decoder->mb_header_bytes > 0) { + memcpy(buf, decoder->mb_header, decoder->mb_header_bytes); + result = decoder->mb_header_bytes; + decoder->mb_header_bytes = 0; + } + + // Read further data, if there is some in the stream still to read. + + to_read = OUTPUT_BUFFER_SIZE - result; + + if (to_read > decoder->stream_remaining) { + to_read = decoder->stream_remaining; + } + + n = lha_decoder_read(decoder->decoder, buf + result, to_read); + + decoder->stream_remaining -= n; + result += n; + + // Once the end of the stream is reached, there may still be + // data from the inner decoder to decompress. When this happens, + // run the decoder until the end. + + if (decoder->stream_remaining == 0) { + decode_to_end(decoder->decoder); + } + + return result; +} + +static LHADecoderType macbinary_decoder_type = { + macbinary_decoder_init, + NULL, + macbinary_decoder_read, + sizeof(MacBinaryDecoder), + OUTPUT_BUFFER_SIZE, + 0, +}; + +LHADecoder *lha_macbinary_passthrough(LHADecoder *decoder, + LHAFileHeader *header) +{ + MacBinaryDecoderClosure closure; + LHADecoder *result; + + closure.decoder = decoder; + closure.header = header; + + result = lha_decoder_new(&macbinary_decoder_type, NULL, + &closure, header->length); + + return result; +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/macbinary.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/macbinary.h new file mode 100644 index 00000000..09fd11a2 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/macbinary.h @@ -0,0 +1,49 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_MACBINARY_H +#define LHASA_MACBINARY_H + +#include "lha_decoder.h" +#include "lha_file_header.h" + +/** + * Create a passthrough decoder to handle MacBinary headers added + * by MacLHA. + * + * The new decoder reads from the specified decoder and filters + * out the header. The contents of the MacBinary header must match + * the details from the specified file header. + * + * @param decoder The "inner" decoder from which to read data. + * @param header The file header, that the contents of the + * MacBinary header must match. + * @return A new decoder, which passes through the + * contents of the inner decoder, stripping + * off the MacBinary header and truncating + * as appropriate. Both decoders must be freed + * by the caller. + */ + +LHADecoder *lha_macbinary_passthrough(LHADecoder *decoder, + LHAFileHeader *header); + +#endif /* #ifndef LHASA_MACBINARY_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/null_decoder.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/null_decoder.c new file mode 100644 index 00000000..170a2206 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/null_decoder.c @@ -0,0 +1,61 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// Null decoder, for uncompressed files. + +#include <stdlib.h> +#include <inttypes.h> + +#include "lha_decoder.h" + +#define BLOCK_READ_SIZE 1024 + +typedef struct { + LHADecoderCallback callback; + void *callback_data; +} LHANullDecoder; + +static int lha_null_init(void *data, LHADecoderCallback callback, + void *callback_data) +{ + LHANullDecoder *decoder = data; + + decoder->callback = callback; + decoder->callback_data = callback_data; + + return 1; +} + +static size_t lha_null_read(void *data, uint8_t *buf) +{ + LHANullDecoder *decoder = data; + + return decoder->callback(buf, BLOCK_READ_SIZE, decoder->callback_data); +} + +LHADecoderType lha_null_decoder = { + lha_null_init, + NULL, + lha_null_read, + sizeof(LHANullDecoder), + BLOCK_READ_SIZE, + 2048 +}; + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/pm1_decoder.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/pm1_decoder.c new file mode 100644 index 00000000..9fc16037 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/pm1_decoder.c @@ -0,0 +1,714 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// Decoder for -pm1- compressed files. +// +// This was difficult to put together. I can't find any versions of +// PMarc that will generate -pm1- encoded files (only -pm2-); however, +// the extraction tool, PMext, will extract them. I have therefore been +// able to reverse engineer the format and write a decoder. I am +// indebted to Alwin Henseler for publishing the Z80 assembly source to +// his UNPMA10 tool, which was apparently decompiled from the original +// PMarc and includes the -pm1- decoding code. + +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> + +#include "lha_decoder.h" +#include "bit_stream_reader.c" +#include "pma_common.c" + +// Size of the ring buffer used to hold the history. + +#define RING_BUFFER_SIZE 16384 + +// Maximum length of a command representing a block of bytes: + +#define MAX_BYTE_BLOCK_LEN 216 + +// Maximum number of bytes that can be copied by a single copy command. + +#define MAX_COPY_BLOCK_LEN 244 + +// Output buffer length. A single call to lha_pm1_read can perform one +// byte block output followed by a copy command. + +#define OUTPUT_BUFFER_SIZE (MAX_BYTE_BLOCK_LEN + MAX_COPY_BLOCK_LEN) + +typedef struct { + BitStreamReader bit_stream_reader; + + // Position in output stream, in bytes. + + unsigned int output_stream_pos; + + // Pointer to the entry in byte_decode_table used to decode + // byte value indices. + + const uint8_t *byte_decode_tree; + + // History ring buffer. + + uint8_t ringbuf[RING_BUFFER_SIZE]; + unsigned int ringbuf_pos; + + // History linked list, for adaptively encoding byte values. + + HistoryLinkedList history_list; + + // Callback to read more compressed data from the input (see + // read_callback_wrapper below). + + LHADecoderCallback callback; + void *callback_data; +} LHAPM1Decoder; + +// Table used to decode distance into history buffer to copy data. + +static const VariableLengthTable copy_ranges[] = { + { 0, 6 }, // 0 + (1 << 6) = 64 + { 64, 8 }, // 64 + (1 << 8) = 320 + { 0, 6 }, // 0 + (1 << 6) = 64 + { 64, 9 }, // 64 + (1 << 9) = 576 + { 576, 11 }, // 576 + (1 << 11) = 2624 + { 2624, 13 }, // 2624 + (1 << 13) = 10816 + + // The above table entries are used after a certain number of + // bytes have been decoded. + // Early in the stream, some of the copy ranges are more limited + // in their range, so that fewer bits are needed. The above + // table entries are redirected to these entries instead. + + // Table entry #3 (64): + { 64, 8 }, // < 320 bytes + + // Table entry #4 (576): + { 576, 8 }, // < 832 bytes + { 576, 9 }, // < 1088 bytes + { 576, 10 }, // < 1600 bytes + + // Table entry #5 (2624): + { 2624, 8 }, // < 2880 bytes + { 2624, 9 }, // < 3136 bytes + { 2624, 10 }, // < 3648 bytes + { 2624, 11 }, // < 4672 bytes + { 2624, 12 }, // < 6720 bytes +}; + +// Table used to decode byte values. + +static const VariableLengthTable byte_ranges[] = { + { 0, 4 }, // 0 + (1 << 4) = 16 + { 16, 4 }, // 16 + (1 << 4) = 32 + { 32, 5 }, // 32 + (1 << 5) = 64 + { 64, 6 }, // 64 + (1 << 6) = 128 + { 128, 6 }, // 128 + (1 << 6) = 191 + { 192, 6 }, // 192 + (1 << 6) = 255 +}; + +// This table is a list of trees to decode indices into byte_ranges. +// Each line is actually a mini binary tree, starting with the first +// byte as the root node. Each nybble of the byte is one of the two +// branches: either a leaf value (a-f) or an offset to the child node. +// Expanded representation is shown in comments below. + +static const uint8_t byte_decode_trees[][5] = { + { 0x12, 0x2d, 0xef, 0x1c, 0xab }, // ((((a b) c) d) (e f)) + { 0x12, 0x23, 0xde, 0xab, 0xcf }, // (((a b) (c f)) (d e)) + { 0x12, 0x2c, 0xd2, 0xab, 0xef }, // (((a b) c) (d (e f))) + { 0x12, 0xa2, 0xd2, 0xbc, 0xef }, // ((a (b c)) (d (e f))) + + { 0x12, 0xa2, 0xc2, 0xbd, 0xef }, // ((a (b d)) (c (e f))) + { 0x12, 0xa2, 0xcd, 0xb1, 0xef }, // ((a (b (e f))) (c d)) + { 0x12, 0xab, 0x12, 0xcd, 0xef }, // ((a b) ((c d) (e f))) + { 0x12, 0xab, 0x1d, 0xc1, 0xef }, // ((a b) ((c (e f)) d)) + + { 0x12, 0xab, 0xc1, 0xd1, 0xef }, // ((a b) (c (d (e f)))) + { 0xa1, 0x12, 0x2c, 0xde, 0xbf }, // (a (((b f) c) (d e))) + { 0xa1, 0x1d, 0x1c, 0xb1, 0xef }, // (a (((b (e f)) c) d)) + { 0xa1, 0x12, 0x2d, 0xef, 0xbc }, // (a (((b c) d) (e f))) + + { 0xa1, 0x12, 0xb2, 0xde, 0xcf }, // (a ((b (c f)) (d e))) + { 0xa1, 0x12, 0xbc, 0xd1, 0xef }, // (a ((b c) (d (e f)))) + { 0xa1, 0x1c, 0xb1, 0xd1, 0xef }, // (a ((b (d (e f))) c)) + { 0xa1, 0xb1, 0x12, 0xcd, 0xef }, // (a (b ((c d) (e f)))) + + { 0xa1, 0xb1, 0xc1, 0xd1, 0xef }, // (a (b (c (d (e f))))) + { 0x12, 0x1c, 0xde, 0xab }, // (((d e) c) (d e)) <- BROKEN! + { 0x12, 0xa2, 0xcd, 0xbe }, // ((a (b e)) (c d)) + { 0x12, 0xab, 0xc1, 0xde }, // ((a b) (c (d e))) + + { 0xa1, 0x1d, 0x1c, 0xbe }, // (a (((b e) c) d)) + { 0xa1, 0x12, 0xbc, 0xde }, // (a ((b c) (d e))) + { 0xa1, 0x1c, 0xb1, 0xde }, // (a ((b (d e)) c)) + { 0xa1, 0xb1, 0xc1, 0xde }, // (a (b (c (d e)))) + + { 0x1d, 0x1c, 0xab }, // (((a b) c) d) + { 0x1c, 0xa1, 0xbd }, // ((a (b d)) c) + { 0x12, 0xab, 0xcd }, // ((a b) (c d)) + { 0xa1, 0x1c, 0xbd }, // (a ((b d) c)) + + { 0xa1, 0xb1, 0xcd }, // (a (b (c d))) + { 0xa1, 0xbc }, // (a (b c)) + { 0xab }, // (a b) + { 0x00 }, // -- special entry: 0, no tree +}; + +// Wrapper function invoked to read more data from the input. This mostly just +// calls the real function that does the read. However, when the end of file +// is reached, instead of returning zero, the buffer is filled with zero bytes +// instead. There seem to be archive files that actually depend on this +// ability to read "beyond" the length of the compressed data. + +static size_t read_callback_wrapper(void *buf, size_t buf_len, void *user_data) +{ + LHAPM1Decoder *decoder = user_data; + size_t result; + + result = decoder->callback(buf, buf_len, decoder->callback_data); + + if (result == 0) { + memset(buf, 0, buf_len); + result = buf_len; + } + + return result; +} + +static int lha_pm1_init(void *data, LHADecoderCallback callback, + void *callback_data) +{ + LHAPM1Decoder *decoder = data; + + memset(decoder, 0, sizeof(LHAPM1Decoder)); + + // Unlike other decoders, the bitstream code must call the wrapper + // function above to read data. + + decoder->callback = callback; + decoder->callback_data = callback_data; + + bit_stream_reader_init(&decoder->bit_stream_reader, + read_callback_wrapper, decoder); + + decoder->output_stream_pos = 0; + decoder->byte_decode_tree = NULL; + decoder->ringbuf_pos = 0; + + init_history_list(&decoder->history_list); + + return 1; +} + +// Read the 5-bit header from the start of the input stream. This +// specifies the table entry to use for byte decodes. + +static int read_start_header(LHAPM1Decoder *decoder) +{ + int index; + + index = read_bits(&decoder->bit_stream_reader, 5); + + if (index < 0) { + return 0; + } + + decoder->byte_decode_tree = byte_decode_trees[index]; + + return 1; +} + +// Function called when a new byte is outputted, to update the +// appropriate data structures. + +static void outputted_byte(LHAPM1Decoder *decoder, uint8_t b) +{ + // Add to history ring buffer. + + decoder->ringbuf[decoder->ringbuf_pos] = b; + decoder->ringbuf_pos + = (decoder->ringbuf_pos + 1) % RING_BUFFER_SIZE; + + // Other updates: history linked list, output stream position: + + update_history_list(&decoder->history_list, b); + ++decoder->output_stream_pos; +} + +// Decode a count of the number of bytes to copy in a copy command. +// Returns -1 for failure. + +static int read_copy_byte_count(LHAPM1Decoder *decoder) +{ + int x; + + // This is a form of static huffman encoding that uses less bits + // to encode short copy amounts (again). + + // Value in the range 3..5? + // Length values start at 3: if it was 2, a different copy + // range would have been used and this function would not + // have been called. + + x = read_bits(&decoder->bit_stream_reader, 2); + + if (x < 0) { + return -1; + } else if (x < 3) { + return x + 3; + } + + // Value in range 6..10? + + x = read_bits(&decoder->bit_stream_reader, 3); + + if (x < 0) { + return -1; + } else if (x < 5) { + return x + 6; + } + + // Value in range 11..14? + + else if (x == 5) { + x = read_bits(&decoder->bit_stream_reader, 2); + + if (x < 0) { + return -1; + } else { + return x + 11; + } + } + + // Value in range 15..22? + + else if (x == 6) { + x = read_bits(&decoder->bit_stream_reader, 3); + + if (x < 0) { + return -1; + } else { + return x + 15; + } + } + + // else x == 7... + + x = read_bits(&decoder->bit_stream_reader, 6); + + if (x < 0) { + return -1; + } else if (x < 62) { + return x + 23; + } + + // Value in range 85..116? + + else if (x == 62) { + x = read_bits(&decoder->bit_stream_reader, 5); + + if (x < 0) { + return -1; + } else { + return x + 85; + } + } + + // Value in range 117..244? + + else { // a = 63 + x = read_bits(&decoder->bit_stream_reader, 7); + + if (x < 0) { + return -1; + } else { + return x + 117; + } + } +} + +// Read a single bit from the input stream, but only once the specified +// point is reached in the output stream. Before that point is reached, +// return the value of 'def' instead. Returns -1 for error. + +static int read_bit_after_threshold(LHAPM1Decoder *decoder, + unsigned int threshold, + int def) +{ + if (decoder->output_stream_pos >= threshold) { + return read_bit(&decoder->bit_stream_reader); + } else { + return def; + } +} + +// Read the range index for the copy type used when performing a copy command. + +static int read_copy_type_range(LHAPM1Decoder *decoder) +{ + int x; + + // This is another static huffman tree, but the path grows as + // more data is decoded. The progression is as follows: + // 1. Initially, only '0' and '2' can be returned. + // 2. After 64 bytes, '1' and '3' can be returned as well. + // 3. After 576 bytes, '4' can be returned. + // 4. After 2624 bytes, '5' can be returned. + + x = read_bit(&decoder->bit_stream_reader); + + if (x < 0) { + return -1; + } else if (x == 0) { + x = read_bit_after_threshold(decoder, 576, 0); + + if (x < 0) { + return -1; + } else if (x != 0) { + return 4; + } else { + // Return either 0 or 1. + return read_bit_after_threshold(decoder, 64, 0); + } + } else { + x = read_bit_after_threshold(decoder, 64, 1); + + if (x < 0) { + return -1; + } else if (x == 0) { + return 3; + } + + x = read_bit_after_threshold(decoder, 2624, 1); + + if (x < 0) { + return -1; + } else if (x != 0) { + return 2; + } else { + return 5; + } + } + +} + +// Read a copy command from the input stream and copy from history. +// Returns 0 for failure. + +static size_t read_copy_command(LHAPM1Decoder *decoder, uint8_t *buf) +{ + int range_index; + int history_distance; + int copy_index, i; + int count; + + range_index = read_copy_type_range(decoder); + + if (range_index < 0) { + return 0; + } + + // The first two entries in the copy_ranges table are used as + // a shorthand to copy two bytes. Otherwise, decode the number + // of bytes to copy. + + if (range_index < 2) { + count = 2; + } else { + count = read_copy_byte_count(decoder); + + if (count < 0) { + return 0; + } + } + + // The 'range_index' variable is an index into the copy_ranges + // array. As a special-case hack, early in the output stream + // some history ranges are inaccessible, so fewer bits can be + // used. Redirect range_index to special entries to do this. + + if (range_index == 3) { + if (decoder->output_stream_pos < 320) { + range_index = 6; + } + } else if (range_index == 4) { + if (decoder->output_stream_pos < 832) { + range_index = 7; + } else if (decoder->output_stream_pos < 1088) { + range_index = 8; + } else if (decoder->output_stream_pos < 1600) { + range_index = 9; + } + } else if (range_index == 5) { + if (decoder->output_stream_pos < 2880) { + range_index = 10; + } else if (decoder->output_stream_pos < 3136) { + range_index = 11; + } else if (decoder->output_stream_pos < 3648) { + range_index = 12; + } else if (decoder->output_stream_pos < 4672) { + range_index = 13; + } else if (decoder->output_stream_pos < 6720) { + range_index = 14; + } + } + + // Calculate the number of bytes back into the history buffer + // to read. + + history_distance = decode_variable_length(&decoder->bit_stream_reader, + copy_ranges, range_index); + + if (history_distance < 0 + || (unsigned) history_distance >= decoder->output_stream_pos) { + return 0; + } + + // Copy from the ring buffer. + + copy_index = (decoder->ringbuf_pos + RING_BUFFER_SIZE + - history_distance - 1) % RING_BUFFER_SIZE; + + for (i = 0; i < count; ++i) { + buf[i] = decoder->ringbuf[copy_index]; + outputted_byte(decoder, decoder->ringbuf[copy_index]); + copy_index = (copy_index + 1) % RING_BUFFER_SIZE; + } + + return count; +} + +// Read the index into the byte decode table, using the byte_decode_tree +// set at the start of the stream. Returns -1 for failure. + +static int read_byte_decode_index(LHAPM1Decoder *decoder) +{ + const uint8_t *ptr; + unsigned int child; + int bit; + + ptr = decoder->byte_decode_tree; + + if (ptr[0] == 0) { + return 0; + } + + // Walk down the tree, reading a bit at each node to determine + // which path to take. + + for (;;) { + bit = read_bit(&decoder->bit_stream_reader); + + if (bit < 0) { + return -1; + } else if (bit == 0) { + child = (*ptr >> 4) & 0x0f; + } else { + child = *ptr & 0x0f; + } + + // Reached a leaf node? + + if (child >= 10) { + return child - 10; + } + + ptr += child; + } +} + +// Read a single byte value from the input stream. +// Returns -1 for failure. + +static int read_byte(LHAPM1Decoder *decoder) +{ + int index; + int count; + + // Read the index into the byte_ranges table to use. + + index = read_byte_decode_index(decoder); + + if (index < 0) { + return -1; + } + + // Decode value using byte_ranges table. This is actually + // a distance to walk along the history linked list - it + // is static huffman encoding, so that recently used byte + // values use fewer bits. + + count = decode_variable_length(&decoder->bit_stream_reader, + byte_ranges, index); + + if (count < 0) { + return -1; + } + + // Walk through the history linked list to get the actual + // value. + + return find_in_history_list(&decoder->history_list, count); +} + +// Read the length of a block of bytes. + +static int read_byte_block_count(BitStreamReader *reader) +{ + int x; + + // This is a form of static huffman coding, where smaller + // lengths are encoded using shorter bit sequences. + + // Value in the range 1..3? + + x = read_bits(reader, 2); + + if (x < 0) { + return 0; + } else if (x < 3) { + return x + 1; + } + + // Value in the range 4..10? + + x = read_bits(reader, 3); + + if (x < 0) { + return 0; + } else if (x < 7) { + return x + 4; + } + + // Value in the range 11..25? + + x = read_bits(reader, 4); + + if (x < 0) { + return 0; + } else if (x < 14) { + return x + 11; + } else if (x == 14) { + // Value in the range 25-88: + + x = read_bits(reader, 6); + + if (x < 0) { + return 0; + } else { + return x + 25; + } + } else { // x = 15 + // Value in the range 89-216: + + x = read_bits(reader, 7); + + if (x < 0) { + return 0; + } else { + return x + 89; + } + } +} + +// Read a block of bytes from the input stream. +// Returns 0 for failure. + +static size_t read_byte_block(LHAPM1Decoder *decoder, uint8_t *buf) +{ + size_t result, result2; + int byteval; + int block_len; + int i; + + // How many bytes to decode? + + block_len = read_byte_block_count(&decoder->bit_stream_reader); + + if (block_len == 0) { + return 0; + } + + // Decode the byte values and add them to the output buffer. + + for (i = 0; i < block_len; ++i) { + byteval = read_byte(decoder); + + if (byteval < 0) { + return 0; + } + + buf[i] = byteval; + outputted_byte(decoder, byteval); + } + + result = (size_t) block_len; + + // Because this is a block of bytes, it can be assumed that the + // block ended for a copy command. The one exception is that if + // the maximum block length was reached, the block may have + // ended just because it could not be any larger. + + if (result == MAX_BYTE_BLOCK_LEN) { + return result; + } + + result2 = read_copy_command(decoder, buf + result); + + if (result2 == 0) { + return 0; + } + + return result + result2; +} + +static size_t lha_pm1_read(void *data, uint8_t *buf) +{ + LHAPM1Decoder *decoder = data; + int command_type; + + // Start of input stream? Read the header. + + if (decoder->byte_decode_tree == NULL + && !read_start_header(decoder)) { + return 0; + } + + // Read what type of commmand this is. + + command_type = read_bit(&decoder->bit_stream_reader); + + if (command_type == 0) { + return read_copy_command(decoder, buf); + } else { + return read_byte_block(decoder, buf); + } +} + +LHADecoderType lha_pm1_decoder = { + lha_pm1_init, + NULL, + lha_pm1_read, + sizeof(LHAPM1Decoder), + OUTPUT_BUFFER_SIZE, + 2048 +}; + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/pm2_decoder.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/pm2_decoder.c new file mode 100644 index 00000000..a423e610 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/pm2_decoder.c @@ -0,0 +1,549 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// +// Decoder for PMarc -pm2- compression format. PMarc is a variant +// of LHA commonly used on the MSX computer architecture. +// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> + +#include "lha_decoder.h" + +#include "bit_stream_reader.c" +#include "pma_common.c" + +// Include tree decoder. + +typedef uint8_t TreeElement; +#include "tree_decode.c" + +// Size of the ring buffer (in bytes) used to store past history +// for copies. + +#define RING_BUFFER_SIZE 8192 + +// Maximum number of bytes that might be placed in the output buffer +// from a single call to lha_pm2_decoder_read (largest copy size). + +#define OUTPUT_BUFFER_SIZE 256 + +// Number of tree elements in the code tree. + +#define CODE_TREE_ELEMENTS 65 + +// Number of tree elements in the offset tree. + +#define OFFSET_TREE_ELEMENTS 17 + +typedef enum { + PM2_REBUILD_UNBUILT, // At start of stream + PM2_REBUILD_BUILD1, // After 1KiB + PM2_REBUILD_BUILD2, // After 2KiB + PM2_REBUILD_BUILD3, // After 4KiB + PM2_REBUILD_CONTINUING, // 8KiB onwards... +} PM2RebuildState; + +typedef struct { + BitStreamReader bit_stream_reader; + + // State of decode tree. + + PM2RebuildState tree_state; + + // Number of bytes until we initiate a tree rebuild. + + size_t tree_rebuild_remaining; + + // History ring buffer, for copies: + + uint8_t ringbuf[RING_BUFFER_SIZE]; + unsigned int ringbuf_pos; + + // History linked list, for adaptively encoding byte values. + + HistoryLinkedList history_list; + + // Array representing the huffman tree used for representing + // code values. A given node of the tree has children + // code_tree[n] and code_tree[n + 1]. code_tree[0] is the + // root node. + + TreeElement code_tree[CODE_TREE_ELEMENTS]; + + // If zero, we don't need an offset tree: + + int need_offset_tree; + + // Array representing huffman tree used to look up offsets. + // Same format as code_tree[]. + + TreeElement offset_tree[OFFSET_TREE_ELEMENTS]; + +} LHAPM2Decoder; + +// Decode table for history value. Characters that appeared recently in +// the history are more likely than ones that appeared a long time ago, +// so the history value is huffman coded so that small values require +// fewer bits. The history value is then used to search within the +// history linked list to get the actual character. + +static const VariableLengthTable history_decode[] = { + { 0, 3 }, // 0 + (1 << 3) = 8 + { 8, 3 }, // 8 + (1 << 3) = 16 + { 16, 4 }, // 16 + (1 << 4) = 32 + { 32, 5 }, // 32 + (1 << 5) = 64 + { 64, 5 }, // 64 + (1 << 5) = 96 + { 96, 5 }, // 96 + (1 << 5) = 128 + { 128, 6 }, // 128 + (1 << 6) = 192 + { 192, 6 }, // 192 + (1 << 6) = 256 +}; + +// Decode table for copies. As with history_decode[], small copies +// are more common, and require fewer bits. + +static const VariableLengthTable copy_decode[] = { + { 17, 3 }, // 17 + (1 << 3) = 25 + { 25, 3 }, // 25 + (1 << 3) = 33 + { 33, 5 }, // 33 + (1 << 5) = 65 + { 65, 6 }, // 65 + (1 << 6) = 129 + { 129, 7 }, // 129 + (1 << 7) = 256 + { 256, 0 }, // 256 (unique value) +}; + +// Initialize PMA decoder. + +static int lha_pm2_decoder_init(void *data, LHADecoderCallback callback, + void *callback_data) +{ + LHAPM2Decoder *decoder = data; + + bit_stream_reader_init(&decoder->bit_stream_reader, + callback, callback_data); + + // Tree has not been built yet. It needs to be built on + // the first call to read(). + + decoder->tree_state = PM2_REBUILD_UNBUILT; + decoder->tree_rebuild_remaining = 0; + + // Initialize ring buffer contents. + + memset(&decoder->ringbuf, ' ', RING_BUFFER_SIZE); + decoder->ringbuf_pos = 0; + + // Init history lookup list. + + init_history_list(&decoder->history_list); + + // Initialize the lookup trees to a known state. + + init_tree(decoder->code_tree, CODE_TREE_ELEMENTS); + init_tree(decoder->offset_tree, OFFSET_TREE_ELEMENTS); + + return 1; +} + +// Read the list of code lengths to use for the code tree and construct +// the code_tree structure. + +static int read_code_tree(LHAPM2Decoder *decoder) +{ + uint8_t code_lengths[31]; + int num_codes, min_code_length, length_bits, val; + unsigned int i; + + // Read the number of codes in the tree. + + num_codes = read_bits(&decoder->bit_stream_reader, 5); + + // Read min_code_length, which is used as an offset. + + min_code_length = read_bits(&decoder->bit_stream_reader, 3); + + if (min_code_length < 0 || num_codes < 0) { + return 0; + } + + // Store flag variable indicating whether we want to read + // the offset tree as well. + + decoder->need_offset_tree + = num_codes >= 10 + && !(num_codes == 29 && min_code_length == 0); + + // Minimum length of zero means a tree containing a single code. + + if (min_code_length == 0) { + set_tree_single(decoder->code_tree, num_codes - 1); + return 1; + } + + // How many bits are used to represent each table entry? + + length_bits = read_bits(&decoder->bit_stream_reader, 3); + + if (length_bits < 0) { + return 0; + } + + // Read table of code lengths: + + for (i = 0; i < (unsigned int) num_codes; ++i) { + + // Read a table entry. A value of zero represents an + // unused code. Otherwise the value represents + // an offset from the minimum length (previously read). + + val = read_bits(&decoder->bit_stream_reader, + (unsigned int) length_bits); + + if (val < 0) { + return 0; + } else if (val == 0) { + code_lengths[i] = 0; + } else { + code_lengths[i] = (uint8_t) (min_code_length + val - 1); + } + } + + // Build the tree. + + build_tree(decoder->code_tree, sizeof(decoder->code_tree), + code_lengths, (unsigned int) num_codes); + + return 1; +} + +// Read the code lengths for the offset tree and construct the offset +// tree lookup table. + +static int read_offset_tree(LHAPM2Decoder *decoder, + unsigned int num_offsets) +{ + uint8_t offset_lengths[8]; + unsigned int off; + unsigned int single_offset, num_codes; + int len; + + if (!decoder->need_offset_tree) { + return 1; + } + + // Read 'num_offsets' 3-bit length values. For each offset + // value 'off', offset_lengths[off] is the length of the + // code that will represent 'off', or 0 if it will not + // appear within the tree. + + num_codes = 0; + single_offset = 0; + + for (off = 0; off < num_offsets; ++off) { + len = read_bits(&decoder->bit_stream_reader, 3); + + if (len < 0) { + return 0; + } + + offset_lengths[off] = (uint8_t) len; + + // Track how many actual codes were in the tree. + + if (len != 0) { + single_offset = off; + ++num_codes; + } + } + + // If there was a single code, this is a single node tree. + + if (num_codes == 1) { + set_tree_single(decoder->offset_tree, single_offset); + return 1; + } + + // Build the tree. + + build_tree(decoder->offset_tree, sizeof(decoder->offset_tree), + offset_lengths, num_offsets); + + return 1; +} + +// Rebuild the decode trees used to compress data. This is called when +// decoder->tree_rebuild_remaining reaches zero. + +static void rebuild_tree(LHAPM2Decoder *decoder) +{ + switch (decoder->tree_state) { + + // Initial tree build, from start of stream: + + case PM2_REBUILD_UNBUILT: + read_code_tree(decoder); + read_offset_tree(decoder, 5); + decoder->tree_state = PM2_REBUILD_BUILD1; + decoder->tree_rebuild_remaining = 1024; + break; + + // Tree rebuild after 1KiB of data has been read: + + case PM2_REBUILD_BUILD1: + read_offset_tree(decoder, 6); + decoder->tree_state = PM2_REBUILD_BUILD2; + decoder->tree_rebuild_remaining = 1024; + break; + + // Tree rebuild after 2KiB of data has been read: + + case PM2_REBUILD_BUILD2: + read_offset_tree(decoder, 7); + decoder->tree_state = PM2_REBUILD_BUILD3; + decoder->tree_rebuild_remaining = 2048; + break; + + // Tree rebuild after 4KiB of data has been read: + + case PM2_REBUILD_BUILD3: + if (read_bit(&decoder->bit_stream_reader) == 1) { + read_code_tree(decoder); + } + read_offset_tree(decoder, 8); + decoder->tree_state = PM2_REBUILD_CONTINUING; + decoder->tree_rebuild_remaining = 4096; + break; + + // Tree rebuild after 8KiB of data has been read, + // and every 4KiB after that: + + case PM2_REBUILD_CONTINUING: + if (read_bit(&decoder->bit_stream_reader) == 1) { + read_code_tree(decoder); + read_offset_tree(decoder, 8); + } + decoder->tree_rebuild_remaining = 4096; + break; + } +} + +static void output_byte(LHAPM2Decoder *decoder, uint8_t *buf, + size_t *buf_len, uint8_t b) +{ + // Add to history ring buffer. + + decoder->ringbuf[decoder->ringbuf_pos] = b; + decoder->ringbuf_pos = (decoder->ringbuf_pos + 1) % RING_BUFFER_SIZE; + + // Add to output buffer. + + buf[*buf_len] = b; + ++*buf_len; + + // Update history chain. + + update_history_list(&decoder->history_list, b); + + // Count down until it is time to perform a rebuild of the + // lookup trees. + + --decoder->tree_rebuild_remaining; + + if (decoder->tree_rebuild_remaining == 0) { + rebuild_tree(decoder); + } +} + +// Read a single byte from the input stream and add it to the output +// buffer. + +static void read_single_byte(LHAPM2Decoder *decoder, unsigned int code, + uint8_t *buf, size_t *buf_len) +{ + int offset; + uint8_t b; + + offset = decode_variable_length(&decoder->bit_stream_reader, + history_decode, code); + + if (offset < 0) { + return; + } + + b = find_in_history_list(&decoder->history_list, (uint8_t) offset); + output_byte(decoder, buf, buf_len, b); +} + +// Calculate how many bytes from history to copy: + +static int history_get_count(LHAPM2Decoder *decoder, unsigned int code) +{ + // How many bytes to copy? A small value represents the + // literal number of bytes to copy; larger values are a header + // for a variable length value to be decoded. + + if (code < 15) { + return (int) code + 2; + } else { + return decode_variable_length(&decoder->bit_stream_reader, + copy_decode, code - 15); + } +} + +// Calculate the offset within history at which to start copying: + +static int history_get_offset(LHAPM2Decoder *decoder, unsigned int code) +{ + unsigned int bits; + int result, val; + + result = 0; + + // Calculate number of bits to read. + + // Code of zero indicates a simple 6-bit value giving the offset. + + if (code == 0) { + bits = 6; + } + + // Mid-range encoded offset value. + // Read a code using the offset tree, indicating the length + // of the offset value to follow. The code indicates the + // number of bits (values 0-7 = 6-13 bits). + + else if (code < 20) { + + val = read_from_tree(&decoder->bit_stream_reader, + decoder->offset_tree); + + if (val < 0) { + return -1; + } else if (val == 0) { + bits = 6; + } else { + bits = (unsigned int) val + 5; + result = 1 << bits; + } + } + + // Large copy values start from offset zero. + + else { + return 0; + } + + // Read a number of bits representing the offset value. The + // number of length of this value is variable, and is calculated + // above. + + val = read_bits(&decoder->bit_stream_reader, bits); + + if (val < 0) { + return -1; + } + + result += val; + + return result; +} + +static void copy_from_history(LHAPM2Decoder *decoder, unsigned int code, + uint8_t *buf, size_t *buf_len) +{ + int to_copy, offset; + unsigned int i, pos, start; + + // Read number of bytes to copy and offset within history to copy + // from. + + to_copy = history_get_count(decoder, code); + offset = history_get_offset(decoder, code); + + if (to_copy < 0 || offset < 0) { + return; + } + + // Sanity check to prevent the potential for buffer overflow. + + if (to_copy > OUTPUT_BUFFER_SIZE) { + return; + } + + // Perform copy. + + start = decoder->ringbuf_pos + RING_BUFFER_SIZE - 1 + - (unsigned int) offset; + + for (i = 0; i < (unsigned int) to_copy; ++i) { + pos = (start + i) % RING_BUFFER_SIZE; + + output_byte(decoder, buf, buf_len, decoder->ringbuf[pos]); + } +} + +// Decode data and store it into buf[], returning the number of +// bytes decoded. + +static size_t lha_pm2_decoder_read(void *data, uint8_t *buf) +{ + LHAPM2Decoder *decoder = data; + size_t result; + int code; + + // On first pass through, build initial lookup trees. + + if (decoder->tree_state == PM2_REBUILD_UNBUILT) { + + // First bit in stream is discarded? + + read_bit(&decoder->bit_stream_reader); + rebuild_tree(decoder); + } + + result = 0; + + code = read_from_tree(&decoder->bit_stream_reader, decoder->code_tree); + + if (code < 0) { + return 0; + } + + if (code < 8) { + read_single_byte(decoder, (unsigned int) code, buf, &result); + } else { + copy_from_history(decoder, (unsigned int) code - 8, + buf, &result); + } + + return result; +} + +LHADecoderType lha_pm2_decoder = { + lha_pm2_decoder_init, + NULL, + lha_pm2_decoder_read, + sizeof(LHAPM2Decoder), + OUTPUT_BUFFER_SIZE, + RING_BUFFER_SIZE +}; + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/pma_common.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/pma_common.c new file mode 100644 index 00000000..00af915f --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/pma_common.c @@ -0,0 +1,162 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// +// Common functions used by PMarc decoders. +// + +typedef struct { + unsigned int offset; + unsigned int bits; +} VariableLengthTable; + +// Read a variable length code, given the header bits already read. +// Returns the decoded value, or -1 for error. + +static int decode_variable_length(BitStreamReader *reader, + const VariableLengthTable *table, + unsigned int header) +{ + int value; + + value = read_bits(reader, table[header].bits); + + if (value < 0) { + return -1; + } + + return (int) table[header].offset + value; +} + +typedef struct { + uint8_t prev; + uint8_t next; +} HistoryNode; + +// History linked list. In the decode stream, codes representing +// characters are not the character itself, but the number of +// nodes to count back in time in the linked list. Every time +// a character is output, it is moved to the front of the linked +// list. The entry point index into the list is the last output +// character, given by history_head; + +typedef struct { + HistoryNode history[256]; + uint8_t history_head; +} HistoryLinkedList; + +// Initialize the history buffer. + +static void init_history_list(HistoryLinkedList *list) +{ + unsigned int i; + + // History buffer is initialized to a linear chain to + // start off with: + + for (i = 0; i < 256; ++i) { + list->history[i].prev = (uint8_t) (i + 1); + list->history[i].next = (uint8_t) (i - 1); + } + + // The chain is cut into groups and initially arranged so + // that the ASCII characters are closest to the start of + // the chain. This is followed by ASCII control characters, + // then various other groups. + + list->history_head = 0x20; + + list->history[0x7f].prev = 0x00; // 0x20 ... 0x7f -> 0x00 + list->history[0x00].next = 0x7f; + + list->history[0x1f].prev = 0xa0; // 0x00 ... 0x1f -> 0xa0 + list->history[0xa0].next = 0x1f; + + list->history[0xdf].prev = 0x80; // 0xa0 ... 0xdf -> 0x80 + list->history[0x80].next = 0xdf; + + list->history[0x9f].prev = 0xe0; // 0x80 ... 0x9f -> 0xe0 + list->history[0xe0].next = 0x9f; + + list->history[0xff].prev = 0x20; // 0xe0 ... 0xff -> 0x20 + list->history[0x20].next = 0xff; +} + +// Look up an entry in the history list, returning the code found. + +static uint8_t find_in_history_list(HistoryLinkedList *list, uint8_t count) +{ + unsigned int i; + uint8_t code; + + // Start from the last outputted byte. + + code = list->history_head; + + // Walk along the history chain until we reach the desired + // node. If we will have to walk more than half the chain, + // go the other way around. + + if (count < 128) { + for (i = 0; i < count; ++i) { + code = list->history[code].prev; + } + } else { + for (i = 0; i < 256U - count; ++i) { + code = list->history[code].next; + } + } + + return code; +} + +// Update history list, by moving the specified byte to the head +// of the queue. + +static void update_history_list(HistoryLinkedList *list, uint8_t b) +{ + HistoryNode *node, *old_head; + + // No update necessary? + + if (list->history_head == b) { + return; + } + + // Unhook the entry from its current position: + + node = &list->history[b]; + list->history[node->next].prev = node->prev; + list->history[node->prev].next = node->next; + + // Hook in between the old head and old_head->next: + + old_head = &list->history[list->history_head]; + node->prev = list->history_head; + node->next = old_head->next; + + list->history[old_head->next].prev = b; + old_head->next = b; + + // 'b' is now the head of the queue: + + list->history_head = b; +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/Makefile.am b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/Makefile.am new file mode 100644 index 00000000..9e810ac9 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/Makefile.am @@ -0,0 +1,9 @@ + +headerfilesdir=$(includedir)/liblhasa-1.0 +headerfiles_HEADERS= \ + lhasa.h \ + lha_decoder.h \ + lha_file_header.h \ + lha_input_stream.h \ + lha_reader.h + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lha_decoder.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lha_decoder.h new file mode 100644 index 00000000..149e0c3e --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lha_decoder.h @@ -0,0 +1,183 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_PUBLIC_LHA_DECODER_H +#define LHASA_PUBLIC_LHA_DECODER_H + +#include <stdlib.h> +#include <inttypes.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file lha_decoder.h + * + * @brief Raw LHA data decoder. + * + * This file defines the interface to the decompression code, which can + * be used to decompress the raw compressed data from an LZH file. + * + * Implementations of the various compression algorithms used in LZH + * archives are provided - these are represented by the + * @ref LHADecoderType structure, and can be retrieved using the + * @ref lha_decoder_for_name function. One of these can then be passed to + * the @ref lha_decoder_new function to create a @ref LHADecoder structure + * and decompress the data. + */ + +/** + * Opaque type representing a type of decoder. + * + * This is an implementation of the decompression code for one of the + * algorithms used in LZH archive files. Pointers to these structures are + * retrieved by using the @ref lha_decoder_for_name function. + */ + +typedef struct _LHADecoderType LHADecoderType; + +/** + * Opaque type representing an instance of a decoder. + * + * This is a decoder structure being used to decompress a stream of + * compressed data. Instantiated using the @ref lha_decoder_new + * function and freed using the @ref lha_decoder_free function. + */ + +typedef struct _LHADecoder LHADecoder; + +/** + * Callback function invoked when a decoder wants to read more compressed + * data. + * + * @param buf Pointer to the buffer in which to store the data. + * @param buf_len Size of the buffer, in bytes. + * @param user_data Extra pointer to pass to the decoder. + * @return Number of bytes read. + */ + +typedef size_t (*LHADecoderCallback)(void *buf, size_t buf_len, + void *user_data); + +/** + * Callback function used for monitoring decode progress. + * The callback is invoked for every block processed (block size depends on + * decode algorithm). + * + * @param num_blocks Number of blocks processed so far. + * @param total_blocks Total number of blocks to process. + * @paaram callback_data Extra user-specified data passed to the callback. + */ + +typedef void (*LHADecoderProgressCallback)(unsigned int num_blocks, + unsigned int total_blocks, + void *callback_data); + +/** + * Get the decoder type for the specified name. + * + * @param name String identifying the decoder type, for + * example, "-lh1-". + * @return Pointer to the decoder type, or NULL if there + * is no decoder type for the specified name. + */ + +LHADecoderType *lha_decoder_for_name(char *name); + +/** + * Allocate a new decoder for the specified type. + * + * @param dtype The decoder type. + * @param callback Callback function for the decoder to call to read + * more compressed data. + * @param callback_data Extra data to pass to the callback function. + * @param stream_length Length of the uncompressed data, in bytes. When + * this point is reached, decompression will stop. + * @return Pointer to the new decoder, or NULL for failure. + */ + +LHADecoder *lha_decoder_new(LHADecoderType *dtype, + LHADecoderCallback callback, + void *callback_data, + size_t stream_length); + +/** + * Free a decoder. + * + * @param decoder The decoder to free. + */ + +void lha_decoder_free(LHADecoder *decoder); + +/** + * Set a callback function to monitor decode progress. + * + * @param decoder The decoder. + * @param callback Callback function to monitor decode progress. + * @param callback_data Extra data to pass to the decoder. + */ + +void lha_decoder_monitor(LHADecoder *decoder, + LHADecoderProgressCallback callback, + void *callback_data); + +/** + * Decode (decompress) more data. + * + * @param decoder The decoder. + * @param buf Pointer to buffer to store decompressed data. + * @param buf_len Size of the buffer, in bytes. + * @return Number of bytes decompressed. + */ + +size_t lha_decoder_read(LHADecoder *decoder, uint8_t *buf, size_t buf_len); + +/** + * Get the current 16-bit CRC of the decompressed data. + * + * This should be called at the end of decompression to check that the + * data was extracted correctly, and the value compared against the CRC + * from the file header. + * + * @param decoder The decoder. + * @return 16-bit CRC of the data decoded so far. + */ + +uint16_t lha_decoder_get_crc(LHADecoder *decoder); + +/** + * Get the count of the number of bytes decoded. + * + * This should be called at the end of decompression, and the value + * compared against the file length from the file header. + * + * @param decoder The decoder. + * @return The number of decoded bytes. + */ + +size_t lha_decoder_get_length(LHADecoder *decoder); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef LHASA_LHA_DECODER_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lha_file_header.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lha_file_header.h new file mode 100644 index 00000000..410ff38d --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lha_file_header.h @@ -0,0 +1,248 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_PUBLIC_LHA_FILE_HEADER_H +#define LHASA_PUBLIC_LHA_FILE_HEADER_H + +#include <inttypes.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file lha_file_header.h + * + * @brief LHA file header structure. + * + * This file contains the definition of the @ref LHAFileHeader structure, + * representing a decoded file header from an LZH file. + */ + +/** OS type value for an unknown OS. */ +#define LHA_OS_TYPE_UNKNOWN 0x00 +/** OS type value for Microsoft MS/DOS. */ +#define LHA_OS_TYPE_MSDOS 'M' +/** OS type value for Microsoft Windows 95. */ +#define LHA_OS_TYPE_WIN95 'w' +/** OS type value for Microsoft Windows NT. */ +#define LHA_OS_TYPE_WINNT 'W' +/** OS type value for Unix. */ +#define LHA_OS_TYPE_UNIX 'U' +/** OS type value for IBM OS/2. */ +#define LHA_OS_TYPE_OS2 '2' +/** OS type for Apple Mac OS (Classic). */ +#define LHA_OS_TYPE_MACOS 'm' +/** OS type for Amiga OS. */ +#define LHA_OS_TYPE_AMIGA 'A' +/** OS type for Atari TOS. */ +#define LHA_OS_TYPE_ATARI 'a' + +// Obscure: + +/** OS type for Sun (Oracle) Java. */ +#define LHA_OS_TYPE_JAVA 'J' +/** OS type for Digital Research CP/M. */ +#define LHA_OS_TYPE_CPM 'C' +/** OS type for Digital Research FlexOS. */ +#define LHA_OS_TYPE_FLEX 'F' +/** OS type for Runser (?). */ +#define LHA_OS_TYPE_RUNSER 'R' +/** OS type for Fujitsu FM Towns OS. */ +#define LHA_OS_TYPE_TOWNSOS 'T' +/** OS type for Microware OS-9. */ +#define LHA_OS_TYPE_OS9 '9' +/** OS type for Microware OS-9/68k. */ +#define LHA_OS_TYPE_OS9_68K 'K' +/** OS type for OS/386 (?). */ +#define LHA_OS_TYPE_OS386 '3' +/** OS type for Sharp X68000 Human68K OS. */ +#define LHA_OS_TYPE_HUMAN68K 'H' + +/** + * Compression type for a stored directory. The same value is also + * used for Unix symbolic links. + */ +#define LHA_COMPRESS_TYPE_DIR "-lhd-" + +/** + * Bit field value set in extra_flags to indicate that the + * Unix file permission header (0x50) was parsed. + */ +#define LHA_FILE_UNIX_PERMS 0x01 + +/** + * Bit field value set in extra_flags to indicate that the + * Unix UID/GID header (0x51) was parsed. + */ +#define LHA_FILE_UNIX_UID_GID 0x02 + +/** + * Bit field value set in extra_flags to indicate that the 'common + * header' extended header (0x00) was parsed, and the common_crc + * field has been set. + */ +#define LHA_FILE_COMMON_CRC 0x04 + +/** + * Bit field value set in extra_flags to indicate that the + * Windows time stamp header (0x41) was parsed, and the Windows + * FILETIME timestamp fields have been set. + */ +#define LHA_FILE_WINDOWS_TIMESTAMPS 0x08 + +/** + * Bit field value set in extra_flags to indicate that the OS-9 + * permissions field is set. + */ +#define LHA_FILE_OS9_PERMS 0x10 + +typedef struct _LHAFileHeader LHAFileHeader; + +#define LHA_FILE_HAVE_EXTRA(header, flag) \ + (((header)->extra_flags & (flag)) != 0) +/** + * Structure containing a decoded LZH file header. + * + * A file header precedes the compressed data of each file stored + * within an LZH archive. It contains the name of the file, and + * various additional metadata, some of which is optional, and + * can depend on the header format, the tool used to create the + * archive, and the operating system on which it was created. + */ + +struct _LHAFileHeader { + + // Internal fields, do not touch! + + unsigned int _refcount; + LHAFileHeader *_next; + + /** + * Stored path, with Unix-style ('/') path separators. + * + * This may be NULL, although if this is a directory + * (@ref LHA_COMPRESS_TYPE_DIR), it is never NULL. + */ + char *path; + + /** + * File name. + * + * This is never NULL, except if this is a directory + * (@ref LHA_COMPRESS_TYPE_DIR), where it is always NULL. + */ + char *filename; + + /** + * Target for symbolic link. + * + * This is NULL unless this header represents a symbolic link + * (@ref LHA_COMPRESS_TYPE_DIR). + */ + char *symlink_target; + + /** + * Compression method. + * + * If the header represents a directory or a symbolic link, the + * compression method is equal to @ref LHA_COMPRESS_TYPE_DIR. + */ + char compress_method[6]; + + /** Length of the compressed data. */ + size_t compressed_length; + + /** Length of the uncompressed data. */ + size_t length; + + /** LZH header format used to store this header. */ + uint8_t header_level; + + /** + * OS type indicator, identifying the OS on which + * the archive was created. + */ + uint8_t os_type; + + /** 16-bit CRC of the compressed data. */ + uint16_t crc; + + /** Unix timestamp of the modification time of the file. */ + unsigned int timestamp; + + /** Pointer to a buffer containing the raw header data. */ + uint8_t *raw_data; + + /** Length of the raw header data. */ + size_t raw_data_len; + + /** + * Flags bitfield identifying extra data decoded from extended + * headers. + */ + unsigned int extra_flags; + + /** Unix permissions, set if @ref LHA_FILE_UNIX_PERMS is set. */ + unsigned int unix_perms; + + /** Unix user ID, set if @ref LHA_FILE_UNIX_UID_GID is set. */ + unsigned int unix_uid; + + /** Unix group ID, set if @ref LHA_FILE_UNIX_UID_GID is set. */ + unsigned int unix_gid; + + /** OS-9 permissions, set if @ref LHA_FILE_OS9_PERMS is set. */ + unsigned int os9_perms; + + /** Unix username. */ + char *unix_username; + + /** Unix group name. */ + char *unix_group; + + /** 16-bit CRC of header contents. */ + uint16_t common_crc; + + /** + * Windows FILETIME file creation time, set if + * @ref LHA_FILE_WINDOWS_TIMESTAMPS is set. + */ + uint64_t win_creation_time; + + /** + * Windows FILETIME file modification time, set if + * @ref LHA_FILE_WINDOWS_TIMESTAMPS is set. + */ + uint64_t win_modification_time; + + /** + * Windows FILETIME file access time, set if + * @ref LHA_FILE_WINDOWS_TIMESTAMPS is set. + */ + uint64_t win_access_time; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef LHASA_PUBLIC_LHA_FILE_HEADER_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lha_input_stream.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lha_input_stream.h new file mode 100644 index 00000000..7dfe0371 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lha_input_stream.h @@ -0,0 +1,134 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + + +#ifndef LHASA_PUBLIC_LHA_INPUT_STREAM_H +#define LHASA_PUBLIC_LHA_INPUT_STREAM_H + +#include <stdio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file lha_input_stream.h + * + * @brief LHA input stream structure. + * + * This file defines the functions relating to the @ref LHAInputStream + * structure, used to read data from an LZH file. + */ + +/** + * Opaque structure, representing an input stream used to read data from + * an LZH file. + */ + +typedef struct _LHAInputStream LHAInputStream; + +/** + * Structure containing pointers to callback functions to read data from + * the input stream. + */ + +typedef struct { + + /** + * Read a block of data into the specified buffer. + * + * @param handle Handle pointer. + * @param buf Pointer to buffer in which to store read data. + * @param buf_len Size of buffer, in bytes. + * @return Number of bytes read, or -1 for error. + */ + + int (*read)(void *handle, void *buf, size_t buf_len); + + + /** + * Skip the specified number of bytes from the input stream. + * This is an optional function. + * + * @param handle Handle pointer. + * @param bytes Number of bytes to skip. + * @return Non-zero for success, or zero for failure. + */ + + int (*skip)(void *handle, size_t bytes); + + /** + * Close the input stream. + * + * @param handle Handle pointer. + */ + + void (*close)(void *handle); + +} LHAInputStreamType; + +/** + * Create new @ref LHAInputStream structure, using a set of generic functions + * to provide LHA data. + * + * @param type Pointer to a @ref LHAInputStreamType structure + * containing callback functions to read data. + * @param handle Handle pointer to be passed to callback functions. + * @return Pointer to a new @ref LHAInputStream or NULL for error. + */ + +LHAInputStream *lha_input_stream_new(const LHAInputStreamType *type, + void *handle); + +/** + * Create new @ref LHAInputStream, reading from the specified filename. + * The file is automatically closed when the input stream is freed. + * + * @param filename Name of the file to read from. + * @return Pointer to a new @ref LHAInputStream or NULL for error. + */ + +LHAInputStream *lha_input_stream_from(char *filename); + +/** + * Create new @ref LHAInputStream, to read from an already-open FILE pointer. + * The FILE is not closed when the input stream is freed; the calling code + * must close it. + * + * @param stream The open FILE structure from which to read data. + * @return Pointer to a new @ref LHAInputStream or NULL for error. + */ + +LHAInputStream *lha_input_stream_from_FILE(FILE *stream); + +/** + * Free an @ref LHAInputStream structure. + * + * @param stream The input stream. + */ + +void lha_input_stream_free(LHAInputStream *stream); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef LHASA_PUBLIC_LHA_INPUT_STREAM_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lha_reader.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lha_reader.h new file mode 100644 index 00000000..bd722bca --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lha_reader.h @@ -0,0 +1,217 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_PUBLIC_LHA_READER_H +#define LHASA_PUBLIC_LHA_READER_H + +#include "lha_decoder.h" +#include "lha_input_stream.h" +#include "lha_file_header.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file lha_reader.h + * + * @brief LHA file reader. + * + * This file contains the interface functions for the @ref LHAReader + * structure, used to decode data from a compressed LZH file and + * extract compressed files. + */ + +/** + * Opaque structure used to decode the contents of an LZH file. + */ + +typedef struct _LHAReader LHAReader; + +/** + * Policy for extracting directories. + * + * When extracting a directory, some of the metadata associated with + * it needs to be set after the contents of the directory have been + * extracted. This includes the modification time (which would + * otherwise be reset to the current time) and the permissions (which + * can affect the ability to extract files into the directory). + * To work around this problem there are several ways of handling + * directory extraction. + */ + +typedef enum { + + /** + * "Plain" policy. In this mode, the metadata is set at the + * same time that the directory is created. This is the + * simplest to comprehend, and the files returned from + * @ref lha_reader_next_file will match the files in the + * archive, but it is not recommended. + */ + + LHA_READER_DIR_PLAIN, + + /** + * "End of directory" policy. In this mode, if a directory + * is extracted, the directory name will be saved. Once the + * contents of the directory appear to have been extracted + * (ie. a file is found that is not within the directory), + * the directory will be returned again by + * @ref lha_reader_next_file. This time, when the directory + * is "extracted" (via @ref lha_reader_extract), the metadata + * will be set. + * + * This method uses less memory than + * @ref LHA_READER_DIR_END_OF_FILE, but there is the risk + * that a file will appear within the archive after the + * metadata has been set for the directory. However, this is + * not normally the case, as files and directories typically + * appear within an archive in order. GNU tar uses the same + * method to address this problem with tar files. + * + * This is the default policy. + */ + + LHA_READER_DIR_END_OF_DIR, + + /** + * "End of file" policy. In this mode, each directory that + * is extracted is recorded in a list. When the end of the + * archive is reached, these directories are returned again by + * @ref lha_reader_next_file. When the directories are + * "extracted" again (via @ref lha_reader_extract), the + * metadata is set. + * + * This avoids the problems that can potentially occur with + * @ref LHA_READER_DIR_END_OF_DIR, but uses more memory. + */ + + LHA_READER_DIR_END_OF_FILE + +} LHAReaderDirPolicy; + +/** + * Create a new @ref LHAReader to read data from an @ref LHAInputStream. + * + * @param stream The input stream to read data from. + * @return Pointer to a new @ref LHAReader structure, + * or NULL for error. + */ + +LHAReader *lha_reader_new(LHAInputStream *stream); + +/** + * Free a @ref LHAReader structure. + * + * @param reader The @ref LHAReader structure. + */ + +void lha_reader_free(LHAReader *reader); + +/** + * Set the @ref LHAReaderDirPolicy used to extract directories. + * + * @param reader The @ref LHAReader structure. + * @param policy The policy to use for directories. + */ + +void lha_reader_set_dir_policy(LHAReader *reader, + LHAReaderDirPolicy policy); + +/** + * Read the header of the next archived file from the input stream. + * + * @param reader The @ref LHAReader structure. + * @return Pointer to an @ref LHAFileHeader structure, or NULL if + * an error occurred. This pointer is only valid until + * the next time that lha_reader_next_file is called. + */ + +LHAFileHeader *lha_reader_next_file(LHAReader *reader); + +/** + * Read some of the (decompresed) data for the current archived file, + * decompressing as appropriate. + * + * @param reader The @ref LHAReader structure. + * @param buf Pointer to a buffer in which to store the data. + * @param buf_len Size of the buffer, in bytes. + * @return Number of bytes stored in the buffer, or zero if + * there is no more data to decompress. + */ + +size_t lha_reader_read(LHAReader *reader, void *buf, size_t buf_len); + +/** + * Decompress the contents of the current archived file, and check + * that the checksum matches correctly. + * + * @param reader The @ref LHAReader structure. + * @param callback Callback function to invoke to monitor progress (or + * NULL if progress does not need to be monitored). + * @param callback_data Extra data to pass to the callback function. + * @return Non-zero if the checksum matches. + */ + +int lha_reader_check(LHAReader *reader, + LHADecoderProgressCallback callback, + void *callback_data); + +/** + * Extract the contents of the current archived file. + * + * @param reader The @ref LHAReader structure. + * @param filename Filename to extract the archived file to, or NULL + * to use the path and filename from the header. + * @param callback Callback function to invoke to monitor progress (or + * NULL if progress does not need to be monitored). + * @param callback_data Extra data to pass to the callback function. + * @return Non-zero for success, or zero for failure (including + * CRC error). + */ + +int lha_reader_extract(LHAReader *reader, + char *filename, + LHADecoderProgressCallback callback, + void *callback_data); + +/** + * Check if the current file (last returned by @ref lha_reader_next_file) + * was generated internally by the extract process. This occurs when a + * directory or symbolic link must be created as a two-stage process, with + * some of the extraction process deferred to later in the stream. + * + * These "fake" duplicates should usually be hidden in the user interface + * when a summary of extraction is presented. + * + * @param reader The @ref LHAReader structure. + * @return Non-zero if the current file is a "fake", or zero + * for a normal file. + */ + +int lha_reader_current_is_fake(LHAReader *reader); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef LHASA_PUBLIC_LHA_READER_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lhasa.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lhasa.h new file mode 100644 index 00000000..7a1b63cd --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/public/lhasa.h @@ -0,0 +1,30 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_PUBLIC_LHASA_H +#define LHASA_PUBLIC_LHASA_H + +#include "lha_decoder.h" +#include "lha_file_header.h" +#include "lha_input_stream.h" +#include "lha_reader.h" + +#endif /* #ifndef LHASA_PUBLIC_LHASA_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/tree_decode.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/tree_decode.c new file mode 100644 index 00000000..a52a91ab --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/lib/tree_decode.c @@ -0,0 +1,252 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +// Common tree decoding code. +// +// A recurring feature used by the different LHA algorithms is to +// encode a set of codes, which have varying bit lengths. This is +// implemented using a binary tree, stored inside an array of +// elements. +// +// This file is implemented as a "template" file to be #include-d by +// other files. The typedef for TreeElement must be defined before +// include. + + +// Upper bit is set in a node value to indicate a leaf. + +#define TREE_NODE_LEAF (TreeElement) (1 << (sizeof(TreeElement) * 8 - 1)) + +// Structure used to hold data needed to build the tree. + +typedef struct { + // The tree data and its size (must not be exceeded) + + TreeElement *tree; + unsigned int tree_len; + + // Counter used to allocate entries from the tree. + // Every time a new node is allocated, this increase by 2. + + unsigned int tree_allocated; + + // The next tree entry. + // As entries are allocated sequentially, the range from + // next_entry..tree_allocated-1 constitutes the indices into + // the tree that are available to be filled in. By the + // end of the tree build, next_entry should = tree_allocated. + + unsigned int next_entry; +} TreeBuildData; + +// Initialize all elements of the given tree to a good initial state. + +static void init_tree(TreeElement *tree, size_t tree_len) +{ + unsigned int i; + + for (i = 0; i < tree_len; ++i) { + tree[i] = TREE_NODE_LEAF; + } +} + +// Set tree to always decode to a single code. + +static void set_tree_single(TreeElement *tree, TreeElement code) +{ + tree[0] = (TreeElement) code | TREE_NODE_LEAF; +} + +// "Expand" the list of queue entries. This generates a new child +// node at each of the entries currently in the queue, adding the +// children of those nodes into the queue to replace them. +// The effect of this is to add an extra level to the tree, and +// to increase the tree depth of the indices in the queue. + +static void expand_queue(TreeBuildData *build) +{ + unsigned int end_offset; + unsigned int new_nodes; + + // Sanity check that there is enough space in the tree for + // all the new nodes. + + new_nodes = (build->tree_allocated - build->next_entry) * 2; + + if (build->tree_allocated + new_nodes > build->tree_len) { + return; + } + + // Go through all entries currently in the allocated range, and + // allocate a subnode for each. + + end_offset = build->tree_allocated; + + while (build->next_entry < end_offset) { + build->tree[build->next_entry] = build->tree_allocated; + build->tree_allocated += 2; + ++build->next_entry; + } +} + +// Read the next entry from the queue of entries waiting to be used. + +static unsigned int read_next_entry(TreeBuildData *build) +{ + unsigned int result; + + // Sanity check. + + if (build->next_entry >= build->tree_allocated) { + return 0; + } + + result = build->next_entry; + ++build->next_entry; + + return result; +} + +// Add all codes to the tree that have the specified length. +// Returns non-zero if there are any entries in code_lengths[] still +// waiting to be added to the tree. + +static int add_codes_with_length(TreeBuildData *build, + uint8_t *code_lengths, + unsigned int num_code_lengths, + unsigned int code_len) +{ + unsigned int i; + unsigned int node; + int codes_remaining; + + codes_remaining = 0; + + for (i = 0; i < num_code_lengths; ++i) { + + // Does this code belong at this depth in the tree? + + if (code_lengths[i] == code_len) { + node = read_next_entry(build); + + build->tree[node] = (TreeElement) i | TREE_NODE_LEAF; + } + + // More work to be done after this pass? + + else if (code_lengths[i] > code_len) { + codes_remaining = 1; + } + } + + return codes_remaining; +} + +// Build a tree, given the specified array of codes indicating the +// required depth within the tree at which each code should be +// located. + +static void build_tree(TreeElement *tree, size_t tree_len, + uint8_t *code_lengths, unsigned int num_code_lengths) +{ + TreeBuildData build; + unsigned int code_len; + + build.tree = tree; + build.tree_len = tree_len; + + // Start with a single entry in the queue - the root node + // pointer. + + build.next_entry = 0; + + // We always have the root ... + + build.tree_allocated = 1; + + // Iterate over each possible code length. + // Note: code_len == 0 is deliberately skipped over, as 0 + // indicates "not used". + + code_len = 0; + + do { + // Advance to the next code length by allocating extra + // nodes to the tree - the slots waiting in the queue + // will now be one level deeper in the tree (and the + // codes 1 bit longer). + + expand_queue(&build); + ++code_len; + + // Add all codes that have this length. + + } while (add_codes_with_length(&build, code_lengths, + num_code_lengths, code_len)); +} + +/* +static void display_tree(TreeElement *tree, unsigned int node, int offset) +{ + unsigned int i; + + if (node & TREE_NODE_LEAF) { + for (i = 0; i < offset; ++i) putchar(' '); + printf("leaf %i\n", node & ~TREE_NODE_LEAF); + } else { + for (i = 0; i < offset; ++i) putchar(' '); + printf("0 ->\n"); + display_tree(tree, tree[node], offset + 4); + for (i = 0; i < offset; ++i) putchar(' '); + printf("1 ->\n"); + display_tree(tree, tree[node + 1], offset + 4); + } +} +*/ + +// Read bits from the input stream, traversing the specified tree +// from the root node until we reach a leaf. The leaf value is +// returned. + +static int read_from_tree(BitStreamReader *reader, TreeElement *tree) +{ + TreeElement code; + int bit; + + // Start from root. + + code = tree[0]; + + while ((code & TREE_NODE_LEAF) == 0) { + + bit = read_bit(reader); + + if (bit < 0) { + return -1; + } + + code = tree[code + (unsigned int) bit]; + } + + // Mask off leaf bit to get the plain code. + + return (int) (code & ~TREE_NODE_LEAF); +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/liblhasa.pc.in b/Src/external_dependencies/openmpt-trunk/include/lhasa/liblhasa.pc.in new file mode 100644 index 00000000..e0400716 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/liblhasa.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: liblhasa +Description: LHA (de)compression library +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -llhasa +Cflags: -I${includedir}/liblhasa-1.0 diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/pkg/Makefile.am b/Src/external_dependencies/openmpt-trunk/include/lhasa/pkg/Makefile.am new file mode 100644 index 00000000..4f6b5884 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/pkg/Makefile.am @@ -0,0 +1,5 @@ + +EXTRA_DIST = \ +win32/GNUmakefile \ +win32/README + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/pkg/config.make.in b/Src/external_dependencies/openmpt-trunk/include/lhasa/pkg/config.make.in new file mode 100644 index 00000000..398203c9 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/pkg/config.make.in @@ -0,0 +1,18 @@ +# This file is included by package makefiles, it is autogenerated +# by configure. + +CC = @CC@ +STRIP = @STRIP@ + +PACKAGE = @PACKAGE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ + +# Documentation files to distribute with packages. + +DOC_FILES = README \ + COPYING \ + NEWS + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/pkg/win32/GNUmakefile b/Src/external_dependencies/openmpt-trunk/include/lhasa/pkg/win32/GNUmakefile new file mode 100644 index 00000000..a40ab6b1 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/pkg/win32/GNUmakefile @@ -0,0 +1,26 @@ + +include ../config.make + +TOPLEVEL=../.. + +EXE_FILES=$(TOPLEVEL)/src/lha.exe + +ZIP=$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)-win32.zip + +$(ZIP): staging + zip -j -r $@ staging/ + +staging: $(EXE_FILES) $(patsubst %,$(TOPLEVEL)/%,$(DOC_FILES)) + rm -rf staging + mkdir staging + cp $(EXE_FILES) staging/ + $(STRIP) staging/*.exe + for f in $(DOC_FILES); do \ + cp $(TOPLEVEL)/$$f staging/$$f.txt; \ + unix2dos staging/$$f.txt; \ + done + groff -Thtml -mandoc $(TOPLEVEL)/doc/lha.1 > staging/lha.html + +clean: + rm -rf $(ZIP) + rm -rf staging diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/pkg/win32/README b/Src/external_dependencies/openmpt-trunk/include/lhasa/pkg/win32/README new file mode 100644 index 00000000..da303624 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/pkg/win32/README @@ -0,0 +1,9 @@ + +Makefile to build Windows packages. Requires zip and unix2dos cygwin +packages to be installed. + +The configure script should be run with the right options to disable +shared libraries: + + ./configure --disable-shared --enable-static + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/rpm.spec.in b/Src/external_dependencies/openmpt-trunk/include/lhasa/rpm.spec.in new file mode 100644 index 00000000..63f8b0da --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/rpm.spec.in @@ -0,0 +1,53 @@ + +Name: @PACKAGE@ +Summary: Free LZH archive tool +Version: @VERSION@ +Release: 1 +Source: https://github.com/downloads/fragglet/@PACKAGE@/@PACKAGE@-@VERSION@.tar.gz +URL: http://fragglet.github.com/lhasa/ +Group: Applications/Archiving +BuildRoot: /var/tmp/@PACKAGE@-buildroot +License: ISC license +Packager: Simon Howard <@PACKAGE_BUGREPORT@> +Prefix: %{_prefix} +Autoreq: 0 + +%description +%(cat README) + +See @PACKAGE_URL@ for more information. + +%prep +rm -rf $RPM_BUILD_ROOT + +%setup -q + +%build +./configure \ + --prefix=/usr \ + --exec-prefix=/usr \ + --bindir=/usr/bin \ + --sbindir=/usr/sbin \ + --sysconfdir=/etc \ + --datadir=/usr/share \ + --includedir=/usr/include \ + --libdir=/usr/lib \ + --libexecdir=/usr/lib \ + --localstatedir=/var/lib \ + --sharedstatedir=/usr/com \ + --mandir=/usr/share/man \ + --infodir=/usr/share/info +make + +%install +%makeinstall + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%doc %{_mandir}/man1/* +/usr/bin/* +/usr/lib*/* +/usr/include/*/* + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/src/Makefile.am b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/Makefile.am new file mode 100644 index 00000000..5d3af87b --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/Makefile.am @@ -0,0 +1,23 @@ + +bin_PROGRAMS=lha +check_PROGRAMS=test-lha + +SOURCE_FILES= \ + main.c \ + options.h \ + filter.c filter.h \ + list.c list.h \ + extract.c extract.h \ + safe.c safe.h + +lha_SOURCES=$(SOURCE_FILES) +lha_CFLAGS=$(MAIN_CFLAGS) -I$(top_builddir)/lib/public -I$(top_builddir) +lha_LDADD=$(top_builddir)/lib/liblhasa.la + +test_lha_SOURCES=$(SOURCE_FILES) +test_lha_CFLAGS=$(TEST_CFLAGS) -I$(top_builddir)/lib/public -I$(top_builddir) +test_lha_LDADD=$(top_builddir)/lib/liblhasatest.a + +clean-local: + rm -f *.gcno *.gcda *.c.gcov + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/src/args.txt b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/args.txt new file mode 100644 index 00000000..e66baf04 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/args.txt @@ -0,0 +1,76 @@ +
+Notes on command line arguments:
+
+Mode - must be the first character of the first argument. Leading '-'
+is ignored:
+
+ e/x - Extract
+ l/v - List (normal / verbose mode)
+ t - Test CRC
+ p - Print files to STDOUT
+
+Options - flags following the mode character. Options are accepted
+even if they are inappropriate for the mode, eg. "lha lf".
+
+ q[level] - Quiet mode. Normal running is quiet level 0.
+
+ in list mode, quiet level 2 strips header/footer from
+ listing.
+
+ in extract mode, overwrite is always performed, and:
+ - at quiet level 1, the 'progress bar' is not shown.
+ - at quiet level 2, no messages are printed at all.
+
+ in test mode, behavior is similar to extract mode, but
+ level 2 behavior is similar to level 0.
+
+ in print mode, at quiet level 2 the filename is not
+ printed before the file contents.
+
+ all quiet modes imply 'f', disabling overwrite without
+ confirmation.
+
+ f - Force overwrite. When extracting, existing files are
+ overwritten with no confirmation.
+
+ n - Don't execute commands, just do a dry run of what would
+ be done.
+
+ eg. for lha en:
+
+ EXTRACT lhasa/src/ (directory)
+ EXTRACT lhasa/src/extract.c
+ EXTRACT lhasa/src/extract.c but file is exist.
+
+ eg. for lha tn:
+
+ VERIFY lhasa/src/main.c
+
+ i - Ignore directory path. When extracting, the directory
+ of each file is ignored - they are all extracted into
+ the same directory.
+
+ Also applies to test mode - the output filename of the
+ tested file does not include the directory.
+
+ In print mode, the path is not printed in the header
+ before each file.
+
+ v - Verbose mode, prints extra information.
+
+ in list mode, verbose mode splits the filename onto a
+ separate preceding line and the header level is shown
+ in its normal place.
+
+ in extract mode, verbose mode prints a message when
+ creating a directory. If creating missing parent directories,
+ they are printed in reverse order, eg.
+
+ Making directory "lhasa/src".
+ Making directory "lhasa".
+ lhasa/src/main.c - Melted : o
+
+ w=dir - Specify destination directory to extract files. Must be
+ the last option specified. '=' can be omitted. If combined
+ with 'i', all files get placed in that one directory.
+
diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/src/extract.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/extract.c new file mode 100644 index 00000000..1e7f5c92 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/extract.c @@ -0,0 +1,676 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "lib/lha_arch.h" + +#include "extract.h" +#include "safe.h" + +// Maximum number of dots in progress output: + +#define MAX_PROGRESS_LEN 58 + +typedef struct { + int invoked; + LHAFileHeader *header; + LHAOptions *options; + char *filename; + char *operation; +} ProgressCallbackData; + +// Given a file header structure, get the path to extract to. +// Returns a newly allocated string that must be free()d. + +static char *file_full_path(LHAFileHeader *header, LHAOptions *options) +{ + size_t len; + char *result; + char *p; + + // Full path, or filename only? + + len = 0; + + if (options->extract_path != NULL) { + len += strlen(options->extract_path) + 1; + } + + if (options->use_path && header->path != NULL) { + len += strlen(header->path); + } + + if (header->filename != NULL) { + len += strlen(header->filename); + } + + result = malloc(len + 1); + + if (result == NULL) { + // TODO? + exit(-1); + } + + result[0] = '\0'; + + if (options->extract_path != NULL) { + strcat(result, options->extract_path); + strcat(result, "/"); + } + + // Add path. If it is an absolute path (contains a leading '/') + // then skip over the leading '/' to make it a relative path. + // This prevents the possibility of a security threat with a + // malicious archive that might try to write to arbitrary + // filesystem locations. + // It also removes the double '/' when using the -w option. + + if (options->use_path && header->path != NULL) { + p = header->path; + while (*p == '/') { + ++p; + } + strcat(result, p); + } + + // The filename header field might conceivably try to include + // a path separator as well, so skip over any leading '/' + // here too. + + if (header->filename != NULL) { + p = header->filename; + while (*p == '/') { + ++p; + } + strcat(result, p); + } + + return result; +} + +static void print_filename(char *filename, char *status) +{ + printf("\r"); + safe_printf("%s", filename); + printf("\t- %s ", status); +} + +static void print_filename_brief(char *filename) +{ + printf("\r"); + safe_printf("%s :", filename); +} + +// Callback function invoked during decompression progress. + +static void progress_callback(unsigned int block, + unsigned int num_blocks, + void *data) +{ + ProgressCallbackData *progress = data; + unsigned int factor; + unsigned int i; + + progress->invoked = 1; + + // If the quiet mode options are specified, print a limited amount + // of information without a progress bar (level 1) or no message + // at all (level 2). + + if (progress->options->quiet >= 2) { + return; + } else if (progress->options->quiet == 1) { + if (block == 0) { + print_filename_brief(progress->filename); + fflush(stdout); + } + + return; + } + + // Scale factor for blocks, so that the line is never too long. When + // MAX_PROGRESS_LEN is exceeded, the length is halved (factor=2), then + // progressively larger scale factors are applied. + + factor = 1 + (num_blocks / MAX_PROGRESS_LEN); + num_blocks = (num_blocks + factor - 1) / factor; + + // First call to specify number of blocks? + + if (block == 0) { + print_filename(progress->filename, progress->operation); + + for (i = 0; i < num_blocks; ++i) { + printf("."); + } + + print_filename(progress->filename, progress->operation); + } else if (((block + factor - 1) % factor) == 0) { + // Otherwise, signal progress: + + printf("o"); + } + + fflush(stdout); +} + +// Print a line to stdout describing a symlink. + +static void print_symlink_line(char *src, char *dest) +{ + safe_printf("Symbolic Link %s -> %s", src, dest); + printf("\n"); +} + +// Perform CRC check of an archived file. + +static int test_archived_file_crc(LHAReader *reader, + LHAFileHeader *header, + LHAOptions *options) +{ + ProgressCallbackData progress; + char *filename; + int success; + + filename = file_full_path(header, options); + + if (options->dry_run) { + if (strcmp(header->compress_method, + LHA_COMPRESS_TYPE_DIR) != 0) { + safe_printf("VERIFY %s", filename); + printf("\n"); + } + + free(filename); + return 1; + } + + progress.invoked = 0; + progress.operation = "Testing :"; + progress.options = options; + progress.filename = filename; + progress.header = header; + + success = lha_reader_check(reader, progress_callback, &progress); + + if (progress.invoked && options->quiet < 2) { + if (success) { + print_filename(filename, "Tested"); + printf("\n"); + } else { + print_filename(filename, "CRC error"); + printf("\n"); + } + + fflush(stdout); + } + + if (!success) { + // TODO: Exit with error + } + + free(filename); + + return success; +} + +// Check that the specified directory exists, and create it if it +// does not. + +static int check_parent_directory(char *path) +{ + LHAFileType file_type; + + file_type = lha_arch_exists(path); + + switch (file_type) { + + case LHA_FILE_DIRECTORY: + // Already exists. + break; + + case LHA_FILE_NONE: + // Create the missing directory: + + if (!lha_arch_mkdir(path, 0755)) { + fprintf(stderr, + "Failed to create parent directory %s\n", + path); + return 0; + } + break; + + case LHA_FILE_FILE: + fprintf(stderr, "Parent path %s is not a directory!\n", + path); + return 0; + + case LHA_FILE_ERROR: + fprintf(stderr, "Failed to stat %s\n", path); + return 0; + } + + return 1; +} + +// Given a filename, create its parent directories as necessary. + +static int make_parent_directories(char *orig_path) +{ + int result; + char *p; + char *path; + + result = 1; + + // Duplicate the path and strip off any trailing '/'s: + + path = strdup(orig_path); + + if (path == NULL) { + exit(-1); + } + + p = path + strlen(path) - 1; + + while (p >= path && *p == '/') { + *p = '\0'; + --p; + } + + // Iterate through the string, finding each path separator. At + // each place, temporarily chop off the end of the path to get + // each parent directory in turn. + + for (p = path; *p == '/'; ++p); + + for (;;) { + p = strchr(p, '/'); + + if (p == NULL) { + break; + } + + *p = '\0'; + + // Check if this parent directory exists and create it: + + if (!check_parent_directory(path)) { + result = 0; + break; + } + + // Restore path separator and advance to the next path. + + *p = '/'; + ++p; + } + + free(path); + + return result; +} + +// Prompt the user with a message, and return the first character of +// the typed response. + +static char prompt_user(char *message) +{ + char result; + int c; + + fprintf(stderr, "%s", message); + fflush(stderr); + + // Read characters until a newline is found, saving the first + // character entered. + + result = 0; + + do { + c = getchar(); + + if (c < 0) { + exit(-1); + } + + if (result == 0) { + result = c; + } + } while (c != '\n'); + + return result; +} + +// A file to be extracted already exists. Apply the overwrite policy +// to decide whether to overwrite the existing file, prompting the +// user if necessary. + +static int confirm_file_overwrite(char *filename, LHAOptions *options) +{ + char response; + + switch (options->overwrite_policy) { + case LHA_OVERWRITE_PROMPT: + break; + case LHA_OVERWRITE_SKIP: + return 0; + case LHA_OVERWRITE_ALL: + return 1; + } + + for (;;) { + safe_fprintf(stderr, "%s ", filename); + + response = prompt_user("OverWrite ?(Yes/[No]/All/Skip) "); + + switch (tolower((unsigned int) response)) { + case 'y': + return 1; + case 'n': + case '\n': + return 0; + case 'a': + options->overwrite_policy = LHA_OVERWRITE_ALL; + return 1; + case 's': + options->overwrite_policy = LHA_OVERWRITE_SKIP; + return 0; + default: + break; + } + } + + return 0; +} + +// Check if the file pointed to by the specified header exists. + +static int file_exists(char *filename) +{ + LHAFileType file_type; + + file_type = lha_arch_exists(filename); + + if (file_type == LHA_FILE_ERROR) { + fprintf(stderr, "Failed to read file type of '%s'\n", filename); + exit(-1); + } + + return file_type != LHA_FILE_NONE; +} + +// Extract an archived file. + +static int extract_archived_file(LHAReader *reader, + LHAFileHeader *header, + LHAOptions *options) +{ + ProgressCallbackData progress; + char *filename; + int success; + int is_dir, is_symlink; + + filename = file_full_path(header, options); + is_symlink = header->symlink_target != NULL; + is_dir = !strcmp(header->compress_method, LHA_COMPRESS_TYPE_DIR) + && !is_symlink; + + // If a file already exists with this name, confirm overwrite. + + if (!is_dir && !is_symlink && file_exists(filename) + && !confirm_file_overwrite(filename, options)) { + if (options->overwrite_policy == LHA_OVERWRITE_SKIP) { + safe_printf("%s : Skipped...", filename); + printf("\n"); + } + free(filename); + return 1; + } + + // No need to extract directories if use_path is disabled. + + if (!options->use_path && is_dir) { + return 1; + } + + // Create parent directories for file: + + if (!make_parent_directories(filename)) { + free(filename); + return 0; + } + + progress.invoked = 0; + progress.operation = "Melting :"; + progress.options = options; + progress.header = header; + progress.filename = filename; + + success = lha_reader_extract(reader, filename, + progress_callback, &progress); + + if (!lha_reader_current_is_fake(reader) && options->quiet < 2) { + if (progress.invoked) { + if (success) { + print_filename(filename, "Melted"); + printf("\n"); + } else { + print_filename(filename, "Failure"); + printf("\n"); + } + } else if (is_symlink) { + print_symlink_line(filename, header->symlink_target); + } + + fflush(stdout); + } + + if (!success) { + // TODO: Exit with error + } + + free(filename); + + return success; +} + +// lha -t command. + +int test_file_crc(LHAFilter *filter, LHAOptions *options) +{ + int result; + + result = 1; + + for (;;) { + LHAFileHeader *header; + + header = lha_filter_next_file(filter); + + if (header == NULL) { + break; + } + + if (!test_archived_file_crc(filter->reader, header, options)) { + result = 0; + } + } + + return result; +} + +// lha -en / -xn / -pn: +// Simulate extracting an archive file, but just print the operations +// that would have been performed to stdout. + +static int extract_archive_dry_run(LHAFilter *filter, LHAOptions *options) +{ + char *filename; + int result; + + result = 1; + + for (;;) { + LHAFileHeader *header; + + header = lha_filter_next_file(filter); + + if (header == NULL) { + break; + } + + filename = file_full_path(header, options); + + // Every line begins the same - "EXTRACT filename..." + + safe_printf("EXTRACT %s", filename); + + // After the filename we might print something extra. + // The message if we have an existing file is weird, but this + // is just accurately duplicating what the Unix LHA tool says. + // The symlink handling is particularly odd - they are treated + // as directories (a bleed-through of the way in which + // symlinks are stored). + + if (header->symlink_target != NULL) { + safe_printf("|%s (directory)", header->symlink_target); + } else if (!strcmp(header->compress_method, + LHA_COMPRESS_TYPE_DIR)) { + safe_printf(" (directory)"); + } else if (file_exists(filename)) { + safe_printf(" but file is exist."); + } + printf("\n"); + + free(filename); + } + + return result; +} + +// lha -e / -x + +int extract_archive(LHAFilter *filter, LHAOptions *options) +{ + int result; + + if (options->dry_run) { + return extract_archive_dry_run(filter, options); + } + + result = 1; + + for (;;) { + LHAFileHeader *header; + + header = lha_filter_next_file(filter); + + if (header == NULL) { + break; + } + + if (!extract_archived_file(filter->reader, header, options)) { + result = 0; + } + } + + return result; +} + +// Dump contents of the current file from the specified reader to stdout. + +static int print_archived_file(LHAReader *reader) +{ + char buf[512]; + size_t bytes; + + for (;;) { + bytes = lha_reader_read(reader, buf, sizeof(buf)); + if (bytes <= 0) { + break; + } + + if (fwrite(buf, 1, bytes, stdout) < bytes) { + return 0; + } + } + + return 1; +} + +// lha -p + +int print_archive(LHAFilter *filter, LHAOptions *options) +{ + LHAFileHeader *header; + int is_normal_file; + char *full_path; + + // As a weird quirk of Unix LHA, lha -pn is equivalent to lha -en: + + if (options->dry_run) { + return extract_archive_dry_run(filter, options); + } + + for (;;) { + header = lha_filter_next_file(filter); + + if (header == NULL) { + break; + } + + is_normal_file = strcmp(header->compress_method, + LHA_COMPRESS_TYPE_DIR) != 0; + + // Print "header" before the file containing the filename. + // For normal files this is a three line separator. + // Symlinks get shown in the same way as during extract. + // Directories are ignored. + + if (options->quiet < 2) { + full_path = file_full_path(header, options); + + if (header->symlink_target != NULL) { + print_symlink_line(full_path, + header->symlink_target); + } else if (is_normal_file) { + printf("::::::::\n"); + safe_printf("%s", full_path); + printf("\n::::::::\n"); + } + + free(full_path); + } + + // If this is a normal file, dump the contents to stdout. + + if (is_normal_file && !print_archived_file(filter->reader)) { + return 0; + } + } + + return 1; +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/src/extract.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/extract.h new file mode 100644 index 00000000..c6a4c1a6 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/extract.h @@ -0,0 +1,32 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_EXTRACT_H +#define LHASA_EXTRACT_H + +#include "filter.h" +#include "options.h" + +int test_file_crc(LHAFilter *filter, LHAOptions *options); +int extract_archive(LHAFilter *filter, LHAOptions *options); +int print_archive(LHAFilter *filter, LHAOptions *options); + +#endif /* #ifndef LHASA_EXTRACT_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/src/filter.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/filter.c new file mode 100644 index 00000000..c4534699 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/filter.c @@ -0,0 +1,137 @@ +/* + +Copyright (c) 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include <stdlib.h> +#include <string.h> + +#include "filter.h" + +void lha_filter_init(LHAFilter *filter, LHAReader *reader, + char **filters, unsigned int num_filters) +{ + filter->reader = reader; + filter->filters = filters; + filter->num_filters = num_filters; +} + +static int match_glob(char *glob, char *str) +{ + // Iterate through the string, matching each character against the + // equivalent character from the glob. + + while (*str != '\0') { + + // When we reach a '*', cut off the remainder of the glob + // and shift forward through the string trying to find + // a point that matches it. + + if (*glob == '*') { + if (match_glob(glob + 1, str)) { + return 1; + } + } else if (*glob == '?' || *glob == *str) { + ++glob; + } else { + return 0; + } + + ++str; + } + + // We have reached the end of the string to match against. + // Any '*'s left at the end of the string are superfluous and + // can be ignored. + + while (*glob == '*') { + ++glob; + } + + // We now have a successful match only if we have simultaneously + // matched the end of the glob. + + return *glob == '\0'; +} + +static int matches_filter(LHAFilter *filter, LHAFileHeader *header) +{ + size_t path_len; + char *path; + unsigned int i; + + // Special case: no filters means match all. + + if (filter->num_filters == 0) { + return 1; + } + + path_len = 0; + + if (header->path != NULL) { + path_len += strlen(header->path); + } + + if (header->filename != NULL) { + path_len += strlen(header->filename); + } + + path = malloc(path_len + 1); + + if (path == NULL) { + // TODO? + return 0; + } + + path[0] = '\0'; + + if (header->path != NULL) { + strcat(path, header->path); + } + + if (header->filename != NULL) { + strcat(path, header->filename); + } + + // Check this path with the list of filters. If one matches, + // we must return true. + + for (i = 0; i < filter->num_filters; ++i) { + if (match_glob(filter->filters[i], path)) { + break; + } + } + + free(path); + + return i < filter->num_filters; +} + +LHAFileHeader *lha_filter_next_file(LHAFilter *filter) +{ + LHAFileHeader *header; + + // Read through headers until we find one that matches. + + do { + header = lha_reader_next_file(filter->reader); + } while (header != NULL && !matches_filter(filter, header)); + + return header; +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/src/filter.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/filter.h new file mode 100644 index 00000000..01c4a48d --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/filter.h @@ -0,0 +1,61 @@ +/* + +Copyright (c) 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_FILTER_H +#define LHASA_FILTER_H + +#include "lha_reader.h" + +typedef struct _LHAFilter LHAFilter; + +struct _LHAFilter { + LHAReader *reader; + char **filters; + unsigned int num_filters; +}; + +/** + * Initialize a @ref LHAFilter structure to read files from + * the specified @ref LHAReader, applying the specified list of + * filters. + * + * @param filter The filter structure to initialize. + * @param reader The reader object to read files from. + * @param filters List of strings containing glob-style filters + * to apply to the filenames to read. + * @param num_filters Number of filters in the 'filters' array. + */ + +void lha_filter_init(LHAFilter *filter, LHAReader *reader, + char **filters, unsigned int num_filters); + +/** + * Read the next file from the input stream. + * + * @param filter The filter structure. + * @return File header structure for the next file that + * matches the filters, or NULL for end of file + * or error. + */ + +LHAFileHeader *lha_filter_next_file(LHAFilter *filter); + +#endif /* #ifndef LHASA_FILTER_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/src/list.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/list.c new file mode 100644 index 00000000..79b10d2c --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/list.c @@ -0,0 +1,682 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> + +#include <sys/stat.h> + +#include "lha_reader.h" +#include "list.h" +#include "safe.h" + +typedef struct { + unsigned int num_files; + unsigned int compressed_length; + unsigned int length; + unsigned int timestamp; +} FileStatistics; + +typedef struct { + char *name; + unsigned int width; + void (*handler)(LHAFileHeader *header); + void (*footer)(FileStatistics *stats); +} ListColumn; + +// Display OS type: + +static char *os_type_to_string(uint8_t os_type) +{ + switch (os_type) { + case LHA_OS_TYPE_MSDOS: + return "[MS-DOS]"; + case LHA_OS_TYPE_WIN95: + return "[Win9x]"; + case LHA_OS_TYPE_WINNT: + return "[WinNT]"; + case LHA_OS_TYPE_UNIX: + return "[Unix]"; + case LHA_OS_TYPE_OS2: + return "[OS/2]"; + case LHA_OS_TYPE_CPM: + return "[CP/M]"; + case LHA_OS_TYPE_MACOS: + return "[Mac OS]"; + case LHA_OS_TYPE_JAVA: + return "[Java]"; + case LHA_OS_TYPE_FLEX: + return "[FLEX]"; + case LHA_OS_TYPE_RUNSER: + return "[Runser]"; + case LHA_OS_TYPE_TOWNSOS: + return "[TownsOS]"; + case LHA_OS_TYPE_OS9: + return "[OS-9]"; + case LHA_OS_TYPE_OS9_68K: + return "[OS-9/68K]"; + case LHA_OS_TYPE_OS386: + return "[OS-386]"; + case LHA_OS_TYPE_HUMAN68K: + return "[Human68K]"; + case LHA_OS_TYPE_ATARI: + return "[Atari]"; + case LHA_OS_TYPE_AMIGA: + return "[Amiga]"; + case LHA_OS_TYPE_UNKNOWN: + return "[generic]"; + default: + return "[unknown]"; + } +} + +// Print Unix file permissions. + +static void unix_permissions_print(LHAFileHeader *header) +{ + const char *perms = "rwxrwxrwx"; + unsigned int i; + + if (strcmp(header->compress_method, LHA_COMPRESS_TYPE_DIR) != 0) { + printf("-"); + } else if (header->symlink_target != NULL) { + printf("l"); + } else { + printf("d"); + } + + for (i = 0; i < 9; ++i) { + if (header->unix_perms & (1U << (8 - i))) { + printf("%c", perms[i]); + } else { + printf("-"); + } + } +} + +// Print OS-9 file permissions. + +static void os9_permissions_print(LHAFileHeader *header) +{ + const char *perms = "sewrewr"; + unsigned int i; + + if (strcmp(header->compress_method, LHA_COMPRESS_TYPE_DIR) != 0) { + printf("-"); + } else { + printf("d"); + } + + for (i = 0; i < 7; ++i) { + if (header->os9_perms & (1U << (6 - i))) { + printf("%c", perms[i]); + } else { + printf("-"); + } + } + + printf(" "); +} + +// File permissions + +static void permission_column_print(LHAFileHeader *header) +{ + // Print permissions. If we do not have any permissions to + // print, fall back to printing the OS type. + + if (LHA_FILE_HAVE_EXTRA(header, LHA_FILE_OS9_PERMS)) { + os9_permissions_print(header); + } else if (LHA_FILE_HAVE_EXTRA(header, LHA_FILE_UNIX_PERMS)) { + unix_permissions_print(header); + } else { + printf("%-10s", os_type_to_string(header->os_type)); + } +} + +static void permission_column_footer(FileStatistics *stats) +{ + printf(" Total "); +} + +static ListColumn permission_column = { + " PERMSSN", 10, + permission_column_print, + permission_column_footer +}; + +// Unix UID/GID + +static void unix_uid_gid_column_print(LHAFileHeader *header) +{ + if (LHA_FILE_HAVE_EXTRA(header, LHA_FILE_UNIX_UID_GID)) { + printf("%5i/%-5i", header->unix_uid, header->unix_gid); + } else { + printf(" "); + } +} + +static void unix_uid_gid_column_footer(FileStatistics *stats) +{ + // The UID/GID column has the total number of files + // listed below it. + + if (stats->num_files == 1) { + printf("%5i file ", stats->num_files); + } else { + printf("%5i files", stats->num_files); + } +} + +static ListColumn unix_uid_gid_column = { + " UID GID", 11, + unix_uid_gid_column_print, + unix_uid_gid_column_footer +}; + +// Compressed file size + +static void packed_column_print(LHAFileHeader *header) +{ + printf("%7lu", (unsigned long) header->compressed_length); +} + +static void packed_column_footer(FileStatistics *stats) +{ + printf("%7lu", (unsigned long) stats->compressed_length); +} + +static ListColumn packed_column = { + " PACKED", 7, + packed_column_print, + packed_column_footer +}; + +// Uncompressed file size + +static void size_column_print(LHAFileHeader *header) +{ + printf("%7lu", (unsigned long) header->length); +} + +static void size_column_footer(FileStatistics *stats) +{ + printf("%7lu", (unsigned long) stats->length); +} + +static ListColumn size_column = { + " SIZE", 7, + size_column_print, + size_column_footer +}; + +// Compression ratio + +static float compression_percent(size_t compressed, size_t uncompressed) +{ + if (uncompressed > 0) { + return ((float) compressed * 100.0f) / (float) uncompressed; + } else { + return 100.0f; + } +} + +static void ratio_column_print(LHAFileHeader *header) +{ + if (!strcmp(header->compress_method, "-lhd-")) { + printf("******"); + } else { + printf("%5.1f%%", compression_percent(header->compressed_length, + header->length)); + } +} + +static void ratio_column_footer(FileStatistics *stats) +{ + if (stats->length == 0) { + printf("******"); + } else { + printf("%5.1f%%", compression_percent(stats->compressed_length, + stats->length)); + } +} + +static ListColumn ratio_column = { + " RATIO", 6, + ratio_column_print, + ratio_column_footer +}; + +// Compression method and CRC checksum + +static void method_crc_column_print(LHAFileHeader *header) +{ + printf("%-5s %04x", header->compress_method, header->crc); +} + +static ListColumn method_crc_column = { + "METHOD CRC", 10, + method_crc_column_print +}; + +// Get the current time. + +static time_t get_now_time(void) +{ + // For test builds, allow the current time to be overridden using + // an environment variable. This is because the list output can + // change depending on the current date. + +#ifdef TEST_BUILD + char *env_val; + unsigned int result; + + env_val = getenv("TEST_NOW_TIME"); + + if (env_val != NULL + && sscanf(env_val, "%u", &result) == 1) { + return (time_t) result; + } +#endif + + return time(NULL); +} + +// File timestamp + +static void output_timestamp(unsigned int timestamp) +{ + const char *months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + struct tm *ts; + time_t tmp; + + if (timestamp == 0) { + printf(" "); + return; + } + + tmp = (time_t) timestamp; + ts = localtime(&tmp); + + // Print date: + + printf("%s %2d ", months[ts->tm_mon], ts->tm_mday); + + // If this is an old time (more than 6 months), print the year. + // For recent timestamps, print the time. + + tmp = get_now_time(); + + if ((time_t) timestamp > tmp - 6 * 30 * 24 * 60 * 60) { + printf("%02i:%02i", ts->tm_hour, ts->tm_min); + } else { + printf(" %04i", ts->tm_year + 1900); + } +} + +static void timestamp_column_print(LHAFileHeader *header) +{ + output_timestamp(header->timestamp); +}; + +static void timestamp_column_footer(FileStatistics *stats) +{ + output_timestamp(stats->timestamp); +}; + +static ListColumn timestamp_column = { + " STAMP", 12, + timestamp_column_print, + timestamp_column_footer +}; + +// Filename + +static void name_column_print(LHAFileHeader *header) +{ + if (header->path != NULL) { + safe_printf("%s", header->path); + } + if (header->filename != NULL) { + safe_printf("%s", header->filename); + } + if (header->symlink_target != NULL) { + safe_printf(" -> %s", header->symlink_target); + } +} + +static ListColumn name_column = { + " NAME", 20, + name_column_print +}; + +static ListColumn short_name_column = { + " NAME", 13, + name_column_print +}; + +// "Separate line" filename display. Used when the 'v' option +// is added. + +static void whole_line_name_column_print(LHAFileHeader *header) +{ + if (header->path != NULL) { + safe_printf("%s", header->path); + } + if (header->filename != NULL) { + safe_printf("%s", header->filename); + } + + // For wide filename mode (-v), | is used as the symlink separator, + // instead of ->. The reason is that this is how symlinks are + // represented within the file headers - the Unix LHA tool only + // does the parsing in normal list mode. + + if (header->symlink_target != NULL) { + safe_printf("|%s", header->symlink_target); + } + + printf("\n"); +} + +static ListColumn whole_line_name_column = { + "", 0, + whole_line_name_column_print +}; + +// Header level column. Used when the 'v' option is added. + +static void header_level_column_print(LHAFileHeader *header) +{ + printf(" [%i]", header->header_level); +} + +static ListColumn header_level_column = { + "", 0, + header_level_column_print +}; + +// Get the last "real" column in a list of columns. This is the last +// column with a width > 0. Beyond this last column it isn't necessary +// to print any more whitespace. + +static ListColumn *last_column(ListColumn **columns) +{ + ListColumn *last; + unsigned int i; + + last = NULL; + + for (i = 0; columns[i] != NULL; ++i) { + if (columns[i]->width != 0) { + last = columns[i]; + } + } + + return last; +} + +// Print the names of the column headings at the top of the file list. + +static void print_list_headings(ListColumn **columns) +{ + ListColumn *last; + unsigned int i, j; + + last = last_column(columns); + + for (i = 0; columns[i] != NULL; ++i) { + j = (unsigned) printf("%s", columns[i]->name); + + if (columns[i]->width > 0 && columns[i] != last) { + for (; j < columns[i]->width + 1; ++j) { + printf(" "); + } + } + } + + printf("\n"); +} + +// Print separator lines shown at top and bottom of file list. + +static void print_list_separators(ListColumn **columns) +{ + ListColumn *last; + unsigned int i, j; + + last = last_column(columns); + + for (i = 0; columns[i] != NULL; ++i) { + for (j = 0; j < columns[i]->width; ++j) { + printf("-"); + } + + if (columns[i]->width != 0 && columns[i] != last) { + printf(" "); + } + } + + printf("\n"); +} + +// Print a row in the list corresponding to a file. + +static void print_columns(ListColumn **columns, LHAFileHeader *header) +{ + ListColumn *last; + unsigned int i; + + last = last_column(columns); + + for (i = 0; columns[i] != NULL; ++i) { + columns[i]->handler(header); + + if (columns[i]->width != 0 && columns[i] != last) { + printf(" "); + } + } + + printf("\n"); +} + +// Print footer information shown at end of list (overall file stats) + +static void print_footers(ListColumn **columns, FileStatistics *stats) +{ + unsigned int i, j, len; + unsigned int num_columns; + + // Work out how many columns there are to print, ignoring trailing + // columns that have no footer: + + num_columns = 0; + + for (i = 0; columns[i] != NULL; ++i) { + ++num_columns; + } + + while (num_columns > 0 && columns[num_columns-1]->footer == NULL) { + --num_columns; + } + + // Print footers for each column. + // Some columns do not have footers: fill in with spaces instead. + + for (i = 0; i < num_columns; ++i) { + if (columns[i]->footer != NULL) { + columns[i]->footer(stats); + } else if (i + 1 < num_columns) { + len = strlen(columns[i]->name); + + for (j = 0; j < len; ++j) { + printf(" "); + } + } + + if (columns[i]->width != 0 && i + 1 < num_columns) { + printf(" "); + } + } + + printf("\n"); +} + +static unsigned int read_file_timestamp(FILE *fstream) +{ + struct stat data; + + if (fstat(fileno(fstream), &data) != 0) { + return (unsigned int) -1; + } + + return (unsigned int) data.st_mtime; +} + +// List contents of file, using the specified columns. +// Different columns are provided for basic and verbose modes. + +static void list_file_contents(LHAFilter *filter, FILE *fstream, + LHAOptions *options, ListColumn **columns) +{ + FileStatistics stats; + + if (options->quiet < 2) { + print_list_headings(columns); + print_list_separators(columns); + } + + stats.num_files = 0; + stats.length = 0; + stats.compressed_length = 0; + stats.timestamp = read_file_timestamp(fstream); + + for (;;) { + LHAFileHeader *header; + + header = lha_filter_next_file(filter); + + if (header == NULL) { + break; + } + + print_columns(columns, header); + + ++stats.num_files; + stats.length += header->length; + stats.compressed_length += header->compressed_length; + } + + if (options->quiet < 2) { + print_list_separators(columns); + print_footers(columns, &stats); + } +} + +// Used for lha -l: + +static ListColumn *normal_column_headers[] = { + &permission_column, + &unix_uid_gid_column, + &size_column, + &ratio_column, + ×tamp_column, + &name_column, + NULL +}; + +// Used for lha -lv: + +static ListColumn *normal_column_headers_verbose[] = { + &whole_line_name_column, + &permission_column, + &unix_uid_gid_column, + &size_column, + &ratio_column, + ×tamp_column, + &header_level_column, + NULL +}; + +// lha -l command. + +void list_file_basic(LHAFilter *filter, LHAOptions *options, FILE *fstream) +{ + ListColumn **headers; + + if (options->verbose) { + headers = normal_column_headers_verbose; + } else { + headers = normal_column_headers; + } + + list_file_contents(filter, fstream, options, headers); +} + +// Used for lha -v: + +static ListColumn *verbose_column_headers[] = { + &permission_column, + &unix_uid_gid_column, + &packed_column, + &size_column, + &ratio_column, + &method_crc_column, + ×tamp_column, + &short_name_column, + NULL +}; + +// Used for lha -vv: + +static ListColumn *verbose_column_headers_verbose[] = { + &whole_line_name_column, + &permission_column, + &unix_uid_gid_column, + &packed_column, + &size_column, + &ratio_column, + &method_crc_column, + ×tamp_column, + &header_level_column, + NULL +}; + +// lha -v command. + +void list_file_verbose(LHAFilter *filter, LHAOptions *options, FILE *fstream) +{ + ListColumn **headers; + + if (options->verbose) { + headers = verbose_column_headers_verbose; + } else { + headers = verbose_column_headers; + } + + list_file_contents(filter, fstream, options, headers); +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/src/list.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/list.h new file mode 100644 index 00000000..3830c9bd --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/list.h @@ -0,0 +1,31 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_LIST_H +#define LHASA_LIST_H + +#include "filter.h" +#include "options.h" + +void list_file_basic(LHAFilter *filter, LHAOptions *options, FILE *fstream); +void list_file_verbose(LHAFilter *filter, LHAOptions *options, FILE *fstream); + +#endif /* #ifndef LHASA_LIST_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/src/main.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/main.c new file mode 100644 index 00000000..4c3e77f4 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/main.c @@ -0,0 +1,275 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "lib/lha_arch.h" +#include "lha_reader.h" + +#include "config.h" +#include "extract.h" +#include "list.h" + +typedef enum { + MODE_UNKNOWN, + MODE_LIST, + MODE_LIST_VERBOSE, + MODE_CRC_CHECK, + MODE_EXTRACT, + MODE_PRINT +} ProgramMode; + +static void help_page(char *progname) +{ + printf( + PACKAGE_NAME " v" PACKAGE_VERSION " command line LHA tool " + "- Copyright (C) 2011,2012 Simon Howard\n" + "usage: %s [-]{lvtxe[q{num}][finv]}[w=<dir>] archive_file [file...]\n" + "commands: options:\n" + " l,v List / Verbose List f Force overwrite (no prompt)\n" + " t Test file CRC in archive i Ignore directory path\n" + " x,e Extract from archive n Perform dry run\n" + " p Print to stdout from archive q{num} Quiet mode\n" + " v Verbose\n" + " w=<dir> Specify extract directory\n" + , progname); + + exit(-1); +} + +static int do_command(ProgramMode mode, char *filename, + LHAOptions *options, + char **filters, unsigned int num_filters) +{ + FILE *fstream; + LHAInputStream *stream; + LHAReader *reader; + LHAFilter filter; + int result; + + if (!strcmp(filename, "-")) { + fstream = stdin; + } else { + fstream = fopen(filename, "rb"); + + if (fstream == NULL) { + fprintf(stderr, "LHa: Error: %s %s\n", + filename, strerror(errno)); + exit(-1); + } + } + + stream = lha_input_stream_from_FILE(fstream); + reader = lha_reader_new(stream); + lha_filter_init(&filter, reader, filters, num_filters); + + result = 1; + + switch (mode) { + case MODE_LIST: + list_file_basic(&filter, options, fstream); + break; + + case MODE_LIST_VERBOSE: + list_file_verbose(&filter, options, fstream); + break; + + case MODE_CRC_CHECK: + result = test_file_crc(&filter, options); + break; + + case MODE_EXTRACT: + result = extract_archive(&filter, options); + break; + + case MODE_PRINT: + result = print_archive(&filter, options); + break; + + case MODE_UNKNOWN: + break; + } + + lha_reader_free(reader); + lha_input_stream_free(stream); + + fclose(fstream); + + return result; +} + +static void init_options(LHAOptions *options) +{ + options->overwrite_policy = LHA_OVERWRITE_PROMPT; + options->quiet = 0; + options->verbose = 0; + options->dry_run = 0; + options->extract_path = NULL; + options->use_path = 1; +} + +// Determine the program mode from the first character of the command +// argument. + +static ProgramMode mode_for_char(char c) +{ + switch (c) { + case 'l': + return MODE_LIST; + case 'v': + return MODE_LIST_VERBOSE; + case 't': + return MODE_CRC_CHECK; + case 'e': + case 'x': + return MODE_EXTRACT; + case 'p': + return MODE_PRINT; + default: + return MODE_UNKNOWN; + } +} + +// Parse the option flags from the command argument. + +static int parse_options(char *arg, LHAOptions *options) +{ + for (; *arg != '\0'; ++arg) { + switch (*arg) { + // Force overwrite of existing files. + case 'f': + options->overwrite_policy = LHA_OVERWRITE_ALL; + break; + + // -i option turns off paths for extract. + case 'i': + options->use_path = 0; + break; + + // Dry run? + case 'n': + options->dry_run = 1; + break; + + // Quiet mode parsing. The quiet 'level' can be + // specified - if the level is omitted, level 2 + // is implied. All quiet mode options imply + // -f (overwrite without confirmation). + case 'q': + if (arg[1] >= '0' && arg[1] <= '9') { + ++arg; + options->quiet = *arg - '0'; + } else { + options->quiet = 2; + } + options->overwrite_policy = LHA_OVERWRITE_ALL; + break; + + // Verbose mode. + case 'v': + options->verbose = 1; + break; + + // Specify extract directory: must be last option + // specified. Optional '=' separator. + case 'w': + ++arg; + if (*arg == '=') { + ++arg; + } + options->extract_path = arg; + arg += strlen(arg) - 1; + break; + + default: + return 0; + } + } + + return 1; +} + +/** + * Parse the command line options, initializing the options structure + * used by the main program code. + * + * @param cmd The 'command' argument (first command line option). + * @param mode Pointer to variable to store program mode. + * @param options Pointer to options structure to initialize. + * @return Non-zero if successful. + */ + +static int parse_command_line(char *cmd, ProgramMode *mode, + LHAOptions *options) +{ + // Parse program mode argument. Initial '-' is ignored. + + if (*cmd == '-') { + ++cmd; + } + + *mode = mode_for_char(*cmd); + + if (*mode == MODE_UNKNOWN) { + return 0; + } + + // Parse remaining options. + + if (!parse_options(cmd + 1, options)) { + return 0; + } + + return 1; +} + +int main(int argc, char *argv[]) +{ + ProgramMode mode; + LHAOptions options; + +#ifdef TEST_BUILD + // When running tests, give output to stdout in binary mode; + // on Windows, this gives the expected output (which was + // constructed for Unix): + + lha_arch_set_binary(stdout); +#endif + + // Parse the command line options and run command. + // As a shortcut, a single argument can be provided to list the + // contents of an archive ("lha foo.lzh" == "lha l foo.lzh"). + + init_options(&options); + + if (argc >= 3 && parse_command_line(argv[1], &mode, &options)) { + return !do_command(mode, argv[2], &options, + argv + 3, argc - 3); + } else if (argc == 2) { + return !do_command(MODE_LIST, argv[1], &options, NULL, 0); + } else { + help_page(argv[0]); + return 0; + } +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/src/options.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/options.h new file mode 100644 index 00000000..28d5825a --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/options.h @@ -0,0 +1,64 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_OPTIONS_H +#define LHASA_OPTIONS_H + +typedef enum { + LHA_OVERWRITE_PROMPT, + LHA_OVERWRITE_SKIP, + LHA_OVERWRITE_ALL +} LHAOverwritePolicy; + +// Options structure. Populated from command line arguments. + +typedef struct { + + // Policy to take when extracting files and a file + // already exists. + + LHAOverwritePolicy overwrite_policy; + + // "Quiet" level. Normal operation is level 0. + + int quiet; + + // "Verbose" mode. + + int verbose; + + // If true, just perform a dry run of the operations that + // would normally be performed, printing messages. + + int dry_run; + + // If not NULL, specifies a path into which to extract files. + + char *extract_path; + + // If true, use the directory path for files - otherwise, + // the directory path is ignored. + + int use_path; + +} LHAOptions; + +#endif /* #ifndef LHASA_OPTIONS_H */ + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/src/safe.c b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/safe.c new file mode 100644 index 00000000..083093ff --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/safe.c @@ -0,0 +1,107 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> + +#include "lib/lha_arch.h" + +// Routines for safe terminal output. +// +// Data in LHA files (eg. filenames) may contain malicious string +// data. If printed carelessly, this can include terminal emulator +// commands that cause very unpleasant things to occur. For more +// information, see: +// +// http://marc.info/?l=bugtraq&m=104612710031920&w=2 +// +// Quote: +// > Many of the features supported by popular terminal emulator +// > software can be abused when un-trusted data is displayed on the +// > screen. The impact of this abuse can range from annoying screen +// > garbage to a complete system compromise. + +// TODO: This may not be ideal behavior for handling files with +// names that contain Unicode characters. + +static void safe_output(FILE *stream, unsigned char *str) +{ + unsigned char *p; + + for (p = str; *p != '\0'; ++p) { + + // Accept only plain ASCII characters. + // Control characters (0x00-0x1f) are rejected, + // as is 0x7f and all characters in the upper range. + + if (*p < 0x20 || *p >= 0x7f) { + *p = '?'; + } + } + + fprintf(stream, "%s", str); +} + +// Version of printf() that strips out any potentially malicious +// characters from the outputted string. +// Note: all escape characters are considered potentially malicious, +// including newline characters. + +int safe_fprintf(FILE *stream, char *format, ...) +{ + va_list args; + int result; + unsigned char *str; + + va_start(args, format); + + result = lha_arch_vasprintf((char **) &str, format, args); + if (str == NULL) { + exit(-1); + } + safe_output(stream, str); + free(str); + + va_end(args); + + return result; +} + +int safe_printf(char *format, ...) +{ + va_list args; + int result; + unsigned char *str; + + va_start(args, format); + + result = lha_arch_vasprintf((char **) &str, format, args); + if (str == NULL) { + exit(-1); + } + safe_output(stdout, str); + free(str); + + va_end(args); + + return result; +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/lhasa/src/safe.h b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/safe.h new file mode 100644 index 00000000..fc32334c --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/lhasa/src/safe.h @@ -0,0 +1,28 @@ +/* + +Copyright (c) 2011, 2012, Simon Howard + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + */ + +#ifndef LHASA_SAFE_H +#define LHASA_SAFE_H + +int safe_fprintf(FILE *stream, char *format, ...); +int safe_printf(char *format, ...); + +#endif /* #ifndef LHASA_SAFE_H */ + |