diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index 402c5edc4..252323ffd 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -97,6 +97,7 @@ 56C63D910D647DF300EAE25A /* NSComparisonPredicate+CogPredicate.m in Sources */ = {isa = PBXBuildFile; fileRef = 56C63D900D647DF300EAE25A /* NSComparisonPredicate+CogPredicate.m */; }; 56DB084C0D6717DC00453B6A /* NSNumber+CogSort.m in Sources */ = {isa = PBXBuildFile; fileRef = 56DB084B0D6717DC00453B6A /* NSNumber+CogSort.m */; }; 56DB08550D67185300453B6A /* NSArray+CogSort.m in Sources */ = {isa = PBXBuildFile; fileRef = 56DB08540D67185300453B6A /* NSArray+CogSort.m */; }; + 83293070277886250010C07E /* OpenMPTOld.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8329306D277885790010C07E /* OpenMPTOld.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 832C1253180BD1E2005507C1 /* Cog.help in Resources */ = {isa = PBXBuildFile; fileRef = 832C1252180BD1E2005507C1 /* Cog.help */; }; 834D793F20E4EFEA00C4A5CC /* OpusPlugin.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 830B62B320E4EF89004A74B2 /* OpusPlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 834D794020E4EFEF00C4A5CC /* VorbisPlugin.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8301F94520E4EEF70017B2DC /* VorbisPlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -352,6 +353,20 @@ remoteGlobalIDString = 8314D6311A354DFE00EEE8E6; remoteInfo = sidplay; }; + 8329306C277885790010C07E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83293065277885790010C07E /* OpenMPTOld.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 83E5EFA31FFEF78100659F0F; + remoteInfo = OpenMPTOld; + }; + 8329306E2778860C0010C07E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83293065277885790010C07E /* OpenMPTOld.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 83E5EFA21FFEF78100659F0F; + remoteInfo = OpenMPTOld; + }; 834068BC20E4E40200A01561 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 17C808C00C3BD1DD005707C4 /* WavPack.xcodeproj */; @@ -623,6 +638,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( + 83293070277886250010C07E /* OpenMPTOld.bundle in CopyFiles */, 834D794020E4EFEF00C4A5CC /* VorbisPlugin.bundle in CopyFiles */, 834D793F20E4EFEA00C4A5CC /* OpusPlugin.bundle in CopyFiles */, 83A360B220E4E81D00192DAB /* Flac.bundle in CopyFiles */, @@ -829,6 +845,7 @@ 56DB08530D67185300453B6A /* NSArray+CogSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSArray+CogSort.h"; path = "Spotlight/NSArray+CogSort.h"; sourceTree = ""; }; 56DB08540D67185300453B6A /* NSArray+CogSort.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSArray+CogSort.m"; path = "Spotlight/NSArray+CogSort.m"; sourceTree = ""; }; 8314D63B1A354DFE00EEE8E6 /* sidplay.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = sidplay.xcodeproj; path = Plugins/sidplay/sidplay.xcodeproj; sourceTree = ""; }; + 83293065277885790010C07E /* OpenMPTOld.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpenMPTOld.xcodeproj; path = Plugins/OpenMPT.old/OpenMPTOld.xcodeproj; sourceTree = ""; }; 832C1252180BD1E2005507C1 /* Cog.help */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Cog.help; sourceTree = ""; }; 833F681E1CDBCAA700AFB9F0 /* es */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; 833F681F1CDBCAA800AFB9F0 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; @@ -1155,6 +1172,7 @@ 83B0669C180D5668008E3612 /* MIDI.xcodeproj */, 17C8089E0C3BD1AB005707C4 /* Musepack.xcodeproj */, 83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */, + 83293065277885790010C07E /* OpenMPTOld.xcodeproj */, 8375B05117FFEA400092A79F /* OpusPlugin.xcodeproj */, 8E8D41C20CBB0DA000135C1B /* Pls.xcodeproj */, 17C808A70C3BD1BA005707C4 /* Shorten.xcodeproj */, @@ -1492,6 +1510,14 @@ name = Products; sourceTree = ""; }; + 83293066277885790010C07E /* Products */ = { + isa = PBXGroup; + children = ( + 8329306D277885790010C07E /* OpenMPTOld.bundle */, + ); + name = Products; + sourceTree = ""; + }; 834068A720E4E40200A01561 /* Products */ = { isa = PBXGroup; children = ( @@ -1701,6 +1727,7 @@ buildRules = ( ); dependencies = ( + 8329306F2778860C0010C07E /* PBXTargetDependency */, ED69CBC625BE32B40090B90D /* PBXTargetDependency */, 834D793E20E4EFD200C4A5CC /* PBXTargetDependency */, 834D793020E4EFCC00C4A5CC /* PBXTargetDependency */, @@ -1843,6 +1870,10 @@ ProductGroup = 83CA5AF820E4E394003E463A /* Products */; ProjectRef = 83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */; }, + { + ProductGroup = 83293066277885790010C07E /* Products */; + ProjectRef = 83293065277885790010C07E /* OpenMPTOld.xcodeproj */; + }, { ProductGroup = 830B62AF20E4EF89004A74B2 /* Products */; ProjectRef = 8375B05117FFEA400092A79F /* OpusPlugin.xcodeproj */; @@ -2001,6 +2032,13 @@ remoteRef = 8314D6401A354DFF00EEE8E6 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 8329306D277885790010C07E /* OpenMPTOld.bundle */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = OpenMPTOld.bundle; + remoteRef = 8329306C277885790010C07E /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 834068BD20E4E40200A01561 /* WavPack.bundle */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; @@ -2330,6 +2368,11 @@ name = Preferences; targetProxy = 17F5623A0C3BD9280019975C /* PBXContainerItemProxy */; }; + 8329306F2778860C0010C07E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = OpenMPTOld; + targetProxy = 8329306E2778860C0010C07E /* PBXContainerItemProxy */; + }; 834D793020E4EFCC00C4A5CC /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "VorbisPlugin Plugin"; diff --git a/Frameworks/OpenMPT.old/Info.plist b/Frameworks/OpenMPT.old/Info.plist new file mode 100644 index 000000000..6d83989d4 --- /dev/null +++ b/Frameworks/OpenMPT.old/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2021 Christopher Snowhill. All rights reserved. + NSPrincipalClass + + + diff --git a/Frameworks/OpenMPT.old/OpenMPT/LICENSE b/Frameworks/OpenMPT.old/OpenMPT/LICENSE new file mode 100644 index 000000000..ae92400c3 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2004-2021, OpenMPT contributors +Copyright (c) 1997-2003, Olivier Lapicque +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the OpenMPT project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Frameworks/OpenMPT.old/OpenMPT/Makefile b/Frameworks/OpenMPT.old/OpenMPT/Makefile new file mode 100644 index 000000000..20c4553cc --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/Makefile @@ -0,0 +1,1628 @@ +# +# libopenmpt and openmpt123 GNU Makefile +# +# Authors: Joern Heusipp +# OpenMPT Devs +# +# The OpenMPT source code is released under the BSD license. +# Read LICENSE for more details. +# + +# +# Supported parameters: +# +# +# Build configuration (provide on each `make` invocation): +# +# CONFIG=[gcc|clang|mingw64-win32|mingw64-win64|emscripten|emscripten-old] (default: CONFIG=) +# +# Build configurations can override or change defaults of other build options. +# See below and in `build/make/` for details. +# +# +# Compiler options (environment variables): +# +# CC +# CXX +# LD +# AR +# CPPFLAGS +# CXXFLAGS +# CFLAGS +# LDFLAGS +# LDLIBS +# ARFLAGS +# +# CXXSTDLIB_PCLIBSPRIVATE C++ standard library (or libraries) required for +# static linking. This will be put in the pkg-config file +# libopenmpt.pc Libs.private field and used for nothing else. +# +# +# +# Build flags (provide on each `make` invocation) (defaults are shown): +# +# DYNLINK=1 Dynamically link examples and openmpt123 against libopenmpt +# SHARED_LIB=1 Build shared library +# STATIC_LIB=1 Build static library +# EXAMPLES=1 Build examples +# OPENMPT123=1 Build openmpt123 +# SHARED_SONAME=1 Set SONAME of shared library +# DEBUG=0 Build debug binaries without optimization and with symbols +# OPTIMIZE=1 Build optimized binaries +# OPTIMIZE_SIZE=0 Build size-optimized binaries +# TEST=1 Include test suite in default target. +# ONLY_TEST=0 Only build the test suite. +# STRICT=0 Treat warnings as errors. +# MODERN=0 Pass more modern compiler options. +# STDCXX=c++17 C++ standard version (only for GCC and clang) +# CHECKED=0 Enable run-time assertions. +# CHECKED_ADDRESS=0 Enable address sanitizer +# CHECKED_UNDEFINED=0 Enable undefined behaviour sanitizer +# +# +# Build flags for libopenmpt (provide on each `make` invocation) +# (defaults are 0): +# +# NO_ZLIB=1 Avoid using zlib, even if found +# NO_MPG123=1 Avoid using libmpg123, even if found +# NO_OGG=1 Avoid using libogg, even if found +# NO_VORBIS=1 Avoid using libvorbis, even if found +# NO_VORBISFILE=1 Avoid using libvorbisfile, even if found +# +# LOCAL_ZLIB=1 Build local copy of zlib, even if found +# LOCAL_MPG123=1 Build local copy of libmpg123, even if found +# LOCAL_OGG=1 Build local copy of libogg, even if found +# LOCAL_VORBIS=1 Build local copy of libvorbis, even if found +# +# NO_MINIMP3=1 Do not fallback to minimp3 +# NO_STBVORBIS=1 Do not fallback to stb_vorbis +# +# USE_ALLEGRO42=1 Use liballegro 4.2 (DJGPP only) +# BUNDLED_ALLEGRO42=1 Use liballegro 4.2 in libopenmpt source tree (DJGPP only) +# +# Build flags for libopenmpt examples and openmpt123 +# (provide on each `make` invocation) +# (defaults are 0): +# +# NO_PORTAUDIO=1 Avoid using PortAudio, even if found +# NO_PORTAUDIOCPP=1 Avoid using PortAudio C++, even if found +# +# Build flags for openmpt123 (provide on each `make` invocation) +# (defaults are 0): +# +# NO_PULSEAUDIO=1 Avoid using PulseAudio, even if found +# NO_SDL2=1 Avoid using SDL2, even if found +# NO_FLAC=1 Avoid using FLAC, even if found +# NO_SNDFILE=1 Avoid using libsndfile, even if found +# +# +# Install options (provide on each `make install` invocation) +# +# PREFIX (e.g.: PREFIX=$HOME/opt, default: PREFIX=/usr/local) +# DESTDIR (e.g.: DESTDIR=bin/dest, default: DESTDIR=) +# +# +# Verbosity: +# +# QUIET=[0,1] (default: QUIET=0) +# VERBOSE=[0,1,2] (default: VERBOSE=0) +# +# +# Supported targets: +# +# make clean +# make [all] +# make doc +# make check +# make dist +# make dist-doc +# make install +# make install-doc +# + + + +INFO = @echo +SILENT = @ +VERYSILENT = @ + + +ifeq ($(VERBOSE),2) +INFO = @true +SILENT = +VERYSILENT = +endif + +ifeq ($(VERBOSE),1) +INFO = @true +SILENT = +VERYSILENT = @ +endif + + +ifeq ($(QUIET),1) +INFO = @true +SILENT = @ +VERYSILENT = @ +endif + + +# general settings + +DYNLINK=1 +SHARED_LIB=1 +STATIC_LIB=1 +EXAMPLES=1 +FUZZ=0 +SHARED_SONAME=1 +DEBUG=0 +OPTIMIZE=1 +OPTIMIZE_SIZE=0 +TEST=1 +ONLY_TEST=0 +SOSUFFIX=.so +SOSUFFIXWINDOWS=0 +NO_SHARED_LINKER_FLAG=0 +OPENMPT123=1 +MODERN=0 +STRICT=0 + +CHECKED=0 +CHECKED_ADDRESS=0 +CHECKED_UNDEFINED=0 + +REQUIRES_RUNPREFIX=0 + + +# get commandline or defaults + +CPPFLAGS := $(CPPFLAGS) +CXXFLAGS := $(CXXFLAGS) +CFLAGS := $(CFLAGS) +LDFLAGS := $(LDFLAGS) +LDLIBS := $(LDLIBS) +ARFLAGS := $(ARFLAGS) + +PC_LIBS_PRIVATE := $(PC_LIBS_PRIVATE) + +PREFIX := $(PREFIX) +ifeq ($(PREFIX),) +PREFIX := /usr/local +endif + +MANDIR ?= $(PREFIX)/share/man +#DESTDIR := $(DESTDIR) +#ifeq ($(DESTDIR),) +#DESTDIR := bin/dest +#endif + + +# version + +include libopenmpt/libopenmpt_version.mk + +LIBOPENMPT_SO_VERSION=$(LIBOPENMPT_LTVER_CURRENT) + + +# host setup + +ifeq ($(OS),Windows_NT) + +HOST=windows +HOST_FLAVOUR= + +TOOLCHAIN_SUFFIX= + +CPPCHECK = cppcheck + +MKDIR_P = mkdir +RM = del /q /f +RMTREE = del /q /f /s +INSTALL = echo install +INSTALL_MAKE_DIR = echo install +INSTALL_DIR = echo install +FIXPATH = $(subst /,\,$1) + +NUMTHREADS:=$(NUMBER_OF_PROCESSORS) + +else + +HOST=unix +HOST_FLAVOUR= + +TOOLCHAIN_SUFFIX= + +CPPCHECK = cppcheck + +MKDIR_P = mkdir -p +RM = rm -f +RMTREE = rm -rf +INSTALL = install +INSTALL_MAKE_DIR = install -d +INSTALL_DIR = cp -r -v +FIXPATH = $1 + +UNAME_S:=$(shell uname -s) +ifeq ($(UNAME_S),Darwin) +HOST_FLAVOUR=MACOSX +endif +ifeq ($(UNAME_S),Linux) +HOST_FLAVOUR=LINUX +endif +ifeq ($(UNAME_S),FreeBSD) +HOST_FLAVOUR=FREEBSD +endif +ifeq ($(UNAME_S),Haiku) +HOST_FLAVOUR=HAIKU +endif + +ifeq ($(HOST_FLAVOUR),LINUX) +NUMTHREADS:=$(shell nproc) +else +NUMTHREADS:=1 +endif + +endif + + +# early build setup + +BINDIR_MADE:=$(shell $(MKDIR_P) bin) + + +# compiler setup + +ifeq ($(CONFIG)x,x) + +include build/make/config-defaults.mk + +else + +include build/make/config-$(CONFIG).mk + +endif + + +# build setup + +ifeq ($(SOSUFFIXWINDOWS),1) +LIBOPENMPT_SONAME=libopenmpt-$(LIBOPENMPT_SO_VERSION)$(SOSUFFIX) +else +LIBOPENMPT_SONAME=libopenmpt$(SOSUFFIX).$(LIBOPENMPT_SO_VERSION) +endif + +INSTALL_PROGRAM = $(INSTALL) -m 0755 +INSTALL_DATA = $(INSTALL) -m 0644 +INSTALL_LIB = $(INSTALL) -m 0644 +INSTALL_DATA_DIR = $(INSTALL_DIR) +INSTALL_MAKE_DIR += -m 0755 + +CPPFLAGS += -Icommon -I. -Iinclude + +ifeq ($(MPT_COMPILER_GENERIC),1) + +CXXFLAGS += +CFLAGS += +LDFLAGS += +LDLIBS += +ARFLAGS += + +ifeq ($(DEBUG),1) +CPPFLAGS += -DMPT_BUILD_DEBUG +CXXFLAGS += -g +CFLAGS += -g +else +ifeq ($(OPTIMIZE),1) +CXXFLAGS += -O +CFLAGS += -O +endif +endif + +ifeq ($(CHECKED),1) +CPPFLAGS += -DMPT_BUILD_CHECKED +CXXFLAGS += -g +CFLAGS += -g +endif + +CXXFLAGS += -Wall +CFLAGS += -Wall + +else + +ifeq ($(MPT_COMPILER_NOVISIBILITY),1) +CXXFLAGS += +CFLAGS += +else +CXXFLAGS += -fvisibility=hidden +CFLAGS += -fvisibility=hidden +endif +LDFLAGS += +LDLIBS += +ARFLAGS += + +ifeq ($(DEBUG),1) +CPPFLAGS += -DMPT_BUILD_DEBUG +CXXFLAGS += -O0 -g -fno-omit-frame-pointer +CFLAGS += -O0 -g -fno-omit-frame-pointer +else +ifeq ($(OPTIMIZE_SIZE),1) +CXXFLAGS += -Os -ffast-math +CFLAGS += -Os -ffast-math -fno-strict-aliasing +LDFLAGS += +ifeq ($(MPT_COMPILER_NOGCSECTIONS),1) +else +CXXFLAGS += -ffunction-sections -fdata-sections +CFLAGS += -ffunction-sections -fdata-sections +LDFLAGS += -Wl,--gc-sections +endif +else +ifeq ($(OPTIMIZE),1) +CXXFLAGS += -O3 -ffast-math +CFLAGS += -O3 -ffast-math -fno-strict-aliasing +endif +endif +endif + +ifeq ($(CHECKED),1) +CPPFLAGS += -DMPT_BUILD_CHECKED +CXXFLAGS += -g -fno-omit-frame-pointer +CFLAGS += -g -fno-omit-frame-pointer +endif + +ifeq ($(FUZZ),1) +CPPFLAGS += +CXXFLAGS += -fno-omit-frame-pointer +CFLAGS += -fno-omit-frame-pointer +endif + +CXXFLAGS += -Wall -Wextra -Wundef -Wcast-qual -Wcast-align $(CXXFLAGS_WARNINGS) +CFLAGS += -Wall -Wextra -Wundef -Wcast-qual -Wcast-align $(CFLAGS_WARNINGS) +LDFLAGS += $(LDFLAGS_WARNINGS) + +endif + +ifeq ($(STRICT),1) +CXXFLAGS += -Werror +CFLAGS += -Werror +endif + +ifeq ($(DYNLINK),1) +LDFLAGS_RPATH += -Wl,-rpath,./bin +LDFLAGS_LIBOPENMPT += -Lbin +LDLIBS_LIBOPENMPT += -lopenmpt +endif + +ifeq ($(HOST),unix) + +ifeq ($(IS_CROSS),1) +else +ifeq ($(shell help2man --version > /dev/null 2>&1 && echo yes ),yes) +MPT_WITH_HELP2MAN := 1 +endif +endif + +ifeq ($(shell doxygen --version > /dev/null 2>&1 && echo yes ),yes) +MPT_WITH_DOXYGEN := 1 +endif + +endif + +PC_LIBS_PRIVATE += $(CXXSTDLIB_PCLIBSPRIVATE) + +ifeq ($(HACK_ARCHIVE_SUPPORT),1) +NO_ZLIB:=1 +endif + +ifeq ($(LOCAL_ZLIB),1) +CPPFLAGS_ZLIB := -DMPT_WITH_ZLIB +LDFLAGS_ZLIB := +LDLIBS_ZLIB := +CPPFLAGS_ZLIB += -Iinclude/zlib/ +LOCAL_ZLIB_SOURCES := +LOCAL_ZLIB_SOURCES += include/zlib/adler32.c +LOCAL_ZLIB_SOURCES += include/zlib/compress.c +LOCAL_ZLIB_SOURCES += include/zlib/crc32.c +LOCAL_ZLIB_SOURCES += include/zlib/deflate.c +LOCAL_ZLIB_SOURCES += include/zlib/gzclose.c +LOCAL_ZLIB_SOURCES += include/zlib/gzlib.c +LOCAL_ZLIB_SOURCES += include/zlib/gzread.c +LOCAL_ZLIB_SOURCES += include/zlib/gzwrite.c +LOCAL_ZLIB_SOURCES += include/zlib/infback.c +LOCAL_ZLIB_SOURCES += include/zlib/inffast.c +LOCAL_ZLIB_SOURCES += include/zlib/inflate.c +LOCAL_ZLIB_SOURCES += include/zlib/inftrees.c +LOCAL_ZLIB_SOURCES += include/zlib/trees.c +LOCAL_ZLIB_SOURCES += include/zlib/uncompr.c +LOCAL_ZLIB_SOURCES += include/zlib/zutil.c +include/zlib/%.o : CFLAGS+=$(CFLAGS_SILENT) -DSTDC -DZ_HAVE_UNISTD_H +include/zlib/%.test.o : CFLAGS+=$(CFLAGS_SILENT) -DSTDC -DZ_HAVE_UNISTD_H +else +ifeq ($(NO_ZLIB),1) +else +#LDLIBS += -lz +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists zlib && echo yes),yes) +CPPFLAGS_ZLIB := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I zlib ) -DMPT_WITH_ZLIB +LDFLAGS_ZLIB := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L zlib ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other zlib ) +LDLIBS_ZLIB := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l zlib ) +PC_REQUIRES_ZLIB := zlib +else +ifeq ($(FORCE_DEPS),1) +$(error zlib not found) +else +$(warning warning: zlib not found) +endif +NO_ZLIB:=1 +endif +endif +endif + +ifeq ($(LOCAL_MPG123),1) +CPPFLAGS_MPG123 := -DMPT_WITH_MPG123 +LDFLAGS_MPG123 := +LDLIBS_MPG123 := +CPPFLAGS_MPG123 += -Iinclude/mpg123/src/libmpg123/ -Iinclude/mpg123/src/compat/ -Iinclude/mpg123/src/ -Iinclude/mpg123/ports/makefile/ +LOCAL_MPG123_SOURCES := +LOCAL_MPG123_SOURCES += include/mpg123/src/compat/compat.c +LOCAL_MPG123_SOURCES += include/mpg123/src/compat/compat_str.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/dct64.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/equalizer.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/feature.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/format.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/frame.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/icy.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/icy2utf8.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/id3.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/index.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer1.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer2.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer3.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/libmpg123.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/ntom.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/optimize.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/parse.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/readers.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/stringbuf.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/synth.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/synth_8bit.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/synth_real.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/synth_s32.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/tabinit.c +include/mpg123/src/compat/%.o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC +include/mpg123/src/compat/%.test.o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC +include/mpg123/src/libmpg123/%.o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC +include/mpg123/src/libmpg123/%.test.o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC +else +ifeq ($(NO_MPG123),1) +else +#LDLIBS += -lmpg123 +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists 'libmpg123 >= 1.14.0' && echo yes),yes) +CPPFLAGS_MPG123 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I 'libmpg123 >= 1.14.0' ) -DMPT_WITH_MPG123 +LDFLAGS_MPG123 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L 'libmpg123 >= 1.14.0' ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other 'libmpg123 >= 1.14.0' ) +LDLIBS_MPG123 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l 'libmpg123 >= 1.14.0' ) +PC_REQUIRES_MPG123 := libmpg123 +else +ifeq ($(FORCE_DEPS),1) +$(error mpg123 not found) +else +$(warning warning: mpg123 not found) +endif +NO_MPG123:=1 +endif +endif +endif + +ifeq ($(LOCAL_OGG),1) +CPPFLAGS_OGG := -DMPT_WITH_OGG +LDFLAGS_OGG := +LDLIBS_OGG := +CPPFLAGS_OGG += -Iinclude/ogg/include/ -Iinclude/ogg/ports/makefile/ +LOCAL_OGG_SOURCES := +LOCAL_OGG_SOURCES += include/ogg/src/bitwise.c +LOCAL_OGG_SOURCES += include/ogg/src/framing.c +include/ogg/src/%.o : CFLAGS+=$(CFLAGS_SILENT) +include/ogg/src/%.test.o : CFLAGS+=$(CFLAGS_SILENT) +else +ifeq ($(NO_OGG),1) +else +#LDLIBS += -logg +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists ogg && echo yes),yes) +CPPFLAGS_OGG := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I ogg ) -DMPT_WITH_OGG +LDFLAGS_OGG := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L ogg ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other ogg ) +LDLIBS_OGG := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l ogg ) +PC_REQUIRES_OGG := ogg +else +ifeq ($(FORCE_DEPS),1) +$(error ogg not found) +else +$(warning warning: ogg not found) +endif +NO_OGG:=1 +endif +endif +endif + +ifeq ($(LOCAL_VORBIS),1) +CPPFLAGS_VORBIS := -DMPT_WITH_VORBIS +LDFLAGS_VORBIS := +LDLIBS_VORBIS := +CPPFLAGS_VORBIS += -Iinclude/vorbis/include/ -Iinclude/vorbis/lib/ -DHAVE_ALLOCA_H +LOCAL_VORBIS_SOURCES := +LOCAL_VORBIS_SOURCES += include/vorbis/lib/analysis.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/bitrate.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/block.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/codebook.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/envelope.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/floor0.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/floor1.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/info.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/lookup.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/lpc.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/lsp.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/mapping0.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/mdct.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/psy.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/registry.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/res0.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/sharedbook.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/smallft.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/synthesis.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/vorbisenc.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/vorbisfile.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/window.c +include/vorbis/lib/%.o : CFLAGS+=$(CFLAGS_SILENT) +include/vorbis/lib/%.test.o : CFLAGS+=$(CFLAGS_SILENT) +else +ifeq ($(NO_VORBIS),1) +else +#LDLIBS += -lvorbis +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists vorbis && echo yes),yes) +CPPFLAGS_VORBIS := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I vorbis ) -DMPT_WITH_VORBIS +LDFLAGS_VORBIS := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L vorbis ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other vorbis ) +LDLIBS_VORBIS := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l vorbis ) +PC_REQUIRES_VORBIS := vorbis +else +ifeq ($(FORCE_DEPS),1) +$(error vorbis not found) +else +$(warning warning: vorbis not found) +endif +NO_VORBIS:=1 +endif +endif +endif + +ifeq ($(LOCAL_VORBIS),1) +CPPFLAGS_VORBISFILE := -DMPT_WITH_VORBISFILE +LDFLAGS_VORBISFILE := +LDLIBS_VORBISFILE := +else +ifeq ($(NO_VORBISFILE),1) +else +#LDLIBS += -lvorbisfile +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists vorbisfile && echo yes),yes) +CPPFLAGS_VORBISFILE := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I vorbisfile ) -DMPT_WITH_VORBISFILE +LDFLAGS_VORBISFILE := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L vorbisfile ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other vorbisfile ) +LDLIBS_VORBISFILE := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l vorbisfile ) +PC_REQUIRES_VORBISFILE := vorbisfile +else +ifeq ($(FORCE_DEPS),1) +$(error vorbisfile not found) +else +$(warning warning: vorbisfile not found) +endif +NO_VORBISFILE:=1 +endif +endif +endif + +ifeq ($(NO_SDL2),1) +else +#LDLIBS += -lsdl2 +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists 'sdl2 >= 2.0.4' && echo yes),yes) +CPPFLAGS_SDL2 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I 'sdl2 >= 2.0.4' ) -DMPT_WITH_SDL2 +LDFLAGS_SDL2 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L 'sdl2 >= 2.0.4' ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other 'sdl2 >= 2.0.4' ) +LDLIBS_SDL2 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l 'sdl2 >= 2.0.4' ) +else +ifeq ($(FORCE_DEPS),1) +$(error sdl2 not found) +else +$(warning warning: sdl2 not found) +endif +NO_SDL2:=1 +endif +endif + +ifeq ($(NO_PORTAUDIO),1) +else +#LDLIBS += -lportaudio +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists portaudio-2.0 && echo yes),yes) +CPPFLAGS_PORTAUDIO := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I portaudio-2.0 ) -DMPT_WITH_PORTAUDIO +LDFLAGS_PORTAUDIO := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L portaudio-2.0 ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other portaudio-2.0 ) +LDLIBS_PORTAUDIO := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l portaudio-2.0 ) +else +ifeq ($(FORCE_DEPS),1) +$(error portaudio not found) +else +$(warning warning: portaudio not found) +endif +NO_PORTAUDIO:=1 +endif +endif + +ifeq ($(NO_PORTAUDIOCPP),1) +else +#LDLIBS += -lportaudiocpp +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists portaudiocpp && echo yes),yes) +CPPFLAGS_PORTAUDIOCPP := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I portaudiocpp ) -DMPT_WITH_PORTAUDIOCPP +LDFLAGS_PORTAUDIOCPP := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L portaudiocpp ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other portaudiocpp ) +LDLIBS_PORTAUDIOCPP := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l portaudiocpp ) +else +ifeq ($(FORCE_DEPS),1) +$(error portaudiocpp not found) +else +$(warning warning: portaudiocpp not found) +endif +NO_PORTAUDIOCPP:=1 +endif +endif + +ifeq ($(NO_PULSEAUDIO),1) +else +#LDLIBS += -lpulse-simple +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists libpulse libpulse-simple && echo yes),yes) +CPPFLAGS_PULSEAUDIO := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I libpulse libpulse-simple ) -DMPT_WITH_PULSEAUDIO +LDFLAGS_PULSEAUDIO := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L libpulse libpulse-simple ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other libpulse libpulse-simple ) +LDLIBS_PULSEAUDIO := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l libpulse libpulse-simple ) +else +ifeq ($(FORCE_DEPS),1) +$(error pulseaudio not found) +else +$(warning warning: pulseaudio not found) +endif +NO_PULSEAUDIO:=1 +endif +endif + +ifeq ($(NO_FLAC),1) +else +#LDLIBS += -lFLAC +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists 'flac >= 1.3.0' && echo yes),yes) +CPPFLAGS_FLAC := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I 'flac >= 1.3.0' ) -DMPT_WITH_FLAC +LDFLAGS_FLAC := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L 'flac >= 1.3.0' ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other 'flac >= 1.3.0' ) +LDLIBS_FLAC := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l 'flac >= 1.3.0' ) +else +ifeq ($(FORCE_DEPS),1) +$(error flac not found) +else +$(warning warning: flac not found) +endif +NO_FLAC:=1 +endif +endif + +ifeq ($(NO_SNDFILE),1) +else +#LDLIBS += -lsndfile +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists sndfile && echo yes),yes) +CPPFLAGS_SNDFILE := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I sndfile ) -DMPT_WITH_SNDFILE +LDFLAGS_SNDFILE := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L sndfile ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other sndfile ) +LDLIBS_SNDFILE := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l sndfile ) +else +ifeq ($(FORCE_DEPS),1) +$(error sndfile not found) +else +$(warning warning: sndfile not found) +endif +NO_SNDFILE:=1 +endif +endif + +ifeq ($(USE_ALLEGRO42),1) +CPPFLAGS_ALLEGRO42 ?= +LDFLAGS_ALLEGRO42 ?= +LDLIBS_ALLEGRO42 ?= liballeg.a +CPPFLAGS_ALLEGRO42 += -DMPT_WITH_ALLEGRO42 +endif + +ifeq ($(HACK_ARCHIVE_SUPPORT),1) +CPPFLAGS += -DMPT_BUILD_HACK_ARCHIVE_SUPPORT +endif + +CPPCHECK_FLAGS += -j $(NUMTHREADS) +CPPCHECK_FLAGS += --std=c99 --std=c++17 +CPPCHECK_FLAGS += --quiet +CPPCHECK_FLAGS += --enable=warning --inline-suppr --template='{file}:{line}: warning: {severity}: {message} [{id}]' +CPPCHECK_FLAGS += --suppress=missingIncludeSystem +CPPCHECK_FLAGS += --suppress=uninitMemberVar + +CPPCHECK_FLAGS += $(CPPFLAGS) +CPPFLAGS += $(CPPFLAGS_ZLIB) $(CPPFLAGS_MPG123) $(CPPFLAGS_OGG) $(CPPFLAGS_VORBIS) $(CPPFLAGS_VORBISFILE) +LDFLAGS += $(LDFLAGS_ZLIB) $(LDFLAGS_MPG123) $(LDFLAGS_OGG) $(LDFLAGS_VORBIS) $(LDFLAGS_VORBISFILE) +LDLIBS += $(LDLIBS_ZLIB) $(LDLIBS_MPG123) $(LDLIBS_OGG) $(LDLIBS_VORBIS) $(LDLIBS_VORBISFILE) + +CPPFLAGS_OPENMPT123 += $(CPPFLAGS_SDL2) $(CPPFLAGS_PORTAUDIO) $(CPPFLAGS_PULSEAUDIO) $(CPPFLAGS_FLAC) $(CPPFLAGS_SNDFILE) $(CPPFLAGS_ALLEGRO42) +LDFLAGS_OPENMPT123 += $(LDFLAGS_SDL2) $(LDFLAGS_PORTAUDIO) $(LDFLAGS_PULSEAUDIO) $(LDFLAGS_FLAC) $(LDFLAGS_SNDFILE) $(LDFLAGS_ALLEGRO42) +LDLIBS_OPENMPT123 += $(LDLIBS_SDL2) $(LDLIBS_PORTAUDIO) $(LDLIBS_PULSEAUDIO) $(LDLIBS_FLAC) $(LDLIBS_SNDFILE) $(LDLIBS_ALLEGRO42) + + +%: %.o + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +%.o: %.cpp + $(INFO) [CXX] $< + $(VERYSILENT)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.cc) $(OUTPUT_OPTION) $< + +%.o: %.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(OUTPUT_OPTION) $< + +%.test.o: %.cpp + $(INFO) [CXX-TEST] $< + $(VERYSILENT)$(CXX) -DLIBOPENMPT_BUILD_TEST $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.test.d + $(SILENT)$(COMPILE.cc) -DLIBOPENMPT_BUILD_TEST $(OUTPUT_OPTION) $< + +%.test.o: %.c + $(INFO) [CC-TEST] $< + $(VERYSILENT)$(CC) -DLIBOPENMPT_BUILD_TEST $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.test.d + $(SILENT)$(COMPILE.c) -DLIBOPENMPT_BUILD_TEST $(OUTPUT_OPTION) $< + +%.tar.gz: %.tar + $(INFO) [GZIP] $< + $(SILENT)gzip --rsyncable --no-name --best > $@ < $< + + +-include build/dist.mk +DIST_LIBOPENMPT_VERSION_PURE:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL) +CPPFLAGS += -Ibuild/svn_version +ifeq ($(MPT_SVNVERSION),) +SVN_INFO:=$(shell svn info . > /dev/null 2>&1 ; echo $$? ) +ifeq ($(SVN_INFO),0) +# in svn checkout +MPT_SVNVERSION := $(shell svnversion -n . | tr ':' '-' ) +MPT_SVNURL := $(shell svn info --xml | grep '^' | sed 's///g' | sed 's/<\/url>//g' ) +MPT_SVNDATE := $(shell svn info --xml | grep '^' | sed 's///g' | sed 's/<\/date>//g' ) +CPPFLAGS += -D MPT_SVNURL=\"$(MPT_SVNURL)\" -D MPT_SVNVERSION=\"$(MPT_SVNVERSION)\" -D MPT_SVNDATE=\"$(MPT_SVNDATE)\" +DIST_OPENMPT_VERSION:=r$(MPT_SVNVERSION) +ifeq ($(LIBOPENMPT_VERSION_PREREL),) +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+release +else +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+r$(MPT_SVNVERSION) +endif +else +GIT_STATUS:=$(shell git status > /dev/null 2>&1 ; echo $$? ) +ifeq ($(GIT_STATUS),0) +# in git chechout +MPT_SVNVERSION := $(shell git log --grep=git-svn-id -n 1 | grep git-svn-id | tail -n 1 | tr ' ' '\n' | tail -n 2 | head -n 1 | sed 's/@/ /g' | awk '{print $$2;}' )$(shell if [ $$(git rev-list $$(git log --grep=git-svn-id -n 1 --format=format:'%H') ^$$(git log -n 1 --format=format:'%H') --count ) -ne 0 ] ; then echo M ; fi ) +MPT_SVNURL := $(shell git log --grep=git-svn-id -n 1 | grep git-svn-id | tail -n 1 | tr ' ' '\n' | tail -n 2 | head -n 1 | sed 's/@/ /g' | awk '{print $$1;}' ) +MPT_SVNDATE := $(shell git log -n 1 --date=iso --format=format:'%cd' | sed 's/ +0000/Z/g' | tr ' ' 'T' ) +CPPFLAGS += -D MPT_SVNURL=\"$(MPT_SVNURL)\" -D MPT_SVNVERSION=\"$(MPT_SVNVERSION)\" -D MPT_SVNDATE=\"$(MPT_SVNDATE)\" +DIST_OPENMPT_VERSION:=r$(MPT_SVNVERSION) +ifeq ($(LIBOPENMPT_VERSION_PREREL),) +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+release +else +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+r$(MPT_SVNVERSION) +endif +else +# not in svn checkout +DIST_OPENMPT_VERSION:=rUNKNOWN +ifeq ($(LIBOPENMPT_VERSION_PREREL),) +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+release +else +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+rUNKNOWN +endif +endif +endif +else +# in dist package +DIST_OPENMPT_VERSION:=r$(MPT_SVNVERSION) +ifeq ($(LIBOPENMPT_VERSION_PREREL),) +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+release +else +DIST_LIBOPENMPT_VERSION:=$(LIBOPENMPT_VERSION_MAJOR).$(LIBOPENMPT_VERSION_MINOR).$(LIBOPENMPT_VERSION_PATCH)$(LIBOPENMPT_VERSION_PREREL)+r$(MPT_SVNVERSION) +endif +endif +DIST_LIBOPENMPT_TARBALL_VERSION:=$(DIST_LIBOPENMPT_VERSION_PURE) + +ifeq ($(MPT_SVNVERSION),) +else +MPT_SVNREVISION := $(shell echo $(MPT_SVNVERSION) | sed 's/M//g' | sed 's/S//g' | sed 's/P//g' | sed -E 's/([0-9]+)-//g' ) +MPT_SVNDIRTY := $(shell echo $(MPT_SVNVERSION) | grep -v 'M\|S\|P' >/dev/null 2>&1 ; echo $$? ) +MPT_SVNMIXED := $(shell echo $(MPT_SVNVERSION) | grep -v '-' >/dev/null 2>&1; echo $$? ) +endif + + +CPPFLAGS += -DLIBOPENMPT_BUILD + + +COMMON_CXX_SOURCES += \ + $(sort $(wildcard common/*.cpp)) \ + +SOUNDLIB_CXX_SOURCES += \ + $(COMMON_CXX_SOURCES) \ + $(sort $(wildcard soundbase/*.cpp)) \ + $(sort $(wildcard soundlib/*.cpp)) \ + $(sort $(wildcard soundlib/plugins/*.cpp)) \ + $(sort $(wildcard soundlib/plugins/dmo/*.cpp)) \ + $(sort $(wildcard sounddsp/*.cpp)) \ + + +ifeq ($(HACK_ARCHIVE_SUPPORT),1) +SOUNDLIB_CXX_SOURCES += $(sort $(wildcard unarchiver/*.cpp)) +endif + +LIBOPENMPT_CXX_SOURCES += \ + $(SOUNDLIB_CXX_SOURCES) \ + libopenmpt/libopenmpt_c.cpp \ + libopenmpt/libopenmpt_cxx.cpp \ + libopenmpt/libopenmpt_impl.cpp \ + libopenmpt/libopenmpt_ext_impl.cpp \ + +include/miniz/miniz.o : CFLAGS+=$(CFLAGS_SILENT) +include/miniz/miniz.test.o : CFLAGS+=$(CFLAGS_SILENT) +ifeq ($(LOCAL_ZLIB),1) +LIBOPENMPT_C_SOURCES += $(LOCAL_ZLIB_SOURCES) +LIBOPENMPTTEST_C_SOURCES += $(LOCAL_ZLIB_SOURCES) +else +ifeq ($(NO_ZLIB),1) +LIBOPENMPT_C_SOURCES += include/miniz/miniz.c +LIBOPENMPTTEST_C_SOURCES += include/miniz/miniz.c +CPPFLAGS += -DMPT_WITH_MINIZ +endif +endif + +include/minimp3/minimp3.o : CFLAGS+=$(CFLAGS_SILENT) +include/minimp3/minimp3.test.o : CFLAGS+=$(CFLAGS_SILENT) +ifeq ($(LOCAL_MPG123),1) +LIBOPENMPT_C_SOURCES += $(LOCAL_MPG123_SOURCES) +LIBOPENMPTTEST_C_SOURCES += $(LOCAL_MPG123_SOURCES) +else +ifeq ($(NO_MPG123),1) +ifeq ($(NO_MINIMP3),1) +else +LIBOPENMPT_C_SOURCES += include/minimp3/minimp3.c +LIBOPENMPTTEST_C_SOURCES += include/minimp3/minimp3.c +CPPFLAGS += -DMPT_WITH_MINIMP3 +endif +endif +endif + +include/stb_vorbis/stb_vorbis.o : CFLAGS+=$(CFLAGS_SILENT) +include/stb_vorbis/stb_vorbis.test.o : CFLAGS+=$(CFLAGS_SILENT) +ifeq ($(LOCAL_VORBIS),1) +ifeq ($(LOCAL_OGG),1) +LIBOPENMPT_C_SOURCES += $(LOCAL_OGG_SOURCES) +LIBOPENMPTTEST_C_SOURCES += $(LOCAL_OGG_SOURCES) +endif +LIBOPENMPT_C_SOURCES += $(LOCAL_VORBIS_SOURCES) +LIBOPENMPTTEST_C_SOURCES += $(LOCAL_VORBIS_SOURCES) +else +ifeq ($(NO_OGG),1) +ifeq ($(NO_STBVORBIS),1) +else +LIBOPENMPT_C_SOURCES += include/stb_vorbis/stb_vorbis.c +LIBOPENMPTTEST_C_SOURCES += include/stb_vorbis/stb_vorbis.c +CPPFLAGS += -DMPT_WITH_STBVORBIS -DSTB_VORBIS_NO_PULLDATA_API -DSTB_VORBIS_NO_STDIO +endif +else +ifeq ($(NO_VORBIS),1) +ifeq ($(NO_STBVORBIS),1) +else +LIBOPENMPT_C_SOURCES += include/stb_vorbis/stb_vorbis.c +LIBOPENMPTTEST_C_SOURCES += include/stb_vorbis/stb_vorbis.c +CPPFLAGS += -DMPT_WITH_STBVORBIS -DSTB_VORBIS_NO_PULLDATA_API -DSTB_VORBIS_NO_STDIO +endif +else +ifeq ($(NO_VORBISFILE),1) +ifeq ($(NO_STBVORBIS),1) +else +LIBOPENMPT_C_SOURCES += include/stb_vorbis/stb_vorbis.c +LIBOPENMPTTEST_C_SOURCES += include/stb_vorbis/stb_vorbis.c +CPPFLAGS += -DMPT_WITH_STBVORBIS -DSTB_VORBIS_NO_PULLDATA_API -DSTB_VORBIS_NO_STDIO +endif +else +endif +endif +endif +endif + +LIBOPENMPT_OBJECTS += $(LIBOPENMPT_CXX_SOURCES:.cpp=.o) $(LIBOPENMPT_C_SOURCES:.c=.o) +LIBOPENMPT_DEPENDS = $(LIBOPENMPT_OBJECTS:.o=.d) +ALL_OBJECTS += $(LIBOPENMPT_OBJECTS) +ALL_DEPENDS += $(LIBOPENMPT_DEPENDS) + +ifeq ($(DYNLINK),1) +OUTPUT_LIBOPENMPT += bin/libopenmpt$(SOSUFFIX) +else +OBJECTS_LIBOPENMPT += $(LIBOPENMPT_OBJECTS) +endif + + +OPENMPT123_CXX_SOURCES += \ + $(sort $(wildcard openmpt123/*.cpp)) \ + +OPENMPT123_OBJECTS += $(OPENMPT123_CXX_SOURCES:.cpp=.o) +OPENMPT123_DEPENDS = $(OPENMPT123_OBJECTS:.o=.d) +ALL_OBJECTS += $(OPENMPT123_OBJECTS) +ALL_DEPENDS += $(OPENMPT123_DEPENDS) + + +LIBOPENMPTTEST_CXX_SOURCES += \ + libopenmpt/libopenmpt_test.cpp \ + $(SOUNDLIB_CXX_SOURCES) \ + $(sort $(wildcard test/*.cpp)) \ + +LIBOPENMPTTEST_OBJECTS = $(LIBOPENMPTTEST_CXX_SOURCES:.cpp=.test.o) $(LIBOPENMPTTEST_C_SOURCES:.c=.test.o) +LIBOPENMPTTEST_DEPENDS = $(LIBOPENMPTTEST_CXX_SOURCES:.cpp=.test.d) $(LIBOPENMPTTEST_C_SOURCES:.c=.test.d) +ALL_OBJECTS += $(LIBOPENMPTTEST_OBJECTS) +ALL_DEPENDS += $(LIBOPENMPTTEST_DEPENDS) + + +EXAMPLES_CXX_SOURCES += $(sort $(wildcard examples/*.cpp)) +EXAMPLES_C_SOURCES += $(sort $(wildcard examples/*.c)) + +EXAMPLES_OBJECTS += $(EXAMPLES_CXX_SOURCES:.cpp=.o) +EXAMPLES_OBJECTS += $(EXAMPLES_C_SOURCES:.c=.o) +EXAMPLES_DEPENDS = $(EXAMPLES_OBJECTS:.o=.d) +ALL_OBJECTS += $(EXAMPLES_OBJECTS) +ALL_DEPENDS += $(EXAMPLES_DEPENDS) + + +FUZZ_CXX_SOURCES += $(sort $(wildcard contrib/fuzzing/*.cpp)) +FUZZ_C_SOURCES += $(sort $(wildcard contrib/fuzzing/*.c)) + +FUZZ_OBJECTS += $(FUZZ_CXX_SOURCES:.cpp=.o) +FUZZ_OBJECTS += $(FUZZ_C_SOURCES:.c=.o) +FUZZ_DEPENDS = $(FUZZ_OBJECTS:.o=.d) +ALL_OBJECTS += $(FUZZ_OBJECTS) +ALL_DEPENDS += $(FUZZ_DEPENDS) + + +.PHONY: all +all: + +-include $(ALL_DEPENDS) + +ifeq ($(DYNLINK),1) +OUTPUTS += bin/libopenmpt$(SOSUFFIX) +endif +ifeq ($(SHARED_LIB),1) +OUTPUTS += bin/libopenmpt$(SOSUFFIX) +endif +ifeq ($(STATIC_LIB),1) +OUTPUTS += bin/libopenmpt.a +endif +ifeq ($(OPENMPT123),1) +OUTPUTS += bin/openmpt123$(EXESUFFIX) +endif +ifeq ($(EXAMPLES),1) +ifeq ($(NO_PORTAUDIO),1) +else +OUTPUTS += bin/libopenmpt_example_c$(EXESUFFIX) +OUTPUTS += bin/libopenmpt_example_c_mem$(EXESUFFIX) +OUTPUTS += bin/libopenmpt_example_c_unsafe$(EXESUFFIX) +endif +ifeq ($(NO_PORTAUDIOCPP),1) +else +OUTPUTS += bin/libopenmpt_example_cxx$(EXESUFFIX) +endif +OUTPUTS += bin/libopenmpt_example_c_pipe$(EXESUFFIX) +OUTPUTS += bin/libopenmpt_example_c_stdout$(EXESUFFIX) +OUTPUTS += bin/libopenmpt_example_c_probe$(EXESUFFIX) +endif +ifeq ($(FUZZ),1) +OUTPUTS += bin/fuzz$(EXESUFFIX) +endif +ifeq ($(TEST),1) +OUTPUTS += bin/libopenmpt_test$(EXESUFFIX) +endif +ifeq ($(HOST),unix) +OUTPUTS += bin/libopenmpt.pc +endif +ifeq ($(OPENMPT123),1) +ifeq ($(MPT_WITH_HELP2MAN),1) +OUTPUTS += bin/openmpt123.1 +endif +endif +ifeq ($(SHARED_SONAME),1) +LIBOPENMPT_LDFLAGS += -Wl,-soname,$(LIBOPENMPT_SONAME) +endif + +MISC_OUTPUTS += bin/empty.cpp +MISC_OUTPUTS += bin/empty.out +MISC_OUTPUTS += bin/openmpt123$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_c$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_c_mem$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_c_probe$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_cxx$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_c_pipe$(EXESUFFIX).norpath +MISC_OUTPUTS += bin/libopenmpt_example_c_stdout$(EXESUFFIX).norpath +MISC_OUTPUTS += libopenmpt$(SOSUFFIX) +MISC_OUTPUTS += bin/.docs +MISC_OUTPUTS += bin/libopenmpt_test$(EXESUFFIX) +MISC_OUTPUTS += bin/libopenmpt_test.wasm +MISC_OUTPUTS += bin/libopenmpt_test.wasm.js +MISC_OUTPUTS += bin/libopenmpt_test.js.mem +MISC_OUTPUTS += bin/made.docs +MISC_OUTPUTS += bin/$(LIBOPENMPT_SONAME) +MISC_OUTPUTS += bin/libopenmpt.wasm +MISC_OUTPUTS += bin/libopenmpt.wasm.js +MISC_OUTPUTS += bin/libopenmpt.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c.wasm.js +MISC_OUTPUTS += bin/libopenmpt_example_c.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_mem.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_mem.wasm.js +MISC_OUTPUTS += bin/libopenmpt_example_c_mem.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_pipe.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_pipe.wasm.js +MISC_OUTPUTS += bin/libopenmpt_example_c_pipe.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_probe.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_probe.wasm.js +MISC_OUTPUTS += bin/libopenmpt_example_c_probe.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_stdout.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_stdout.wasm.js +MISC_OUTPUTS += bin/libopenmpt_example_c_stdout.js.mem +MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe.wasm.js +MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe.js.mem +MISC_OUTPUTS += bin/openmpt.a +#old +MISC_OUTPUTS += bin/libopenmpt_example_c_safe$(EXESUFFIX) +MISC_OUTPUTS += bin/libopenmpt_example_c_safe$(EXESUFFIX).norpath + +MISC_OUTPUTDIRS += bin/dest +MISC_OUTPUTDIRS += bin/docs + +DIST_OUTPUTS += bin/dist.mk +DIST_OUTPUTS += bin/svn_version_dist.h +DIST_OUTPUTS += bin/dist.tar +DIST_OUTPUTS += bin/dist-tar.tar +DIST_OUTPUTS += bin/dist-zip.tar +DIST_OUTPUTS += bin/dist-doc.tar +DIST_OUTPUTS += bin/dist-autotools.tar +DIST_OUTPUTS += bin/dist-js.tar +DIST_OUTPUTS += bin/dist-dos.tar +DIST_OUTPUTS += bin/made.docs + +DIST_OUTPUTDIRS += bin/dist +DIST_OUTPUTDIRS += bin/dist-doc +DIST_OUTPUTDIRS += bin/dist-tar +DIST_OUTPUTDIRS += bin/dist-zip +DIST_OUTPUTDIRS += bin/dist-autotools +DIST_OUTPUTDIRS += bin/dist-js +DIST_OUTPUTDIRS += bin/dist-dos +DIST_OUTPUTDIRS += bin/docs + + + +ifeq ($(ONLY_TEST),1) +all: bin/libopenmpt_test$(EXESUFFIX) +else +all: $(OUTPUTS) +endif + +.PHONY: docs +docs: bin/made.docs + +.PHONY: doc +doc: bin/made.docs + +bin/made.docs: + $(VERYSILENT)mkdir -p bin/docs + $(INFO) [DOXYGEN] libopenmpt +ifeq ($(SILENT_DOCS),1) + $(SILENT) ( cat libopenmpt/Doxyfile ; echo 'PROJECT_NUMBER = "$(DIST_LIBOPENMPT_VERSION)"' ; echo 'WARN_IF_DOC_ERROR = NO' ) | doxygen - +else + $(SILENT) ( cat libopenmpt/Doxyfile ; echo 'PROJECT_NUMBER = "$(DIST_LIBOPENMPT_VERSION)"' ) | doxygen - +endif + $(VERYSILENT)touch $@ + +.PHONY: check +check: test + +.PHONY: test +test: bin/libopenmpt_test$(EXESUFFIX) +ifeq ($(REQUIRES_RUNPREFIX),1) + cd bin && $(RUNPREFIX) libopenmpt_test$(EXESUFFIX) +else + bin/libopenmpt_test$(EXESUFFIX) +endif + +bin/libopenmpt_test$(EXESUFFIX): $(LIBOPENMPTTEST_OBJECTS) + $(INFO) [LD-TEST] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(TEST_LDFLAGS) $(LIBOPENMPTTEST_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@ + +bin/libopenmpt.pc: + $(INFO) [GEN] $@ + $(VERYSILENT)rm -rf $@ + $(VERYSILENT)echo > $@.tmp + $(VERYSILENT)echo 'prefix=$(PREFIX)' >> $@.tmp + $(VERYSILENT)echo 'exec_prefix=$${prefix}' >> $@.tmp + $(VERYSILENT)echo 'libdir=$${exec_prefix}/lib' >> $@.tmp + $(VERYSILENT)echo 'includedir=$${prefix}/include' >> $@.tmp + $(VERYSILENT)echo '' >> $@.tmp + $(VERYSILENT)echo 'Name: libopenmpt' >> $@.tmp + $(VERYSILENT)echo 'Description: Tracker module player based on OpenMPT' >> $@.tmp + $(VERYSILENT)echo 'Version: $(DIST_LIBOPENMPT_VERSION)' >> $@.tmp + $(VERYSILENT)echo 'Requires.private: $(PC_REQUIRES_ZLIB) $(PC_REQUIRES_MPG123) $(PC_REQUIRES_OGG) $(PC_REQUIRES_VORBIS) $(PC_REQUIRES_VORBISFILE)' >> $@.tmp + $(VERYSILENT)echo 'Libs: -L$${libdir} -lopenmpt' >> $@.tmp + $(VERYSILENT)echo 'Libs.private: $(PC_LIBS_PRIVATE)' >> $@.tmp + $(VERYSILENT)echo 'Cflags: -I$${includedir}' >> $@.tmp + $(VERYSILENT)mv $@.tmp $@ + +.PHONY: install +install: $(OUTPUTS) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/include/libopenmpt + $(INSTALL_DATA) libopenmpt/libopenmpt_config.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_config.h + $(INSTALL_DATA) libopenmpt/libopenmpt_version.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_version.h + $(INSTALL_DATA) libopenmpt/libopenmpt.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt.h + $(INSTALL_DATA) libopenmpt/libopenmpt_stream_callbacks_buffer.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_stream_callbacks_buffer.h + $(INSTALL_DATA) libopenmpt/libopenmpt_stream_callbacks_fd.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_stream_callbacks_fd.h + $(INSTALL_DATA) libopenmpt/libopenmpt_stream_callbacks_file.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_stream_callbacks_file.h + $(INSTALL_DATA) libopenmpt/libopenmpt.hpp $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt.hpp + $(INSTALL_DATA) libopenmpt/libopenmpt_ext.h $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_ext.h + $(INSTALL_DATA) libopenmpt/libopenmpt_ext.hpp $(DESTDIR)$(PREFIX)/include/libopenmpt/libopenmpt_ext.hpp + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib/pkgconfig + $(INSTALL_DATA) bin/libopenmpt.pc $(DESTDIR)$(PREFIX)/lib/pkgconfig/libopenmpt.pc +ifeq ($(SHARED_LIB),1) +ifeq ($(SHARED_SONAME),1) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib + $(INSTALL_LIB) bin/$(LIBOPENMPT_SONAME) $(DESTDIR)$(PREFIX)/lib/$(LIBOPENMPT_SONAME) + ln -sf $(LIBOPENMPT_SONAME) $(DESTDIR)$(PREFIX)/lib/libopenmpt$(SOSUFFIX) +else + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib + $(INSTALL_LIB) bin/libopenmpt$(SOSUFFIX) $(DESTDIR)$(PREFIX)/lib/libopenmpt$(SOSUFFIX) +endif + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib +endif +ifeq ($(STATIC_LIB),1) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/lib + $(INSTALL_DATA) bin/libopenmpt.a $(DESTDIR)$(PREFIX)/lib/libopenmpt.a +endif +ifeq ($(OPENMPT123),1) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/bin + $(INSTALL_PROGRAM) bin/openmpt123$(EXESUFFIX).norpath $(DESTDIR)$(PREFIX)/bin/openmpt123$(EXESUFFIX) +ifeq ($(MPT_WITH_HELP2MAN),1) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(MANDIR)/man1 + $(INSTALL_DATA) bin/openmpt123.1 $(DESTDIR)$(MANDIR)/man1/openmpt123.1 +endif +endif + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/share/doc/libopenmpt + $(INSTALL_DATA) LICENSE $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/LICENSE + $(INSTALL_DATA) README.md $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/README.md + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples + $(INSTALL_DATA) examples/libopenmpt_example_c.c $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_c.c + $(INSTALL_DATA) examples/libopenmpt_example_c_mem.c $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_c_mem.c + $(INSTALL_DATA) examples/libopenmpt_example_c_probe.c $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_c_probe.c + $(INSTALL_DATA) examples/libopenmpt_example_c_unsafe.c $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_c_unsafe.c + $(INSTALL_DATA) examples/libopenmpt_example_c_pipe.c $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_c_pipe.c + $(INSTALL_DATA) examples/libopenmpt_example_c_stdout.c $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_c_stdout.c + $(INSTALL_DATA) examples/libopenmpt_example_cxx.cpp $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/examples/libopenmpt_example_cxx.cpp + +.PHONY: install-doc +install-doc: bin/made.docs +ifeq ($(MPT_WITH_DOXYGEN),1) + $(INSTALL_MAKE_DIR) $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/html/ + $(INSTALL_DATA_DIR) bin/docs/html $(DESTDIR)$(PREFIX)/share/doc/libopenmpt/html +endif + +.PHONY: dist +dist: bin/dist-tar.tar bin/dist-zip.tar bin/dist-doc.tar + +.PHONY: dist-tar +dist-tar: bin/dist-tar.tar + +bin/dist-tar.tar: bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar.gz + rm -rf bin/dist-tar.tar + cd bin/dist-tar/ && rm -rf libopenmpt + cd bin/dist-tar/ && mkdir -p libopenmpt/src.makefile/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-tar/ && cp libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar.gz libopenmpt/src.makefile/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-tar/ && tar cv --numeric-owner --owner=0 --group=0 -f ../dist-tar.tar libopenmpt + +.PHONY: dist-zip +dist-zip: bin/dist-zip.tar + +bin/dist-zip.tar: bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip + rm -rf bin/dist-zip.tar + cd bin/dist-zip/ && rm -rf libopenmpt + cd bin/dist-zip/ && mkdir -p libopenmpt/src.msvc/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-zip/ && cp libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip libopenmpt/src.msvc/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-zip/ && tar cv --numeric-owner --owner=0 --group=0 -f ../dist-zip.tar libopenmpt + +.PHONY: dist-doc +dist-doc: bin/dist-doc.tar + +bin/dist-doc.tar: bin/dist-doc/libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc.tar.gz + rm -rf bin/dist-doc.tar + cd bin/dist-doc/ && rm -rf libopenmpt + cd bin/dist-doc/ && mkdir -p libopenmpt/doc/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-doc/ && cp libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc.tar.gz libopenmpt/doc/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-doc/ && tar cv --numeric-owner --owner=0 --group=0 -f ../dist-doc.tar libopenmpt + +.PHONY: dist-js +dist-js: bin/dist-js.tar + +bin/dist-js.tar: bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar.gz + rm -rf bin/dist-js.tar + cd bin/dist-js/ && rm -rf libopenmpt + cd bin/dist-js/ && mkdir -p libopenmpt/dev.js/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-js/ && cp libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar.gz libopenmpt/dev.js/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-js/ && tar cv --numeric-owner --owner=0 --group=0 -f ../dist-js.tar libopenmpt + +.PHONY: dist-dos +dist-dos: bin/dist-dos.tar + +bin/dist-dos.tar: bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION).bin.dos.zip + rm -rf bin/dist-dos.tar + cd bin/dist-dos/ && rm -rf libopenmpt + cd bin/dist-dos/ && mkdir -p libopenmpt/bin.dos/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-dos/ && cp libopenmpt-$(DIST_LIBOPENMPT_VERSION).bin.dos.zip libopenmpt/bin.dos/$(DIST_LIBOPENMPT_TARBALL_VERSION)/ + cd bin/dist-dos/ && tar cv --numeric-owner --owner=0 --group=0 -f ../dist-dos.tar libopenmpt + +.PHONY: bin/dist.mk +bin/dist.mk: + rm -rf $@ + echo > $@.tmp + echo 'MPT_SVNVERSION=$(MPT_SVNVERSION)' >> $@.tmp + echo 'MPT_SVNURL=$(MPT_SVNURL)' >> $@.tmp + echo 'MPT_SVNDATE=$(MPT_SVNDATE)' >> $@.tmp + mv $@.tmp $@ + +.PHONY: bin/svn_version_dist.h +bin/svn_version_dist.h: + rm -rf $@ + echo > $@.tmp + echo '#pragma once' >> $@.tmp + echo '#define OPENMPT_VERSION_SVNVERSION "$(MPT_SVNVERSION)"' >> $@.tmp + echo '#define OPENMPT_VERSION_REVISION $(MPT_SVNREVISION)' >> $@.tmp + echo '#define OPENMPT_VERSION_DIRTY $(MPT_SVNDIRTY)' >> $@.tmp + echo '#define OPENMPT_VERSION_MIXEDREVISIONS $(MPT_SVNMIXED)' >> $@.tmp + echo '#define OPENMPT_VERSION_URL "$(MPT_SVNURL)"' >> $@.tmp + echo '#define OPENMPT_VERSION_DATE "$(MPT_SVNDATE)"' >> $@.tmp + echo '#define OPENMPT_VERSION_IS_PACKAGE 1' >> $@.tmp + echo >> $@.tmp + mv $@.tmp $@ + +.PHONY: bin/dist-doc/libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc.tar +bin/dist-doc/libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc.tar: docs + mkdir -p bin/dist-doc + rm -rf bin/dist-doc/libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc + mkdir -p bin/dist-doc/libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc + cp -Rv bin/docs/html bin/dist-doc/libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc/docs + cd bin/dist-doc/ && tar cv --numeric-owner --owner=0 --group=0 libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc > libopenmpt-$(DIST_LIBOPENMPT_VERSION).doc.tar + +.PHONY: bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar +bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar: bin/dist.mk bin/svn_version_dist.h + mkdir -p bin/dist-tar + rm -rf bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build + mkdir -p bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc + mkdir -p bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include + svn export ./LICENSE bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSE + svn export ./README.md bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/README.md + svn export ./Makefile bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/Makefile + svn export ./.clang-format bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/.clang-format + svn export ./bin bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin + svn export ./build/android_ndk bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/android_ndk + svn export ./build/make bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/make + svn export ./build/svn_version bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/svn_version + svn export ./common bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/common + svn export ./doc/contributing.md bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/contributing.md + svn export ./doc/libopenmpt_styleguide.md bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/libopenmpt_styleguide.md + svn export ./doc/module_formats.md bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/module_formats.md + svn export ./soundbase bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/soundbase + svn export ./soundlib bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/soundlib + svn export ./sounddsp bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/sounddsp + svn export ./test bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/test + svn export ./libopenmpt bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/libopenmpt + svn export ./examples bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/examples + svn export ./openmpt123 bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/openmpt123 + svn export ./contrib bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/contrib + svn export ./include/allegro42 bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/allegro42 + svn export ./include/cwsdpmi bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/cwsdpmi + svn export ./include/minimp3 bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/minimp3 + svn export ./include/miniz bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/miniz + svn export ./include/stb_vorbis bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/stb_vorbis + cp bin/dist.mk bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/dist.mk + cp bin/svn_version_dist.h bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/svn_version/svn_version.h + cd bin/dist-tar/ && tar cv --numeric-owner --owner=0 --group=0 libopenmpt-$(DIST_LIBOPENMPT_VERSION) > libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar + +.PHONY: bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip +bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip: bin/dist.mk bin/svn_version_dist.h + mkdir -p bin/dist-zip + rm -rf bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build + mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/genie + mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/premake + mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc + mkdir -p bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include + svn export ./LICENSE bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSE --native-eol CRLF + svn export ./README.md bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/README.md --native-eol CRLF + svn export ./Makefile bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/Makefile --native-eol CRLF + svn export ./.clang-format bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/.clang-format --native-eol CRLF + svn export ./bin bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin --native-eol CRLF + svn export ./build/genie/def bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/genie/def --native-eol CRLF + svn export ./build/premake/def bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/premake/def --native-eol CRLF + svn export ./build/premake/inc bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/premake/inc --native-eol CRLF + svn export ./build/premake/lnk bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/premake/lnk --native-eol CRLF + svn export ./build/scriptlib bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/scriptlib --native-eol CRLF + svn export ./build/svn_version bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/svn_version --native-eol CRLF + svn export ./build/vcpkg bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vcpkg --native-eol CRLF + svn export ./build/vs bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs --native-eol CRLF + svn export ./build/vs2017win7 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017win7 --native-eol CRLF + svn export ./build/vs2017win81 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017win81 --native-eol CRLF + svn export ./build/vs2017win10 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017win10 --native-eol CRLF + svn export ./build/vs2017uwp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017uwp --native-eol CRLF + svn export ./build/vs2019win7 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019win7 --native-eol CRLF + svn export ./build/vs2019win81 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019win81 --native-eol CRLF + svn export ./build/vs2019win10 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019win10 --native-eol CRLF + svn export ./build/vs2019uwp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019uwp --native-eol CRLF + svn export ./build/download_externals.cmd bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/download_externals.cmd --native-eol CRLF + svn export ./common bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/common --native-eol CRLF + svn export ./doc/contributing.md bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/contributing.md --native-eol CRLF + svn export ./doc/libopenmpt_styleguide.md bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/libopenmpt_styleguide.md --native-eol CRLF + svn export ./doc/module_formats.md bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/module_formats.md --native-eol CRLF + svn export ./soundbase bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/soundbase --native-eol CRLF + svn export ./soundlib bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/soundlib --native-eol CRLF + svn export ./sounddsp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/sounddsp --native-eol CRLF + svn export ./test bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/test --native-eol CRLF + svn export ./libopenmpt bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/libopenmpt --native-eol CRLF + svn export ./examples bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/examples --native-eol CRLF + svn export ./openmpt123 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/openmpt123 --native-eol CRLF + svn export ./contrib bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/contrib --native-eol CRLF + svn export ./include/minimp3 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/minimp3 --native-eol CRLF + svn export ./include/miniz bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/miniz --native-eol CRLF + svn export ./include/mpg123 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/mpg123 --native-eol CRLF + svn export ./include/flac bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/flac --native-eol CRLF + svn export ./include/portaudio bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/portaudio --native-eol CRLF + svn export ./include/ogg bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/ogg --native-eol CRLF + svn export ./include/pugixml bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/pugixml --native-eol CRLF + svn export ./include/stb_vorbis bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/stb_vorbis --native-eol CRLF + svn export ./include/vorbis bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/vorbis --native-eol CRLF + svn export ./include/winamp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/winamp --native-eol CRLF + svn export ./include/xmplay bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/xmplay --native-eol CRLF + svn export ./include/zlib bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/zlib --native-eol CRLF + cp bin/dist.mk bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/dist.mk + cp bin/svn_version_dist.h bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/svn_version/svn_version.h + cd bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/ && zip -r ../libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip --compression-method deflate -9 * + +.PHONY: bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION).zip +bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION).zip: bin/svn_version_dist.h + mkdir -p bin/dist-zip + rm -rf bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION) + svn export ./ bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION)/ --native-eol CRLF + cp bin/svn_version_dist.h bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION)/common/svn_version_default/svn_version.h + cd bin/dist-zip/OpenMPT-src-$(DIST_OPENMPT_VERSION)/ && zip -r ../OpenMPT-src-$(DIST_OPENMPT_VERSION).zip --compression-method deflate -9 * + +.PHONY: bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar +bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar: + mkdir -p bin/dist-js + rm -rf bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/licenses + svn export ./LICENSE bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/license.txt + svn export ./include/minimp3/LICENSE bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/licenses/license.minimp3.txt + svn export ./include/miniz/miniz.c bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/licenses/license.miniz.txt + svn export ./include/stb_vorbis/stb_vorbis.c bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/licenses/license.stb_vorbis.txt + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all + cp bin/stage/all/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all/libopenmpt.js + cp bin/stage/all/libopenmpt.js.mem bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all/libopenmpt.js.mem + cp bin/stage/all/libopenmpt.wasm bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all/libopenmpt.wasm + cp bin/stage/all/libopenmpt.wasm.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all/libopenmpt.wasm.js + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/wasm + cp bin/stage/wasm/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/wasm/libopenmpt.js + cp bin/stage/wasm/libopenmpt.wasm bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/wasm/libopenmpt.wasm + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/js + cp bin/stage/js/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/js/libopenmpt.js + cp bin/stage/js/libopenmpt.js.mem bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/js/libopenmpt.js.mem + cd bin/dist-js/ && tar cv --numeric-owner --owner=0 --group=0 libopenmpt-$(DIST_LIBOPENMPT_VERSION) > libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar + +.PHONY: bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION).bin.dos.zip +bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION).bin.dos.zip: + mkdir -p bin/dist-dos + rm -rf bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION) + mkdir -p bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES + svn export ./LICENSE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSE.TXT --native-eol CRLF + cd bin/dist-dos && unzip ../../build/externals/all422s.zip allegro/readme.txt + mv bin/dist-dos/allegro/readme.txt bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/ALLEGRO.TXT + rmdir bin/dist-dos/allegro + cp include/cwsdpmi/bin/cwsdpmi.doc bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/CWSDPMI.TXT + svn export ./include/minimp3/LICENSE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/MINIMP3.TXT --native-eol CRLF + svn export ./include/miniz/miniz.c bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/MINIZ.TXT --native-eol CRLF + svn export ./include/stb_vorbis/stb_vorbis.c bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/LICENSES/STBVORB.TXT --native-eol CRLF + mkdir -p bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/SRC + cp build/externals/csdpmi7s.zip bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/SRC/CSDPMI7S.ZIP + mkdir -p bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN + cp bin/openmpt123.exe bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/OMPT123.EXE + cp include/cwsdpmi/bin/cwsdpmi.doc bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSDPMI.DOC + cp include/cwsdpmi/bin/CWSDPMI.EXE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSDPMI.EXE + cp include/cwsdpmi/bin/CWSDPR0.EXE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSDPR0.EXE + cp include/cwsdpmi/bin/cwsparam.doc bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSPARAM.DOC + cp include/cwsdpmi/bin/CWSPARAM.EXE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSPARAM.EXE + cp include/cwsdpmi/bin/CWSDSTUB.EXE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSDSTUB.EXE + cp include/cwsdpmi/bin/CWSDSTR0.EXE bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/BIN/CWSDSTR0.EXE + cd bin/dist-dos/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/ && zip -r ../libopenmpt-$(DIST_LIBOPENMPT_VERSION).bin.dos.zip --compression-method deflate -9 * + +bin/libopenmpt.a: $(LIBOPENMPT_OBJECTS) + $(INFO) [AR] $@ + $(SILENT)$(AR) $(ARFLAGS) $@ $^ + +bin/libopenmpt$(SOSUFFIX): $(LIBOPENMPT_OBJECTS) + $(INFO) [LD] $@ +ifeq ($(NO_SHARED_LINKER_FLAG),1) + $(SILENT)$(LINK.cc) $(LIBOPENMPT_LDFLAGS) $(SO_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ +else + $(SILENT)$(LINK.cc) -shared $(LIBOPENMPT_LDFLAGS) $(SO_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ +endif +ifeq ($(SHARED_SONAME),1) + $(SILENT)mv bin/libopenmpt$(SOSUFFIX) bin/$(LIBOPENMPT_SONAME) + $(SILENT)ln -sf $(LIBOPENMPT_SONAME) bin/libopenmpt$(SOSUFFIX) +endif + +bin/openmpt123.1: bin/openmpt123$(EXESUFFIX) openmpt123/openmpt123.h2m + $(INFO) [HELP2MAN] $@ + $(SILENT)help2man --no-discard-stderr --no-info --version-option=--man-version --help-option=--man-help --include=openmpt123/openmpt123.h2m $< > $@ + +openmpt123/openmpt123.o: openmpt123/openmpt123.cpp + $(INFO) [CXX] $< + $(VERYSILENT)$(CXX) $(CXXFLAGS) $(CXXFLAGS_OPENMPT123) $(CPPFLAGS) $(CPPFLAGS_OPENMPT123) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.cc) $(CXXFLAGS_OPENMPT123) $(CPPFLAGS_OPENMPT123) $(OUTPUT_OPTION) $< +bin/openmpt123$(EXESUFFIX): $(OPENMPT123_OBJECTS) $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_OPENMPT123) $(OPENMPT123_OBJECTS) $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_OPENMPT123) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_OPENMPT123) $(OPENMPT123_OBJECTS) $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_OPENMPT123) -o $@ +endif + +contrib/fuzzing/fuzz.o: contrib/fuzzing/fuzz.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(OUTPUT_OPTION) $< +bin/fuzz$(EXESUFFIX): contrib/fuzzing/fuzz.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_LIBOPENMPT) contrib/fuzzing/fuzz.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) contrib/fuzzing/fuzz.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +endif + +examples/libopenmpt_example_c.o: examples/libopenmpt_example_c.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CFLAGS_PORTAUDIO) $(CPPFLAGS) $(CPPFLAGS_PORTAUDIO) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(CFLAGS_PORTAUDIO) $(CPPFLAGS_PORTAUDIO) $(OUTPUT_OPTION) $< +examples/libopenmpt_example_c_mem.o: examples/libopenmpt_example_c_mem.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CFLAGS_PORTAUDIO) $(CPPFLAGS) $(CPPFLAGS_PORTAUDIO) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(CFLAGS_PORTAUDIO) $(CPPFLAGS_PORTAUDIO) $(OUTPUT_OPTION) $< +examples/libopenmpt_example_c_unsafe.o: examples/libopenmpt_example_c_unsafe.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CFLAGS_PORTAUDIO) $(CPPFLAGS) $(CPPFLAGS_PORTAUDIO) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(CFLAGS_PORTAUDIO) $(CPPFLAGS_PORTAUDIO) $(OUTPUT_OPTION) $< +examples/libopenmpt_example_c_pipe.o: examples/libopenmpt_example_c_pipe.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(OUTPUT_OPTION) $< +examples/libopenmpt_example_c_stdout.o: examples/libopenmpt_example_c_stdout.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(OUTPUT_OPTION) $< +examples/libopenmpt_example_c_probe.o: examples/libopenmpt_example_c_probe.c + $(INFO) [CC] $< + $(VERYSILENT)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.c) $(OUTPUT_OPTION) $< +examples/libopenmpt_example_cxx.o: examples/libopenmpt_example_cxx.cpp + $(INFO) [CXX] $< + $(VERYSILENT)$(CXX) $(CXXFLAGS) $(CXXFLAGS_PORTAUDIOCPP) $(CPPFLAGS) $(CPPFLAGS_PORTAUDIOCPP) $(TARGET_ARCH) -M -MT$@ $< > $*.d + $(SILENT)$(COMPILE.cc) $(CXXFLAGS_PORTAUDIOCPP) $(CPPFLAGS_PORTAUDIOCPP) $(OUTPUT_OPTION) $< +bin/libopenmpt_example_c$(EXESUFFIX): examples/libopenmpt_example_c.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(BIN_LDFLAGS)$(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ +endif +bin/libopenmpt_example_c_mem$(EXESUFFIX): examples/libopenmpt_example_c_mem.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_mem.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_mem.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ +endif +bin/libopenmpt_example_c_unsafe$(EXESUFFIX): examples/libopenmpt_example_c_unsafe.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_unsafe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIO) examples/libopenmpt_example_c_unsafe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIO) -o $@ +endif +bin/libopenmpt_example_c_pipe$(EXESUFFIX): examples/libopenmpt_example_c_pipe.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_pipe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_pipe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +endif +bin/libopenmpt_example_c_stdout$(EXESUFFIX): examples/libopenmpt_example_c_stdout.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_stdout.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_stdout.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +endif +bin/libopenmpt_example_c_probe$(EXESUFFIX): examples/libopenmpt_example_c_probe.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_probe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) examples/libopenmpt_example_c_probe.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) -o $@ +endif +bin/libopenmpt_example_cxx$(EXESUFFIX): examples/libopenmpt_example_cxx.o $(OBJECTS_LIBOPENMPT) $(OUTPUT_LIBOPENMPT) + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIOCPP) examples/libopenmpt_example_cxx.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIOCPP) -o $@ +ifeq ($(HOST),unix) + $(SILENT)mv $@ $@.norpath + $(INFO) [LD] $@ + $(SILENT)$(LINK.cc) $(BIN_LDFLAGS) $(LDFLAGS_RPATH) $(LDFLAGS_LIBOPENMPT) $(LDFLAGS_PORTAUDIOCPP) examples/libopenmpt_example_cxx.o $(OBJECTS_LIBOPENMPT) $(LOADLIBES) $(LDLIBS) $(LDLIBS_LIBOPENMPT) $(LDLIBS_PORTAUDIOCPP) -o $@ +endif + +.PHONY: cppcheck-libopenmpt +cppcheck-libopenmpt: + $(INFO) [CPPCHECK] libopenmpt + $(SILENT)$(CPPCHECK) -DCPPCHECK -DMPT_CPPCHECK_CUSTOM $(CPPCHECK_FLAGS) $(CPPCHECK_PLATFORM) --check-config --suppress=unmatchedSuppression $(LIBOPENMPT_CXX_SOURCES) $(LIBOPENMPT_C_SOURCES) + $(SILENT)$(CPPCHECK) -DCPPCHECK -DMPT_CPPCHECK_CUSTOM $(CPPCHECK_FLAGS) $(CPPCHECK_PLATFORM) $(LIBOPENMPT_CXX_SOURCES) $(LIBOPENMPT_C_SOURCES) + +.PHONY: cppcheck-libopenmpt-test +cppcheck-libopenmpt-test: + $(INFO) [CPPCHECK] libopenmpt-test + $(SILENT)$(CPPCHECK) -DCPPCHECK -DMPT_CPPCHECK_CUSTOM $(CPPCHECK_FLAGS) $(CPPCHECK_PLATFORM) -DLIBOPENMPT_BUILD_TEST --check-config --suppress=unmatchedSuppression $(LIBOPENMPTTEST_CXX_SOURCES) $(LIBOPENMPTTEST_C_SOURCES) + $(SILENT)$(CPPCHECK) -DCPPCHECK -DMPT_CPPCHECK_CUSTOM $(CPPCHECK_FLAGS) $(CPPCHECK_PLATFORM) -DLIBOPENMPT_BUILD_TEST $(LIBOPENMPTTEST_CXX_SOURCES) $(LIBOPENMPTTEST_C_SOURCES) + +.PHONY: cppcheck-openmpt123 +cppcheck-openmpt123: + $(INFO) [CPPCHECK] openmpt123 + $(SILENT)$(CPPCHECK) -DCPPCHECK -DMPT_CPPCHECK_CUSTOM $(CPPCHECK_FLAGS) $(CPPCHECK_PLATFORM) --check-config --suppress=unmatchedSuppression $(OPENMPT123_CXX_SOURCES) + $(SILENT)$(CPPCHECK) -DCPPCHECK -DMPT_CPPCHECK_CUSTOM $(CPPCHECK_FLAGS) $(CPPCHECK_PLATFORM) $(OPENMPT123_CXX_SOURCES) + +.PHONY: cppcheck +cppcheck: cppcheck-libopenmpt cppcheck-libopenmpt-test cppcheck-openmpt123 + +.PHONY: clean +clean: + $(INFO) clean ... + $(SILENT)$(RM) $(call FIXPATH,$(OUTPUTS) $(ALL_OBJECTS) $(ALL_DEPENDS) $(MISC_OUTPUTS)) + $(SILENT)$(RMTREE) $(call FIXPATH,$(MISC_OUTPUTDIRS)) + +.PHONY: clean-dist +clean-dist: + $(INFO) clean-dist ... + $(SILENT)$(RM) $(call FIXPATH,$(DIST_OUTPUTS)) + $(SILENT)$(RMTREE) $(call FIXPATH,$(DIST_OUTPUTDIRS)) + +.PHONY: distversion +distversion: + $(SILENT)echo "$(DIST_LIBOPENMPT_VERSION)" + +.PHONY: distversion-pure +distversion-pure: + $(SILENT)echo "$(DIST_LIBOPENMPT_VERSION_PURE)" + +.PHONY: distversion-tarball +distversion-tarball: + $(SILENT)echo "$(DIST_LIBOPENMPT_TARBALL_VERSION)" diff --git a/Frameworks/OpenMPT.old/OpenMPT/README.md b/Frameworks/OpenMPT.old/OpenMPT/README.md new file mode 100644 index 000000000..1d9de2b90 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/README.md @@ -0,0 +1,260 @@ + +README +====== + + +OpenMPT and libopenmpt +====================== + +This repository contains OpenMPT, a free Windows/Wine-based +[tracker](https://en.wikipedia.org/wiki/Music_tracker) and libopenmpt, +a library to render tracker music (MOD, XM, S3M, IT MPTM and dozens of other +legacy formats) to a PCM audio stream. libopenmpt is directly based on OpenMPT, +offering the same playback quality and format support, and development of the +two happens in parallel. + + +License +------- + +The OpenMPT/libopenmpt project is distributed under the *BSD-3-Clause* License. +See [LICENSE](LICENSE) for the full license text. + +Files below the `include/` (external projects) and `contrib/` (related assets +not directly considered integral part of the project) folders may be subject to +other licenses. See the respective subfolders for license information. These +folders are not distributed in all source packages, and in particular they are +not distributed in the Autotools packages. + + +How to compile +-------------- + + +### OpenMPT + + - Supported Visual Studio versions: + + - Visual Studio 2017 and 2019 Community/Professional/Enterprise + + To compile the project, open `build/vsVERSIONwin7/OpenMPT.sln` (VERSION + being 2017 or 2019) and hit the compile button. Other target systems can + be found in the `vs2017*` and `vs2019*` sibling folders. + + - OpenMPT requires the compile host system to be 64bit x86-64. + + - The Windows 8.1 SDK is required to build OpenMPT with Visual Studio 2017 + (this is included with Visual Studio, however may need to be selected + explicitly during setup). + + - Microsoft Foundation Classes (MFC) are required to build OpenMPT. + + +### libopenmpt and openmpt123 + +For detailed requirements, see `libopenmpt/dox/quickstart.md`. + + - Autotools + + Grab a `libopenmpt-VERSION-autotools.tar.gz` tarball. + + ./configure + make + make check + sudo make install + + Cross-compilation is generally supported (although only tested for + targetting MinGW-w64). + + Note that some MinGW-w64 distributions come with the `win32` threading model + enabled by default instead of the `posix` threading model. The `win32` + threading model lacks proper support for C++11 `` and `` as + well as thread-safe magic statics. It is recommended to use the `posix` + threading model for libopenmpt for this reason. On Debian, the appropriate + configure command is + `./configure --host=x86_64-w64-mingw32 CC=x86_64-w64-mingw32-gcc-posix CXX=x86_64-w64-mingw32-g++-posix` + for 64bit, or + `./configure --host=i686-w64-mingw32 CC=i686-w64-mingw32-gcc-posix CXX=i686-w64-mingw32-g++-posix` + for 32bit. Other MinGW-w64 distributions may differ. + + - Visual Studio: + + - You will find solutions for Visual Studio 2017 and 2019 in the + corresponding `build/vsVERSIONwin7/` folder. + Projects that target Windows 10 Desktop (including ARM and ARM64) are + available in `build/vsVERSIONwin10/` (Visual Studio 2017 requires SDK + version 10.0.16299). + Minimal projects that target Windows 10 UWP are available in + `build/vsVERSIONuwp/`. + Most projects are supported with any of the mentioned Visual Studio + verions, with the following exceptions: + + - in_openmpt: Requires Visual Studio with MFC. + + - xmp-openmpt: Requires Visual Studio with MFC. + + - libopenmpt requires the compile host system to be 64bit x86-64 when + building with Visual Studio. + + - You will need the Winamp 5 SDK and the XMPlay SDK if you want to + compile the plugins for these 2 players. They can be downloaded + automatically on Windows 7 or later by just running the + `build/download_externals.cmd` script. + + If you do not want to or cannot use this script, you may follow these + manual steps instead: + + - Winamp 5 SDK: + + To build libopenmpt as a winamp input plugin, copy the contents of + `WA5.55_SDK.exe` to include/winamp/. + + Please visit + [winamp.com](http://wiki.winamp.com/wiki/Plug-in_Developer) to + download the SDK. + You can disable in_openmpt in the solution configuration. + + - XMPlay SDK: + + To build libopenmpt with XMPlay input plugin support, copy the + contents of xmp-sdk.zip into include/xmplay/. + + Please visit [un4seen.com](https://www.un4seen.com/xmplay.html) to + download the SDK. + You can disable xmp-openmpt in the solution configuration. + + - Makefile + + The makefile supports different build environments and targets via the + `CONFIG=` parameter directly to the make invocation. + Use `make CONFIG=$newconfig clean` when switching between different configs + because the makefile cleans only intermediates and target that are active + for the current config and no configuration state is kept around across + invocations. + + - mingw-w64: + + The required compiler version is at least GCC 7. + + make CONFIG=mingw64-win32 # for win32 + + make CONFIG=mingw64-win64 # for win64 + + - gcc or clang (on Unix-like systems, including Mac OS X with MacPorts, + and Haiku (32-bit Hybrid and 64-bit)): + + The minimum required compiler versions are: + + - gcc 7 + + - clang 5 + + The Makefile requires pkg-config for native builds. + For sound output in openmpt123, PortAudio or SDL is required. + openmpt123 can optionally use libflac and libsndfile to render PCM + files to disk. + + When using gcc, run: + + make CONFIG=gcc + + When using clang, it is recommended to do: + + make CONFIG=clang + + Otherwise, simply run + + make + + which will try to guess the compiler based on your operating system. + + - emscripten (on Unix-like systems): + + Run: + + # generates WebAssembly with JavaScript fallback + make CONFIG=emscripten EMSCRIPTEN_TARGET=all + + or + + # generates WebAssembly + make CONFIG=emscripten EMSCRIPTEN_TARGET=wasm + + or + + # generates JavaScript with compatibility for older VMs + make CONFIG=emscripten EMSCRIPTEN_TARGET=js + + Running the test suite on the command line is also supported by using + node.js. Depending on how your distribution calls the `node.js` binary, + you might have to edit `build/make/config-emscripten.mk`. + + - DJGPP / DOS + + Cross-compilation from Linux systems is supported with DJGPP GCC 7.2 or + later via + + make CONFIG=djgpp + + `openmpt123` can use liballegro 4.2 for sound output on DJGPP/DOS. + liballegro can either be installed system-wide in the DJGPP environment + or downloaded into the `libopenmpt` source tree. + + make CONFIG=djgpp USE_ALLEGRO42=1 # use installed liballegro + + or + + ./build/download_externals.sh # download liballegro binaries + make CONFIG=djgpp USE_ALLEGRO42=1 BUNDLED_ALLEGRO42=1 + + - American Fuzzy Lop: + + To compile libopenmpt with fuzzing instrumentation for afl-fuzz, run: + + make CONFIG=afl + + For more detailed instructions, read `contrib/fuzzing/readme.md`. + + - other compilers: + + To compile libopenmpt with other C++17 compliant compilers, run: + + make CONFIG=generic + + + The `Makefile` supports some customizations. You might want to read the top + which should get you some possible make settings, like e.g. + `make DYNLINK=0` or similar. Cross compiling or different compiler would + best be implemented via new `config-*.mk` files. + + The `Makefile` also supports building doxygen documentation by using + + make doc + + Binaries and documentation can be installed systen-wide with + + make PREFIX=/yourprefix install + make PREFIX=/yourprefix install-doc + + Some systems (i.e. Linux) require running + + sudo ldconfig + + in order for the system linker to be able to pick up newly installed + libraries. + + `PREFIX` defaults to `/usr/local`. A `DESTDIR=` parameter is also + supported. + + - Android NDK + + See `build/android_ndk/README.AndroidNDK.txt`. + + + +Contributing to OpenMPT/libopenmpt +---------------------------------- + + +See [contributing](doc/contributing.md). + diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/BuildSettings.h b/Frameworks/OpenMPT.old/OpenMPT/common/BuildSettings.h new file mode 100644 index 000000000..68a9b22a8 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/BuildSettings.h @@ -0,0 +1,744 @@ +/* + * BuildSettings.h + * --------------- + * Purpose: Global, user settable compile time flags (and some global system header configuration) + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + + + +#include "CompilerDetect.h" + + + +// set windows version early so that we can deduce dependencies from SDK version + +#if MPT_OS_WINDOWS + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0601 // _WIN32_WINNT_WIN7 +#endif + +#ifndef WINVER +#define WINVER _WIN32_WINNT +#endif + +#endif // MPT_OS_WINDOWS + + + +#if defined(MODPLUG_TRACKER) && defined(LIBOPENMPT_BUILD) +#error "either MODPLUG_TRACKER or LIBOPENMPT_BUILD has to be defined" +#elif defined(MODPLUG_TRACKER) +// nothing +#elif defined(LIBOPENMPT_BUILD) +// nothing +#else +#error "either MODPLUG_TRACKER or LIBOPENMPT_BUILD has to be defined" +#endif // MODPLUG_TRACKER || LIBOPENMPT_BUILD + + + +// wrapper for autoconf macros + +#if defined(HAVE_CONFIG_H) + +#include "config.h" + +// Fixup dependencies which are currently not used in libopenmpt itself +#ifdef MPT_WITH_FLAC +#undef MPT_WITH_FLAC +#endif + +#endif // HAVE_CONFIG_H + + + +// Dependencies from the MSVC build system +#if defined(MPT_BUILD_MSVC) + +// This section defines which dependencies are available when building with +// MSVC. Other build systems provide MPT_WITH_* macros via command-line or other +// means. +// OpenMPT and libopenmpt should compile and run successfully (albeit with +// reduced functionality) with any or all dependencies missing/disabled. +// The defaults match the bundled third-party libraries with the addition of +// ASIO and VST SDKs. + +#if defined(MODPLUG_TRACKER) + +#if MPT_OS_WINDOWS +#if !defined(MPT_BUILD_WINESUPPORT) +#define MPT_WITH_MFC +#endif // !MPT_BUILD_WINESUPPORT +#endif // MPT_OS_WINDOWS + +// OpenMPT-only dependencies +#if !MPT_MSVC_BEFORE(2019,0) +// disabled for VS2017 because of multiple initialization of inline variables +// https://developercommunity.visualstudio.com/t/static-inline-variable-gets-destroyed-multiple-tim/297876 +#define MPT_WITH_ASIO +#endif +#define MPT_WITH_DMO +#define MPT_WITH_LAME +#define MPT_WITH_LHASA +#define MPT_WITH_MINIZIP +#define MPT_WITH_NLOHMANNJSON +#define MPT_WITH_OPUS +#define MPT_WITH_OPUSENC +#define MPT_WITH_OPUSFILE +#define MPT_WITH_PORTAUDIO +//#define MPT_WITH_PULSEAUDIO +//#define MPT_WITH_PULSEAUDIOSIMPLE +#define MPT_WITH_RTAUDIO +#define MPT_WITH_SMBPITCHSHIFT +#define MPT_WITH_UNRAR +#define MPT_WITH_VORBISENC + +// OpenMPT and libopenmpt dependencies (not for openmp123, player plugins or examples) +//#define MPT_WITH_DL +#define MPT_WITH_FLAC +//#define MPT_WITH_LTDL +#if MPT_OS_WINDOWS +#define MPT_WITH_MEDIAFOUNDATION +#endif +//#define MPT_WITH_MINIMP3 +//#define MPT_WITH_MINIZ +#define MPT_WITH_MPG123 +#define MPT_WITH_OGG +//#define MPT_WITH_STBVORBIS +#define MPT_WITH_VORBIS +#define MPT_WITH_VORBISFILE +#if MPT_OS_WINDOWS +#if (_WIN32_WINNT >= 0x0A00) +#define MPT_WITH_WINDOWS10 +#endif +#endif +#define MPT_WITH_ZLIB + +#endif // MODPLUG_TRACKER + +#if defined(LIBOPENMPT_BUILD) + +// OpenMPT and libopenmpt dependencies (not for openmp123, player plugins or examples) +#if defined(LIBOPENMPT_BUILD_FULL) && defined(LIBOPENMPT_BUILD_SMALL) +#error "only one of LIBOPENMPT_BUILD_FULL or LIBOPENMPT_BUILD_SMALL can be defined" +#endif // LIBOPENMPT_BUILD_FULL && LIBOPENMPT_BUILD_SMALL + +#if defined(LIBOPENMPT_BUILD_SMALL) + +//#define MPT_WITH_DL +//#define MPT_WITH_FLAC +//#define MPT_WITH_LTDL +//#define MPT_WITH_MEDIAFOUNDATION +#define MPT_WITH_MINIMP3 +#define MPT_WITH_MINIZ +//#define MPT_WITH_MPG123 +//#define MPT_WITH_OGG +#define MPT_WITH_STBVORBIS +//#define MPT_WITH_VORBIS +//#define MPT_WITH_VORBISFILE +//#define MPT_WITH_ZLIB + +#else // !LIBOPENMPT_BUILD_SMALL + +//#define MPT_WITH_DL +//#define MPT_WITH_FLAC +//#define MPT_WITH_LTDL +//#define MPT_WITH_MEDIAFOUNDATION +//#define MPT_WITH_MINIMP3 +//#define MPT_WITH_MINIZ +#define MPT_WITH_MPG123 +#define MPT_WITH_OGG +//#define MPT_WITH_STBVORBIS +#define MPT_WITH_VORBIS +#define MPT_WITH_VORBISFILE +#define MPT_WITH_ZLIB + +#endif // LIBOPENMPT_BUILD_SMALL + +#endif // LIBOPENMPT_BUILD + +#endif // MPT_BUILD_MSVC + + +#if defined(MPT_BUILD_XCODE) + +#if defined(MODPLUG_TRACKER) + +// n/a + +#endif // MODPLUG_TRACKER + +#if defined(LIBOPENMPT_BUILD) + +//#define MPT_WITH_DL +//#define MPT_WITH_FLAC +//#define MPT_WITH_LTDL +//#define MPT_WITH_MEDIAFOUNDATION +//#define MPT_WITH_MINIMP3 +//#define MPT_WITH_MINIZ +//#define MPT_WITH_MPG123 +//#define MPT_WITH_OGG +//#define MPT_WITH_STBVORBIS +//#define MPT_WITH_VORBIS +//#define MPT_WITH_VORBISFILE +//#define MPT_WITH_ZLIB + +#endif // LIBOPENMPT_BUILD + +#endif // MPT_BUILD_XCODE + + + +#if defined(MODPLUG_TRACKER) + +// Enable built-in test suite. +#if defined(MPT_BUILD_DEBUG) || defined(MPT_BUILD_CHECKED) +#define ENABLE_TESTS +#endif + +// Disable any file saving functionality (not really useful except for the player library) +//#define MODPLUG_NO_FILESAVE + +// Disable any debug logging +//#define NO_LOGGING +#if !defined(MPT_BUILD_DEBUG) && !defined(MPT_BUILD_CHECKED) && !defined(MPT_BUILD_WINESUPPORT) +#define MPT_LOG_GLOBAL_LEVEL_STATIC +#define MPT_LOG_GLOBAL_LEVEL 0 +#endif + +// Enable all individual logging macros and MPT_LOG calls +//#define MPT_ALL_LOGGING + +// Disable all runtime asserts +#if !defined(MPT_BUILD_DEBUG) && !defined(MPT_BUILD_CHECKED) && !defined(MPT_BUILD_WINESUPPORT) +#define NO_ASSERTS +#endif + +// Enable callback stream wrapper for FileReader (required by libopenmpt C API). +//#define MPT_FILEREADER_CALLBACK_STREAM + +// Support for externally linked samples e.g. in MPTM files +#define MPT_EXTERNAL_SAMPLES + +// Support mpt::ChartsetLocale +#define MPT_ENABLE_CHARSET_LOCALE + +// Use inline assembly +#define ENABLE_ASM + +// Disable unarchiving support +//#define NO_ARCHIVE_SUPPORT + +// Disable the built-in reverb effect +//#define NO_REVERB + +// Disable built-in miscellaneous DSP effects (surround, mega bass, noise reduction) +//#define NO_DSP + +// Disable the built-in equalizer. +//#define NO_EQ + +// Disable the built-in automatic gain control +//#define NO_AGC + +// Define to build without VST plugin support; makes build possible without VST SDK. +//#define NO_VST + +// (HACK) Define to build without any plugin support +//#define NO_PLUGINS + +// Do not build libopenmpt C api +#define NO_LIBOPENMPT_C + +// Do not build libopenmpt C++ api +#define NO_LIBOPENMPT_CXX + +#endif // MODPLUG_TRACKER + + + +#if defined(LIBOPENMPT_BUILD) + +#if (defined(_DEBUG) || defined(DEBUG)) && !defined(MPT_BUILD_DEBUG) +#define MPT_BUILD_DEBUG +#endif + +#if defined(LIBOPENMPT_BUILD_TEST) +#define ENABLE_TESTS +#else +#define MODPLUG_NO_FILESAVE +#endif +#if defined(MPT_BUILD_ANALZYED) || defined(MPT_BUILD_DEBUG) || defined(MPT_BUILD_CHECKED) || defined(ENABLE_TESTS) +// enable asserts +#else +#define NO_ASSERTS +#endif +//#define NO_LOGGING +//#define MPT_ALL_LOGGING +#define MPT_FILEREADER_CALLBACK_STREAM +//#define MPT_EXTERNAL_SAMPLES +#if defined(ENABLE_TESTS) || defined(MPT_BUILD_HACK_ARCHIVE_SUPPORT) +#define MPT_ENABLE_CHARSET_LOCALE +#else +//#define MPT_ENABLE_CHARSET_LOCALE +#endif +// Do not use inline asm in library builds. There is just about no codepath which would use it anyway. +//#define ENABLE_ASM +#if defined(MPT_BUILD_HACK_ARCHIVE_SUPPORT) +//#define NO_ARCHIVE_SUPPORT +#else +#define NO_ARCHIVE_SUPPORT +#endif +//#define NO_REVERB +#define NO_DSP +#define NO_EQ +#define NO_AGC +#define NO_VST +//#define NO_PLUGINS +//#define NO_LIBOPENMPT_C +//#define NO_LIBOPENMPT_CXX + +#endif // LIBOPENMPT_BUILD + + + +#if MPT_OS_WINDOWS + + #ifndef MPT_ENABLE_CHARSET_LOCALE + #define MPT_ENABLE_CHARSET_LOCALE + #endif + +#elif MPT_OS_LINUX + +#elif MPT_OS_ANDROID + +#elif MPT_OS_EMSCRIPTEN + + #ifndef MPT_LOCALE_ASSUME_CHARSET + #define MPT_LOCALE_ASSUME_CHARSET Charset::UTF8 + #endif + +#elif MPT_OS_MACOSX_OR_IOS + +#elif MPT_OS_DJGPP + + #ifndef MPT_LOCALE_ASSUME_CHARSET + #define MPT_LOCALE_ASSUME_CHARSET Charset::CP437 + #endif + +#endif + + + +#if MPT_COMPILER_MSVC && !defined(MPT_USTRING_MODE_UTF8_FORCE) + + // Use wide strings for MSVC because this is the native encoding on + // microsoft platforms. + #define MPT_USTRING_MODE_WIDE 1 + #define MPT_USTRING_MODE_UTF8 0 + +#else // !MPT_COMPILER_MSVC + + #define MPT_USTRING_MODE_WIDE 0 + #define MPT_USTRING_MODE_UTF8 1 + +#endif // MPT_COMPILER_MSVC + +#if MPT_USTRING_MODE_UTF8 + + // MPT_USTRING_MODE_UTF8 mpt::ustring is implemented via mpt::u8string + #define MPT_ENABLE_U8STRING 1 + +#else + + #define MPT_ENABLE_U8STRING 0 + +#endif + +#if defined(MODPLUG_TRACKER) || MPT_USTRING_MODE_WIDE + + // mpt::ToWString, mpt::wfmt, ConvertStrTo + // Required by the tracker to ease interfacing with WinAPI. + // Required by MPT_USTRING_MODE_WIDE to ease type tunneling in mpt::format. + #define MPT_WSTRING_FORMAT 1 + +#else + + #define MPT_WSTRING_FORMAT 0 + +#endif + +#if MPT_OS_WINDOWS || MPT_USTRING_MODE_WIDE || MPT_WSTRING_FORMAT + + // mpt::ToWide + // Required on Windows by mpt::PathString. + // Required by MPT_USTRING_MODE_WIDE as they share the conversion functions. + // Required by MPT_WSTRING_FORMAT because of std::string<->std::wstring conversion in mpt::ToString and mpt::ToWString. + #define MPT_WSTRING_CONVERT 1 + +#else + + #define MPT_WSTRING_CONVERT 0 + +#endif + + + +// fixing stuff up + +#if defined(MPT_BUILD_ANALYZED) || defined(MPT_BUILD_CHECKED) +#ifdef NO_ASSERTS +#undef NO_ASSERTS // static or dynamic analyzers want assertions on +#endif +#endif + +#if defined(MPT_BUILD_FUZZER) +#ifndef MPT_FUZZ_TRACKER +#define MPT_FUZZ_TRACKER +#endif +#endif + +#if defined(ENABLE_ASM) +#if MPT_COMPILER_MSVC && defined(_M_IX86) + +#define ENABLE_CPUID +// Generate general x86 inline assembly and intrinsics. +#define ENABLE_X86 +// Generate MMX instructions (only used when the CPU supports it). +#define ENABLE_MMX +// Generate SSE instructions (only used when the CPU supports it). +#define ENABLE_SSE +// Generate SSE2 instructions (only used when the CPU supports it). +#define ENABLE_SSE2 +// Generate SSE3 instructions (only used when the CPU supports it). +#define ENABLE_SSE3 +// Generate SSE4 instructions (only used when the CPU supports it). +#define ENABLE_SSE4 +// Generate AVX instructions (only used when the CPU supports it). +#define ENABLE_AVX +// Generate AVX2 instructions (only used when the CPU supports it). +#define ENABLE_AVX2 + +#elif MPT_COMPILER_MSVC && defined(_M_X64) + +#define ENABLE_CPUID +// Generate general x64 intrinsics. +#define ENABLE_X64 +// Generate SSE instructions (only used when the CPU supports it). +#define ENABLE_SSE +// Generate SSE2 instructions (only used when the CPU supports it). +#define ENABLE_SSE2 +// Generate SSE3 instructions (only used when the CPU supports it). +#define ENABLE_SSE3 +// Generate SSE4 instructions (only used when the CPU supports it). +#define ENABLE_SSE4 +// Generate AVX instructions (only used when the CPU supports it). +#define ENABLE_AVX +// Generate AVX2 instructions (only used when the CPU supports it). +#define ENABLE_AVX2 + +#elif MPT_BUILD_XCODE && defined(__x86_64__) + +// No CPUID enabled, only one code path supported anyway +// #define ENABLE_CPUID +// Enable the SSE2 intrinsic functions unconditionally +#define ENABLE_SSE2 + +#elif MPT_BUILD_XCODE && defined(__arm64__) + +// No CPUID, it's kind of a pain on ARM anyway +// #define ENABLE_CPUID +// Enable the NEON intrinsic functions unconditionally +#define ENABLE_NEON + +#else + +#undef ENABLE_ASM + +#endif // arch +#endif // ENABLE_ASM + +#if defined(ENABLE_TESTS) && defined(MODPLUG_NO_FILESAVE) +#undef MODPLUG_NO_FILESAVE // tests recommend file saving +#endif + +#if defined(MPT_WITH_ZLIB) && defined(MPT_WITH_MINIZ) +// Only one deflate implementation should be used. Prefer zlib. +#undef MPT_WITH_MINIZ +#endif + +#if !MPT_OS_WINDOWS && defined(MPT_WITH_MEDIAFOUNDATION) +#undef MPT_WITH_MEDIAFOUNDATION // MediaFoundation requires Windows +#endif + +#if !MPT_COMPILER_MSVC && !MPT_COMPILER_CLANG && defined(MPT_WITH_MEDIAFOUNDATION) +#undef MPT_WITH_MEDIAFOUNDATION // MediaFoundation requires MSVC or Clang due to ATL (no MinGW support) +#endif + +#if defined(MPT_WITH_MEDIAFOUNDATION) && !defined(MPT_ENABLE_TEMPFILE) +#define MPT_ENABLE_TEMPFILE +#endif + +#if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_TEMPFILE) +#define MPT_ENABLE_TEMPFILE +#endif + +#if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_DYNBIND) +#define MPT_ENABLE_DYNBIND // Tracker requires dynamic library loading for export codecs +#endif + +#if defined(MPT_WITH_MEDIAFOUNDATION) && !defined(MPT_ENABLE_DYNBIND) +#define MPT_ENABLE_DYNBIND // MediaFoundation needs dynamic loading in order to test availability of delay loaded libs +#endif + +#if (defined(MPT_WITH_MPG123) || defined(MPT_WITH_MINIMP3)) && !defined(MPT_ENABLE_MP3_SAMPLES) +#define MPT_ENABLE_MP3_SAMPLES +#endif + +#if defined(ENABLE_TESTS) +#define MPT_ENABLE_FILEIO // Test suite requires PathString for file loading. +#endif + +#if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_FILEIO) +#define MPT_ENABLE_FILEIO // Tracker requires disk file io +#endif + +#if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_THREAD) +#define MPT_ENABLE_THREAD // Tracker requires threads +#endif + +#if defined(MPT_EXTERNAL_SAMPLES) && !defined(MPT_ENABLE_FILEIO) +#define MPT_ENABLE_FILEIO // External samples require disk file io +#endif + +#if defined(NO_PLUGINS) +// Any plugin type requires NO_PLUGINS to not be defined. +#define NO_VST +#endif + +#if defined(ENABLE_ASM) || !defined(NO_VST) +#define MPT_ENABLE_ALIGNED_ALLOC +#endif + + + +#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) && !defined(MPT_BUILD_WINESUPPORT_WRAPPER) +#ifndef MPT_NO_NAMESPACE +#define MPT_NO_NAMESPACE +#endif +#endif + +#if defined(MPT_NO_NAMESPACE) + +#ifdef OPENMPT_NAMESPACE +#undef OPENMPT_NAMESPACE +#endif +#define OPENMPT_NAMESPACE + +#ifdef OPENMPT_NAMESPACE_BEGIN +#undef OPENMPT_NAMESPACE_BEGIN +#endif +#define OPENMPT_NAMESPACE_BEGIN + +#ifdef OPENMPT_NAMESPACE_END +#undef OPENMPT_NAMESPACE_END +#endif +#define OPENMPT_NAMESPACE_END + +#else + +#ifndef OPENMPT_NAMESPACE +#define OPENMPT_NAMESPACE OpenMPT +#endif + +#ifndef OPENMPT_NAMESPACE_BEGIN +#define OPENMPT_NAMESPACE_BEGIN namespace OPENMPT_NAMESPACE { +#endif +#ifndef OPENMPT_NAMESPACE_END +#define OPENMPT_NAMESPACE_END } +#endif + +#endif + + + +// platform configuration + +#ifdef MPT_WITH_MFC +//#define MPT_MFC_FULL // use full MFC, including MFC controls +#endif // MPT_WITH_MFC + +#if defined(MODPLUG_TRACKER) +#if MPT_OS_WINDOWS +#if !defined(MPT_BUILD_WINESUPPORT) +#ifndef MPT_MFC_FULL +#define _AFX_NO_MFC_CONTROLS_IN_DIALOGS // Do not include support for MFC controls in dialogs (reduces binary bloat; remove this #define if you want to use MFC controls) +#endif // !MPT_MFC_FULL +#endif // !MPT_BUILD_WINESUPPORT +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + +#if MPT_OS_WINDOWS + +#define WIN32_LEAN_AND_MEAN + +// windows.h excludes +#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines +#define NOMINMAX // Macros min(a,b) and max(a,b) +#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. +#define NOCOMM // COMM driver routines +#define NOKANJI // Kanji support stuff. +#define NOPROFILER // Profiler interface. +#define NOMCX // Modem Configuration Extensions + +// mmsystem.h excludes +#define MMNODRV +//#define MMNOSOUND +//#define MMNOWAVE +//#define MMNOMIDI +#define MMNOAUX +#define MMNOMIXER +//#define MMNOTIMER +#define MMNOJOY +#define MMNOMCI +//#define MMNOMMIO +//#define MMNOMMSYSTEM + +// mmreg.h excludes +#define NOMMIDS +//#define NONEWWAVE +#define NONEWRIFF +#define NOJPEGDIB +#define NONEWIC +#define NOBITMAP + +#endif // MPT_OS_WINDOWS + + + +// stdlib configuration + +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif + +#define __STDC_CONSTANT_MACROS +#define __STDC_LIMIT_MACROS + +#define _USE_MATH_DEFINES + +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif + +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif + + + +// compiler configuration + +#if MPT_COMPILER_MSVC + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS // Define to disable the "This function or variable may be unsafe" warnings. +#endif +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT 1 +#ifndef _SCL_SECURE_NO_WARNINGS +#define _SCL_SECURE_NO_WARNINGS +#endif + +#ifndef NO_WARN_MBCS_MFC_DEPRECATION +#define NO_WARN_MBCS_MFC_DEPRECATION +#endif + +#pragma warning(disable:4355) // 'this' : used in base member initializer list + +// happens for immutable classes (i.e. classes containing const members) +#pragma warning(disable:4512) // assignment operator could not be generated + +#pragma warning(error:4309) // Treat "truncation of constant value"-warning as error. +#pragma warning(error:4463) // Treat overflow; assigning value to bit-field that can only hold values from low_value to high_value"-warning as error. + +#ifdef MPT_BUILD_ANALYZED +// Disable Visual Studio static analyzer warnings that generate too many false positives in VS2010. +//#pragma warning(disable:6246) +//#pragma warning(disable:6262) +#pragma warning(disable:6297) // 32-bit value is shifted, then cast to 64-bit value. Results might not be an expected value. +#pragma warning(disable:6326) // Potential comparison of a constant with another constant +//#pragma warning(disable:6385) +//#pragma warning(disable:6386) +#endif // MPT_BUILD_ANALYZED + +#endif // MPT_COMPILER_MSVC + +#if MPT_COMPILER_CLANG + +#if defined(MPT_BUILD_MSVC) +#pragma clang diagnostic warning "-Wimplicit-fallthrough" +#endif // MPT_BUILD_MSVC + +#if defined(MODPLUG_TRACKER) +#pragma clang diagnostic ignored "-Wunused-local-typedef" +#endif // MODPLUG_TRACKER + +#endif // MPT_COMPILER_CLANG + + + + + +// standard library quirks + + + + + +// third-party library configuration + +#ifdef MPT_WITH_FLAC +#ifdef MPT_BUILD_MSVC_STATIC +#define FLAC__NO_DLL +#endif +#endif + +#ifdef MPT_WITH_SMBPITCHSHIFT +#ifdef MPT_BUILD_MSVC_SHARED +#define SMBPITCHSHIFT_USE_DLL +#endif +#endif + +#ifdef MPT_WITH_STBVORBIS +#define STB_VORBIS_HEADER_ONLY +#ifndef STB_VORBIS_NO_PULLDATA_API +#define STB_VORBIS_NO_PULLDATA_API +#endif +#ifndef STB_VORBIS_NO_STDIO +#define STB_VORBIS_NO_STDIO +#endif +#endif + +#ifdef MPT_WITH_VORBISFILE +#ifndef OV_EXCLUDE_STATIC_CALLBACKS +#define OV_EXCLUDE_STATIC_CALLBACKS +#endif +#endif + +#ifdef MPT_WITH_ZLIB +#ifdef MPT_BUILD_MSVC_SHARED +#define ZLIB_DLL +#endif +#endif + diff --git a/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h b/Frameworks/OpenMPT.old/OpenMPT/common/CompilerDetect.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h rename to Frameworks/OpenMPT.old/OpenMPT/common/CompilerDetect.h diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/ComponentManager.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/ComponentManager.cpp new file mode 100644 index 000000000..ace006272 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/ComponentManager.cpp @@ -0,0 +1,469 @@ +/* + * ComponentManager.cpp + * -------------------- + * Purpose: Manages loading of optional components. + * Notes : (currently none) + * Authors: Joern Heusipp + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "ComponentManager.h" + +#include "Logging.h" + +#include "mptMutex.h" + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MPT_ENABLE_COMPONENTS) + + +ComponentBase::ComponentBase(ComponentType type) + : m_Type(type) + , m_Initialized(false) + , m_Available(false) +{ + return; +} + + +ComponentBase::~ComponentBase() +{ + return; +} + + +void ComponentBase::SetInitialized() +{ + m_Initialized = true; +} + + +void ComponentBase::SetAvailable() +{ + m_Available = true; +} + + +ComponentType ComponentBase::GetType() const +{ + return m_Type; +} + + +bool ComponentBase::IsInitialized() const +{ + return m_Initialized; +} + + +bool ComponentBase::IsAvailable() const +{ + return m_Initialized && m_Available; +} + + +mpt::ustring ComponentBase::GetVersion() const +{ + return mpt::ustring(); +} + + +void ComponentBase::Initialize() +{ + if(IsInitialized()) + { + return; + } + if(DoInitialize()) + { + SetAvailable(); + } + SetInitialized(); +} + + +#if defined(MPT_ENABLE_DYNBIND) + + +ComponentLibrary::ComponentLibrary(ComponentType type) + : ComponentBase(type) + , m_BindFailed(false) +{ + return; +} + + +ComponentLibrary::~ComponentLibrary() +{ + return; +} + + +bool ComponentLibrary::AddLibrary(const std::string &libName, const mpt::LibraryPath &libPath) +{ + if(m_Libraries[libName].IsValid()) + { + // prefer previous + return true; + } + mpt::Library lib(libPath); + if(!lib.IsValid()) + { + return false; + } + m_Libraries[libName] = lib; + return true; +} + + +void ComponentLibrary::ClearLibraries() +{ + m_Libraries.clear(); +} + + +void ComponentLibrary::SetBindFailed() +{ + m_BindFailed = true; +} + + +void ComponentLibrary::ClearBindFailed() +{ + m_BindFailed = false; +} + + +bool ComponentLibrary::HasBindFailed() const +{ + return m_BindFailed; +} + + +mpt::Library ComponentLibrary::GetLibrary(const std::string &libName) const +{ + const auto it = m_Libraries.find(libName); + if(it == m_Libraries.end()) + { + return mpt::Library(); + } + return it->second; +} + + +#endif // MPT_ENABLE_DYNBIND + + +#if MPT_COMPONENT_MANAGER + + +ComponentFactoryBase::ComponentFactoryBase(const std::string &id, const std::string &settingsKey) + : m_ID(id) + , m_SettingsKey(settingsKey) +{ + return; +} + + +ComponentFactoryBase::~ComponentFactoryBase() +{ + return; +} + + +std::string ComponentFactoryBase::GetID() const +{ + return m_ID; +} + + +std::string ComponentFactoryBase::GetSettingsKey() const +{ + return m_SettingsKey; +} + + +void ComponentFactoryBase::PreConstruct() const +{ + MPT_LOG(LogInformation, "Components", + mpt::format(U_("Constructing Component %1")) + ( mpt::ToUnicode(mpt::Charset::ASCII, m_ID) + ) + ); +} + + +void ComponentFactoryBase::Initialize(ComponentManager &componentManager, std::shared_ptr component) const +{ + if(componentManager.IsComponentBlocked(GetSettingsKey())) + { + return; + } + componentManager.InitializeComponent(component); +} + + +// Global list of component register functions. +// We do not use a global scope static list head because the corresponding +// mutex would be no POD type and would thus not be safe to be usable in +// zero-initialized state. +// Function scope static initialization is guaranteed to be thread safe +// in C++11. +// We use this implementation to be future-proof. +// MSVC currently does not exploit the possibility of using multiple threads +// for global lifetime object's initialization. +// An implementation with a simple global list head and no mutex at all would +// thus work fine for MSVC (currently). + +static mpt::mutex & ComponentListMutex() +{ + static mpt::mutex g_ComponentListMutex; + return g_ComponentListMutex; +} + +static ComponentListEntry * & ComponentListHead() +{ + static ComponentListEntry *g_ComponentListHead = nullptr; + return g_ComponentListHead; +} + +bool ComponentListPush(ComponentListEntry *entry) +{ + mpt::lock_guard guard(ComponentListMutex()); + entry->next = ComponentListHead(); + ComponentListHead() = entry; + return true; +} + + +static std::shared_ptr g_ComponentManager; + + +void ComponentManager::Init(const IComponentManagerSettings &settings) +{ + MPT_LOG(LogInformation, "Components", U_("Init")); + // cannot use make_shared because the constructor is private + g_ComponentManager = std::shared_ptr(new ComponentManager(settings)); +} + + +void ComponentManager::Release() +{ + MPT_LOG(LogInformation, "Components", U_("Release")); + g_ComponentManager = nullptr; +} + + +std::shared_ptr ComponentManager::Instance() +{ + return g_ComponentManager; +} + + +ComponentManager::ComponentManager(const IComponentManagerSettings &settings) + : m_Settings(settings) +{ + mpt::lock_guard guard(ComponentListMutex()); + for(ComponentListEntry *entry = ComponentListHead(); entry; entry = entry->next) + { + entry->reg(*this); + } +} + + +void ComponentManager::Register(const IComponentFactory &componentFactory) +{ + if(m_Components.find(componentFactory.GetID()) != m_Components.end()) + { + return; + } + RegisteredComponent registeredComponent; + registeredComponent.settingsKey = componentFactory.GetSettingsKey(); + registeredComponent.factoryMethod = componentFactory.GetStaticConstructor(); + registeredComponent.instance = nullptr; + registeredComponent.weakInstance = std::weak_ptr(); + m_Components.insert(std::make_pair(componentFactory.GetID(), registeredComponent)); +} + + +void ComponentManager::Startup() +{ + MPT_LOG(LogDebug, "Components", U_("Startup")); + if(m_Settings.LoadOnStartup()) + { + for(auto &it : m_Components) + { + it.second.instance = it.second.factoryMethod(*this); + it.second.weakInstance = it.second.instance; + } + } + if(!m_Settings.KeepLoaded()) + { + for(auto &it : m_Components) + { + it.second.instance = nullptr; + } + } +} + + +bool ComponentManager::IsComponentBlocked(const std::string &settingsKey) const +{ + if(settingsKey.empty()) + { + return false; + } + return m_Settings.IsBlocked(settingsKey); +} + + +void ComponentManager::InitializeComponent(std::shared_ptr component) const +{ + if(!component) + { + return; + } + if(component->IsInitialized()) + { + return; + } + component->Initialize(); +} + + +std::shared_ptr ComponentManager::GetComponent(const IComponentFactory &componentFactory) +{ + std::shared_ptr component = nullptr; + auto it = m_Components.find(componentFactory.GetID()); + if(it != m_Components.end()) + { // registered component + if((*it).second.instance) + { // loaded + component = (*it).second.instance; + } else + { // not loaded + component = (*it).second.weakInstance.lock(); + if(!component) + { + component = (*it).second.factoryMethod(*this); + } + if(m_Settings.KeepLoaded()) + { // keep the component loaded + (*it).second.instance = component; + } + (*it).second.weakInstance = component; + } + } else + { // unregistered component + component = componentFactory.Construct(*this); + } + MPT_ASSERT(component); + return component; +} + + +std::shared_ptr ComponentManager::ReloadComponent(const IComponentFactory &componentFactory) +{ + std::shared_ptr component = nullptr; + auto it = m_Components.find(componentFactory.GetID()); + if(it != m_Components.end()) + { // registered component + if((*it).second.instance) + { // loaded + (*it).second.instance = nullptr; + if(!(*it).second.weakInstance.expired()) + { + throw std::runtime_error("Component not completely unloaded. Cannot reload."); + } + (*it).second.weakInstance = std::weak_ptr(); + } + // not loaded + component = (*it).second.factoryMethod(*this); + if(m_Settings.KeepLoaded()) + { // keep the component loaded + (*it).second.instance = component; + } + (*it).second.weakInstance = component; + } else + { // unregistered component + component = componentFactory.Construct(*this); + } + MPT_ASSERT(component); + return component; +} + + +std::vector ComponentManager::GetRegisteredComponents() const +{ + std::vector result; + result.reserve(m_Components.size()); + for(const auto &it : m_Components) + { + result.push_back(it.first); + } + return result; +} + + +ComponentInfo ComponentManager::GetComponentInfo(std::string name) const +{ + ComponentInfo result; + result.name = name; + result.state = ComponentStateUnregistered; + result.settingsKey = ""; + result.type = ComponentTypeUnknown; + const auto it = m_Components.find(name); + if(it == m_Components.end()) + { + result.state = ComponentStateUnregistered; + return result; + } + result.settingsKey = it->second.settingsKey; + if(IsComponentBlocked(it->second.settingsKey)) + { + result.state = ComponentStateBlocked; + return result; + } + std::shared_ptr component = it->second.instance; + if(!component) + { + component = it->second.weakInstance.lock(); + } + if(!component) + { + result.state = ComponentStateUnintialized; + return result; + } + result.type = component->GetType(); + if(!component->IsInitialized()) + { + result.state = ComponentStateUnintialized; + return result; + } + if(!component->IsAvailable()) + { + result.state = ComponentStateUnavailable; + return result; + } + result.state = ComponentStateAvailable; + return result; +} + + +mpt::PathString ComponentManager::GetComponentPath() const +{ + return m_Settings.Path(); +} + + +#endif // MPT_COMPONENT_MANAGER + + +#endif // MPT_ENABLE_COMPONENTS + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/ComponentManager.h b/Frameworks/OpenMPT.old/OpenMPT/common/ComponentManager.h new file mode 100644 index 000000000..1fb0307e7 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/ComponentManager.h @@ -0,0 +1,519 @@ +/* + * ComponentManager.h + * ------------------ + * Purpose: Manages loading of optional components. + * Notes : (currently none) + * Authors: Joern Heusipp + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "BuildSettings.h" + +#include +#include +#include "../common/misc_util.h" +#include "../common/mptMutex.h" + + +OPENMPT_NAMESPACE_BEGIN + + +#define MPT_ENABLE_COMPONENTS + + +#if defined(MPT_ENABLE_COMPONENTS) + + +#if defined(MODPLUG_TRACKER) +#define MPT_COMPONENT_MANAGER 1 +#else +#define MPT_COMPONENT_MANAGER 0 +#endif + + +enum ComponentType +{ + ComponentTypeUnknown = 0, + ComponentTypeBuiltin, // PortAudio + ComponentTypeSystem, // mf.dll + ComponentTypeSystemInstallable, // acm mp3 codec + ComponentTypeBundled, // libsoundtouch + ComponentTypeForeign, // libmp3lame +}; + + +class ComponentFactoryBase; + + +class IComponent +{ + + friend class ComponentFactoryBase; + +protected: + + IComponent() = default; + +public: + + virtual ~IComponent() = default; + +public: + + virtual ComponentType GetType() const = 0; + + virtual bool IsInitialized() const = 0; // Initialize() has been called + virtual bool IsAvailable() const = 0; // Initialize() has been successfull + virtual mpt::ustring GetVersion() const = 0; + + virtual void Initialize() = 0; // try to load the component + +}; + + +class ComponentBase + : public IComponent +{ + +private: + + ComponentType m_Type; + + bool m_Initialized; + bool m_Available; + +protected: + + ComponentBase(ComponentType type); + +public: + + virtual ~ComponentBase(); + +protected: + + void SetInitialized(); + void SetAvailable(); + +public: + + ComponentType GetType() const override; + bool IsInitialized() const override; + bool IsAvailable() const override; + + mpt::ustring GetVersion() const override; + +public: + + void Initialize() override; + +protected: + + virtual bool DoInitialize() = 0; + +}; + + +class ComponentBuiltin : public ComponentBase +{ +public: + ComponentBuiltin() + : ComponentBase(ComponentTypeBuiltin) + { + return; + } + bool DoInitialize() override + { + return true; + } +}; + + +#define MPT_GLOBAL_BIND(lib, name) name = &::name; + + +#if defined(MPT_ENABLE_DYNBIND) + + +class ComponentLibrary + : public ComponentBase +{ + +private: + + typedef std::map TLibraryMap; + TLibraryMap m_Libraries; + + bool m_BindFailed; + +protected: + + ComponentLibrary(ComponentType type); + +public: + + virtual ~ComponentLibrary(); + +protected: + + bool AddLibrary(const std::string &libName, const mpt::LibraryPath &libPath); + void ClearLibraries(); + void SetBindFailed(); + void ClearBindFailed(); + bool HasBindFailed() const; + +public: + + virtual mpt::Library GetLibrary(const std::string &libName) const; + + template + bool Bind(Tfunc * & f, const std::string &libName, const std::string &symbol) const + { + return GetLibrary(libName).Bind(f, symbol); + } + +protected: + + bool DoInitialize() override = 0; + +}; + + +#define MPT_COMPONENT_BIND(libName, func) MPT_DO { if(!Bind( func , libName , #func )) { SetBindFailed(); } } MPT_WHILE_0 +#define MPT_COMPONENT_BIND_OPTIONAL(libName, func) Bind( func , libName , #func ) +#define MPT_COMPONENT_BIND_SYMBOL(libName, symbol, func) MPT_DO { if(!Bind( func , libName , symbol )) { SetBindFailed(); } } MPT_WHILE_0 +#define MPT_COMPONENT_BIND_SYMBOL_OPTIONAL(libName, symbol, func) Bind( func , libName , symbol ) + +#if MPT_OS_WINDOWS +#ifdef UNICODE +#define MPT_COMPONENT_BINDWIN_SUFFIX "W" +#else +#define MPT_COMPONENT_BINDWIN_SUFFIX "A" +#endif +#define MPT_COMPONENT_BINDWIN(libName, func) MPT_DO { if(!Bind( func , libName , #func MPT_COMPONENT_BINDWIN_SUFFIX )) { SetBindFailed(); } } MPT_WHILE_0 +#define MPT_COMPONENT_BINDWIN_OPTIONAL(libName, func) Bind( func , libName , #func MPT_COMPONENT_BINDWIN_SUFFIX ) +#define MPT_COMPONENT_BINDWIN_SYMBOL(libName, symbol, func) MPT_DO { if(!Bind( func , libName , symbol MPT_COMPONENT_BINDWIN_SUFFIX )) { SetBindFailed(); } } MPT_WHILE_0 +#define MPT_COMPONENT_BINDWIN_SYMBOL_OPTIONAL(libName, symbol, func) Bind( func , libName , symbol MPT_COMPONENT_BINDWIN_SUFFIX ) +#endif + + +class ComponentSystemDLL : public ComponentLibrary +{ +private: + mpt::PathString m_BaseName; +public: + ComponentSystemDLL(const mpt::PathString &baseName) + : ComponentLibrary(ComponentTypeSystem) + , m_BaseName(baseName) + { + return; + } + bool DoInitialize() override + { + AddLibrary(m_BaseName.ToUTF8(), mpt::LibraryPath::System(m_BaseName)); + return GetLibrary(m_BaseName.ToUTF8()).IsValid(); + } +}; + + +class ComponentBundledDLL : public ComponentLibrary +{ +private: + mpt::PathString m_FullName; +public: + ComponentBundledDLL(const mpt::PathString &fullName) + : ComponentLibrary(ComponentTypeBundled) + , m_FullName(fullName) + { + return; + } + bool DoInitialize() override + { + AddLibrary(m_FullName.ToUTF8(), mpt::LibraryPath::AppFullName(m_FullName)); + return GetLibrary(m_FullName.ToUTF8()).IsValid(); + } +}; + + +#endif // MPT_ENABLE_DYNBIND + + +#if MPT_COMPONENT_MANAGER + + +class ComponentManager; + +typedef std::shared_ptr (*ComponentFactoryMethod)(ComponentManager &componentManager); + + +class IComponentFactory +{ +protected: + IComponentFactory() = default; +public: + virtual ~IComponentFactory() = default; +public: + virtual std::string GetID() const = 0; + virtual std::string GetSettingsKey() const = 0; + virtual std::shared_ptr Construct(ComponentManager &componentManager) const = 0; + virtual ComponentFactoryMethod GetStaticConstructor() const = 0; +}; + + +class ComponentFactoryBase + : public IComponentFactory +{ +private: + std::string m_ID; + std::string m_SettingsKey; +protected: + ComponentFactoryBase(const std::string &id, const std::string &settingsKey); + void PreConstruct() const; + void Initialize(ComponentManager &componentManager, std::shared_ptr component) const; +public: + virtual ~ComponentFactoryBase(); + std::string GetID() const override; + std::string GetSettingsKey() const override; + std::shared_ptr Construct(ComponentManager &componentManager) const override = 0; + ComponentFactoryMethod GetStaticConstructor() const override = 0; +}; + + +template +class ComponentFactory + : public ComponentFactoryBase +{ +public: + ComponentFactory() + : ComponentFactoryBase(T::g_ID, T::g_SettingsKey) + { + return; + } +public: + std::shared_ptr Construct(ComponentManager &componentManager) const override + { + PreConstruct(); + std::shared_ptr component = std::make_shared(); + Initialize(componentManager, component); + return component; + } + static std::shared_ptr StaticConstruct(ComponentManager &componentManager) + { + return ComponentFactory().Construct(componentManager); + } + virtual ComponentFactoryMethod GetStaticConstructor() const override + { + return &StaticConstruct; + } +}; + + +class IComponentManagerSettings +{ +public: + virtual bool LoadOnStartup() const = 0; + virtual bool KeepLoaded() const = 0; + virtual bool IsBlocked(const std::string &key) const = 0; + virtual mpt::PathString Path() const = 0; +protected: + virtual ~IComponentManagerSettings() = default; +}; + + +class ComponentManagerSettingsDefault + : public IComponentManagerSettings +{ +public: + bool LoadOnStartup() const override { return false; } + bool KeepLoaded() const override { return true; } + bool IsBlocked(const std::string & /*key*/ ) const override { return false; } + mpt::PathString Path() const override { return mpt::PathString(); } +}; + + +enum ComponentState +{ + ComponentStateUnregistered, + ComponentStateBlocked, + ComponentStateUnintialized, + ComponentStateUnavailable, + ComponentStateAvailable, +}; + + +struct ComponentInfo +{ + std::string name; + ComponentState state; + std::string settingsKey; + ComponentType type; +}; + + +class ComponentManager +{ + friend class ComponentFactoryBase; +public: + static void Init(const IComponentManagerSettings &settings); + static void Release(); + static std::shared_ptr Instance(); +private: + ComponentManager(const IComponentManagerSettings &settings); +private: + struct RegisteredComponent + { + std::string settingsKey; + ComponentFactoryMethod factoryMethod; + std::shared_ptr instance; + std::weak_ptr weakInstance; + }; + typedef std::map TComponentMap; + const IComponentManagerSettings &m_Settings; + TComponentMap m_Components; +private: + bool IsComponentBlocked(const std::string &settingsKey) const; + void InitializeComponent(std::shared_ptr component) const; +public: + void Register(const IComponentFactory &componentFactory); + void Startup(); + std::shared_ptr GetComponent(const IComponentFactory &componentFactory); + std::shared_ptr ReloadComponent(const IComponentFactory &componentFactory); + std::vector GetRegisteredComponents() const; + ComponentInfo GetComponentInfo(std::string name) const; + mpt::PathString GetComponentPath() const; +}; + + +struct ComponentListEntry +{ + ComponentListEntry *next; + void (*reg)(ComponentManager &componentManager); +}; + +bool ComponentListPush(ComponentListEntry *entry); + +#define MPT_DECLARE_COMPONENT_MEMBERS public: static const char * const g_ID; static const char * const g_SettingsKey; + +#define MPT_REGISTERED_COMPONENT(name, settingsKey) \ + static void RegisterComponent ## name (ComponentManager &componentManager) \ + { \ + componentManager.Register(ComponentFactory< name >()); \ + } \ + static ComponentListEntry Component ## name ## ListEntry = { nullptr, & RegisterComponent ## name }; \ + bool Component ## name ## Registered = ComponentListPush(& Component ## name ## ListEntry ); \ + const char * const name :: g_ID = #name ; \ + const char * const name :: g_SettingsKey = settingsKey ; \ +/**/ + + +template +std::shared_ptr GetComponent() +{ + return std::dynamic_pointer_cast(ComponentManager::Instance()->GetComponent(ComponentFactory())); +} + + +template +std::shared_ptr ReloadComponent() +{ + return std::dynamic_pointer_cast(ComponentManager::Instance()->ReloadComponent(ComponentFactory())); +} + + +inline mpt::PathString GetComponentPath() +{ + return ComponentManager::Instance()->GetComponentPath(); +} + + +#else // !MPT_COMPONENT_MANAGER + + +#define MPT_DECLARE_COMPONENT_MEMBERS + +#define MPT_REGISTERED_COMPONENT(name, settingsKey) + + +template +std::shared_ptr GetComponent() +{ + static std::weak_ptr cache; + static mpt::mutex m; + mpt::lock_guard l(m); + std::shared_ptr component = cache.lock(); + if(!component) + { + component = std::make_shared(); + component->Initialize(); + cache = component; + } + return component; +} + + +inline mpt::PathString GetComponentPath() +{ + return mpt::PathString(); +} + + +#endif // MPT_COMPONENT_MANAGER + + +// Simple wrapper around std::shared_ptr which automatically +// gets a reference to the component (or constructs it) on initialization. +template +class ComponentHandle +{ +private: + std::shared_ptr component; +public: + ComponentHandle() + : component(GetComponent()) + { + return; + } + ~ComponentHandle() + { + return; + } + bool IsAvailable() const + { + return component && component->IsAvailable(); + } + const T *get() const + { + return component.get(); + } + const T &operator*() const + { + return *component; + } + const T *operator->() const + { + return &*component; + } +#if MPT_COMPONENT_MANAGER + void Reload() + { + component = nullptr; + component = ReloadComponent(); + } +#endif +}; + + +template +bool IsComponentAvailable(const ComponentHandle &handle) +{ + return handle.IsAvailable(); +} + + +#endif // MPT_ENABLE_COMPONENTS + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/Endianness.h b/Frameworks/OpenMPT.old/OpenMPT/common/Endianness.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/Endianness.h rename to Frameworks/OpenMPT.old/OpenMPT/common/Endianness.h diff --git a/Frameworks/OpenMPT/OpenMPT/common/FileReader.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/FileReader.cpp similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/FileReader.cpp rename to Frameworks/OpenMPT.old/OpenMPT/common/FileReader.cpp diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/FileReader.h b/Frameworks/OpenMPT.old/OpenMPT/common/FileReader.h new file mode 100644 index 000000000..7569ce5ec --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/FileReader.h @@ -0,0 +1,1431 @@ +/* + * FileReader.h + * ------------ + * Purpose: A basic class for transparent reading of memory-based files. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + +#include "mptStringBuffer.h" +#include "misc_util.h" +#include "Endianness.h" +#include "mptIO.h" +#include +#include +#include +#include + +#include "FileReaderFwd.h" + + +OPENMPT_NAMESPACE_BEGIN + + +// change to show warnings for functions which trigger pre-caching the whole file for unseekable streams +//#define FILEREADER_DEPRECATED [[deprecated]] +#define FILEREADER_DEPRECATED + + +class FileReaderTraitsMemory +{ + +public: + + using off_t = FileDataContainerMemory::off_t; + + using data_type = FileDataContainerMemory; + using ref_data_type = const FileDataContainerMemory &; + using shared_data_type = const FileDataContainerMemory &; + using value_data_type = FileDataContainerMemory; + + static shared_data_type get_shared(const data_type & data) { return data; } + static ref_data_type get_ref(const data_type & data) { return data; } + + static value_data_type make_data() { return mpt::const_byte_span(); } + static value_data_type make_data(mpt::const_byte_span data) { return data; } + + static value_data_type make_chunk(shared_data_type data, off_t position, off_t size) + { + return mpt::as_span(data.GetRawData() + position, size); + } + +}; + +class FileReaderTraitsStdStream +{ + +public: + + using off_t = IFileDataContainer::off_t; + + using data_type = std::shared_ptr; + using ref_data_type = const IFileDataContainer &; + using shared_data_type = std::shared_ptr; + using value_data_type = std::shared_ptr; + + static shared_data_type get_shared(const data_type & data) { return data; } + static ref_data_type get_ref(const data_type & data) { return *data; } + + static value_data_type make_data() { return std::make_shared(); } + static value_data_type make_data(mpt::const_byte_span data) { return std::make_shared(data); } + + static value_data_type make_chunk(shared_data_type data, off_t position, off_t size) + { + return std::static_pointer_cast(std::make_shared(data, position, size)); + } + +}; + +using FileReaderTraitsDefault = FileReaderTraitsStdStream; + +namespace mpt +{ +namespace FileReader +{ + + // Read a "T" object from the stream. + // If not enough bytes can be read, false is returned. + // If successful, the file cursor is advanced by the size of "T". + template + bool Read(TFileCursor &f, T &target) + { + // cppcheck false-positive + // cppcheck-suppress uninitvar + mpt::byte_span dst = mpt::as_raw_memory(target); + if(dst.size() != f.GetRaw(dst)) + { + return false; + } + f.Skip(dst.size()); + return true; + } + + // Read some kind of integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + T ReadIntLE(TFileCursor &f) + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + typename mpt::make_le::type target; + if(Read(f, target)) + { + return target; + } else + { + return 0; + } + } + + // Read some kind of integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + T ReadIntBE(TFileCursor &f) + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + typename mpt::make_be::type target; + if(Read(f, target)) + { + return target; + } else + { + return 0; + } + } + + // Read a integer in little-endian format which has some of its higher bytes not stored in file. + // If successful, the file cursor is advanced by the given size. + template + T ReadTruncatedIntLE(TFileCursor &f, typename TFileCursor::off_t size) + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + MPT_ASSERT(sizeof(T) >= size); + if(size == 0) + { + return 0; + } + if(!f.CanRead(size)) + { + return 0; + } + uint8 buf[sizeof(T)]; + bool negative = false; + for(std::size_t i = 0; i < sizeof(T); ++i) + { + uint8 byte = 0; + if(i < size) + { + Read(f, byte); + negative = std::numeric_limits::is_signed && ((byte & 0x80) != 0x00); + } else + { + // sign or zero extend + byte = negative ? 0xff : 0x00; + } + buf[i] = byte; + } + typename mpt::make_le::type target; + std::memcpy(&target, buf, sizeof(T)); + return target; + } + + // Read a supplied-size little endian integer to a fixed size variable. + // The data is properly sign-extended when fewer bytes are stored. + // If more bytes are stored, higher order bytes are silently ignored. + // If successful, the file cursor is advanced by the given size. + template + T ReadSizedIntLE(TFileCursor &f, typename TFileCursor::off_t size) + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + if(size == 0) + { + return 0; + } + if(!f.CanRead(size)) + { + return 0; + } + if(size < sizeof(T)) + { + return ReadTruncatedIntLE(f, size); + } + T retval = ReadIntLE(f); + f.Skip(size - sizeof(T)); + return retval; + } + + // Read unsigned 32-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + uint32 ReadUint32LE(TFileCursor &f) + { + return ReadIntLE(f); + } + + // Read unsigned 32-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + uint32 ReadUint32BE(TFileCursor &f) + { + return ReadIntBE(f); + } + + // Read signed 32-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + int32 ReadInt32LE(TFileCursor &f) + { + return ReadIntLE(f); + } + + // Read signed 32-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + int32 ReadInt32BE(TFileCursor &f) + { + return ReadIntBE(f); + } + + // Read unsigned 16-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + uint16 ReadUint16LE(TFileCursor &f) + { + return ReadIntLE(f); + } + + // Read unsigned 16-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + uint16 ReadUint16BE(TFileCursor &f) + { + return ReadIntBE(f); + } + + // Read signed 16-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + int16 ReadInt16LE(TFileCursor &f) + { + return ReadIntLE(f); + } + + // Read signed 16-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + int16 ReadInt16BE(TFileCursor &f) + { + return ReadIntBE(f); + } + + // Read a single 8bit character. + // If successful, the file cursor is advanced by the size of the integer. + template + char ReadChar(TFileCursor &f) + { + char target; + if(Read(f, target)) + { + return target; + } else + { + return 0; + } + } + + // Read unsigned 8-Bit integer. + // If successful, the file cursor is advanced by the size of the integer. + template + uint8 ReadUint8(TFileCursor &f) + { + uint8 target; + if(Read(f, target)) + { + return target; + } else + { + return 0; + } + } + + // Read signed 8-Bit integer. If successful, the file cursor is advanced by the size of the integer. + template + int8 ReadInt8(TFileCursor &f) + { + int8 target; + if(Read(f, target)) + { + return target; + } else + { + return 0; + } + } + + // Read 32-Bit float in little-endian format. + // If successful, the file cursor is advanced by the size of the float. + template + float ReadFloatLE(TFileCursor &f) + { + IEEE754binary32LE target; + if(Read(f, target)) + { + return target; + } else + { + return 0.0f; + } + } + + // Read 32-Bit float in big-endian format. + // If successful, the file cursor is advanced by the size of the float. + template + float ReadFloatBE(TFileCursor &f) + { + IEEE754binary32BE target; + if(Read(f, target)) + { + return target; + } else + { + return 0.0f; + } + } + + // Read 64-Bit float in little-endian format. + // If successful, the file cursor is advanced by the size of the float. + template + double ReadDoubleLE(TFileCursor &f) + { + IEEE754binary64LE target; + if(Read(f, target)) + { + return target; + } else + { + return 0.0; + } + } + + // Read 64-Bit float in big-endian format. + // If successful, the file cursor is advanced by the size of the float. + template + double ReadDoubleBE(TFileCursor &f) + { + IEEE754binary64BE target; + if(Read(f, target)) + { + return target; + } else + { + return 0.0; + } + } + + // Read a struct. + // If successful, the file cursor is advanced by the size of the struct. Otherwise, the target is zeroed. + template + bool ReadStruct(TFileCursor &f, T &target) + { + static_assert(mpt::is_binary_safe::value); + if(Read(f, target)) + { + return true; + } else + { + Clear(target); + return false; + } + } + + // Allow to read a struct partially (if there's less memory available than the struct's size, fill it up with zeros). + // The file cursor is advanced by "partialSize" bytes. + template + typename TFileCursor::off_t ReadStructPartial(TFileCursor &f, T &target, typename TFileCursor::off_t partialSize = sizeof(T)) + { + static_assert(mpt::is_binary_safe::value); + typename TFileCursor::off_t copyBytes = std::min(partialSize, sizeof(T)); + if(!f.CanRead(copyBytes)) + { + copyBytes = f.BytesLeft(); + } + f.GetRaw(mpt::as_raw_memory(target).data(), copyBytes); + std::memset(mpt::as_raw_memory(target).data() + copyBytes, 0, sizeof(target) - copyBytes); + f.Skip(partialSize); + return copyBytes; + } + + // Read a string of length srcSize into fixed-length char array destBuffer using a given read mode. + // The file cursor is advanced by "srcSize" bytes. + // Returns true if at least one byte could be read or 0 bytes were requested. + template + bool ReadString(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::off_t srcSize) + { + typename TFileCursor::PinnedRawDataView source = f.ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + typename TFileCursor::off_t realSrcSize = source.size(); // In case fewer bytes are available + mpt::String::WriteAutoBuf(destBuffer) = mpt::String::ReadBuf(mode, mpt::byte_cast(source.data()), realSrcSize); + return (realSrcSize > 0 || srcSize == 0); + } + + // Read a string of length srcSize into a std::string dest using a given read mode. + // The file cursor is advanced by "srcSize" bytes. + // Returns true if at least one character could be read or 0 characters were requested. + template + bool ReadString(TFileCursor &f, std::string &dest, const typename TFileCursor::off_t srcSize) + { + dest.clear(); + typename TFileCursor::PinnedRawDataView source = f.ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + typename TFileCursor::off_t realSrcSize = source.size(); // In case fewer bytes are available + dest = mpt::String::ReadBuf(mode, mpt::byte_cast(source.data()), realSrcSize); + return (realSrcSize > 0 || srcSize == 0); + } + + // Read a string of length srcSize into a mpt::charbuf dest using a given read mode. + // The file cursor is advanced by "srcSize" bytes. + // Returns true if at least one character could be read or 0 characters were requested. + template + bool ReadString(TFileCursor &f, mpt::charbuf &dest, const typename TFileCursor::off_t srcSize) + { + typename TFileCursor::PinnedRawDataView source = f.ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + typename TFileCursor::off_t realSrcSize = source.size(); // In case fewer bytes are available + dest = mpt::String::ReadBuf(mode, mpt::byte_cast(source.data()), realSrcSize); + return (realSrcSize > 0 || srcSize == 0); + } + + // Read a charset encoded string of length srcSize into a mpt::ustring dest using a given read mode. + // The file cursor is advanced by "srcSize" bytes. + // Returns true if at least one character could be read or 0 characters were requested. + template + bool ReadString(TFileCursor &f, mpt::ustring &dest, mpt::Charset charset, const typename TFileCursor::off_t srcSize) + { + dest.clear(); + typename TFileCursor::PinnedRawDataView source = f.ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + typename TFileCursor::off_t realSrcSize = source.size(); // In case fewer bytes are available + dest = mpt::ToUnicode(charset, mpt::String::ReadBuf(mode, mpt::byte_cast(source.data()), realSrcSize)); + return (realSrcSize > 0 || srcSize == 0); + } + + // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode. + // The file cursor is advanced by the string length. + // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. + template + bool ReadSizedString(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::off_t maxLength = std::numeric_limits::max()) + { + packed srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs + if(!Read(f, srcSize)) + return false; + return ReadString(f, destBuffer, std::min(static_cast(srcSize), maxLength)); + } + + // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode. + // The file cursor is advanced by the string length. + // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. + template + bool ReadSizedString(TFileCursor &f, std::string &dest, const typename TFileCursor::off_t maxLength = std::numeric_limits::max()) + { + packed srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs + if(!Read(f, srcSize)) + return false; + return ReadString(f, dest, std::min(static_cast(srcSize), maxLength)); + } + + // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a mpt::charbuf dest using a given read mode. + // The file cursor is advanced by the string length. + // Returns true if the size field could be read and at least one character could be read or 0 characters were requested. + template + bool ReadSizedString(TFileCursor &f, mpt::charbuf &dest, const typename TFileCursor::off_t maxLength = std::numeric_limits::max()) + { + packed srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs + if(!Read(f, srcSize)) + return false; + return ReadString(f, dest, std::min(static_cast(srcSize), maxLength)); + } + + // Read a null-terminated string into a std::string + template + bool ReadNullString(TFileCursor &f, std::string &dest, const typename TFileCursor::off_t maxLength = std::numeric_limits::max()) + { + dest.clear(); + if(!f.CanRead(1)) + return false; + try + { + char buffer[64]; + typename TFileCursor::off_t avail = 0; + while((avail = std::min(f.GetRaw(buffer, std::size(buffer)), maxLength - dest.length())) != 0) + { + auto end = std::find(buffer, buffer + avail, '\0'); + dest.insert(dest.end(), buffer, end); + f.Skip(end - buffer); + if(end < buffer + avail) + { + // Found null char + f.Skip(1); + break; + } + } + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); + } + return dest.length() != 0; + } + + // Read a string up to the next line terminator into a std::string + template + bool ReadLine(TFileCursor &f, std::string &dest, const typename TFileCursor::off_t maxLength = std::numeric_limits::max()) + { + dest.clear(); + if(!f.CanRead(1)) + return false; + try + { + char buffer[64]; + char c = '\0'; + typename TFileCursor::off_t avail = 0; + while((avail = std::min(f.GetRaw(buffer, std::size(buffer)), maxLength - dest.length())) != 0) + { + auto end = std::find_if(buffer, buffer + avail, mpt::String::Traits::IsLineEnding); + dest.insert(dest.end(), buffer, end); + f.Skip(end - buffer); + if(end < buffer + avail) + { + // Found line ending + f.Skip(1); + // Handle CRLF line ending + if(*end == '\r') + { + if(Read(f, c) && c != '\n') + f.SkipBack(1); + } + break; + } + } + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); + } + return true; + } + + // Read an array of binary-safe T values. + // If successful, the file cursor is advanced by the size of the array. + // Otherwise, the target is zeroed. + template + bool ReadArray(TFileCursor &f, T (&destArray)[destSize]) + { + static_assert(mpt::is_binary_safe::value); + if(f.CanRead(sizeof(destArray))) + { + f.ReadRaw(mpt::as_raw_memory(destArray)); + return true; + } else + { + Clear(destArray); + return false; + } + } + + // Read an array of binary-safe T values. + // If successful, the file cursor is advanced by the size of the array. + // Otherwise, the target is zeroed. + template + bool ReadArray(TFileCursor &f, std::array &destArray) + { + static_assert(mpt::is_binary_safe::value); + if(f.CanRead(sizeof(destArray))) + { + f.ReadRaw(mpt::as_raw_memory(destArray)); + return true; + } else + { + destArray.fill(T()); + return false; + } + } + + // Read destSize elements of binary-safe type T into a vector. + // If successful, the file cursor is advanced by the size of the vector. + // Otherwise, the vector is resized to destSize, but possibly existing contents are not cleared. + template + bool ReadVector(TFileCursor &f, std::vector &destVector, size_t destSize) + { + static_assert(mpt::is_binary_safe::value); + destVector.resize(destSize); + if(f.CanRead(sizeof(T) * destSize)) + { + f.ReadRaw(mpt::as_raw_memory(destVector)); + return true; + } else + { + return false; + } + } + + template + std::array ReadArray(TFileCursor &f) + { + std::array destArray; + ReadArray(f, destArray); + return destArray; + } + + // Compare a magic string with the current stream position. + // Returns true if they are identical and advances the file cursor by the the length of the "magic" string. + // Returns false if the string could not be found. The file cursor is not advanced in this case. + template + bool ReadMagic(TFileCursor &f, const char *const magic, typename TFileCursor::off_t magicLength) + { + std::byte buffer[16] = { std::byte(0) }; + typename TFileCursor::off_t bytesRead = 0; + typename TFileCursor::off_t bytesRemain = magicLength; + while(bytesRemain) + { + typename TFileCursor::off_t numBytes = std::min(static_cast(sizeof(buffer)), bytesRemain); + if(f.GetRawWithOffset(bytesRead, buffer, numBytes) != numBytes) + return false; + if(memcmp(buffer, magic + bytesRead, numBytes)) + return false; + bytesRemain -= numBytes; + bytesRead += numBytes; + } + f.Skip(magicLength); + return true; + } + template + bool ReadMagic(TFileCursor &f, const char (&magic)[N]) + { + MPT_ASSERT(magic[N - 1] == '\0'); + for(std::size_t i = 0; i < N - 1; ++i) + { + MPT_ASSERT(magic[i] != '\0'); + } + return ReadMagic(f, static_cast(magic), static_cast(N - 1)); + } + + // Read variable-length unsigned integer (as found in MIDI files). + // If successful, the file cursor is advanced by the size of the integer and true is returned. + // False is returned if not enough bytes were left to finish reading of the integer or if an overflow happened (source doesn't fit into target integer). + // In case of an overflow, the target is also set to the maximum value supported by its data type. + template + bool ReadVarInt(TFileCursor &f, T &target) + { + static_assert(std::numeric_limits::is_integer == true + && std::numeric_limits::is_signed == false, + "Target type is not an unsigned integer"); + + if(f.NoBytesLeft()) + { + target = 0; + return false; + } + + std::byte bytes[16]; // More than enough for any valid VarInt + typename TFileCursor::off_t avail = f.GetRaw(bytes, sizeof(bytes)); + typename TFileCursor::off_t readPos = 1; + + uint8 b = mpt::byte_cast(bytes[0]); + target = (b & 0x7F); + size_t writtenBits = static_cast(mpt::bit_width(target)); // Bits used in the most significant byte + + while(readPos < avail && (b & 0x80) != 0) + { + b = mpt::byte_cast(bytes[readPos++]); + target <<= 7; + target |= (b & 0x7F); + writtenBits += 7; + if(readPos == avail) + { + f.Skip(readPos); + avail = f.GetRaw(bytes, sizeof(bytes)); + readPos = 0; + } + } + f.Skip(readPos); + + if(writtenBits > sizeof(target) * 8u) + { + // Overflow + target = Util::MaxValueOfType(target); + return false; + } else if((b & 0x80) != 0) + { + // Reached EOF + return false; + } + return true; + } + +} // namespace FileReader +} // namespace mpt + +namespace FR = mpt::FileReader; + +namespace detail { + +template +class FileReader +{ + +private: + + using traits_type = Ttraits; + +public: + + using off_t = typename traits_type::off_t; + + using data_type = typename traits_type::data_type; + using ref_data_type = typename traits_type::ref_data_type; + using shared_data_type = typename traits_type::shared_data_type; + using value_data_type = typename traits_type::value_data_type; + +protected: + + shared_data_type SharedDataContainer() const { return traits_type::get_shared(m_data); } + ref_data_type DataContainer() const { return traits_type::get_ref(m_data); } + + static value_data_type DataInitializer() { return traits_type::make_data(); } + static value_data_type DataInitializer(mpt::const_byte_span data) { return traits_type::make_data(data); } + + static value_data_type CreateChunkImpl(shared_data_type data, off_t position, off_t size) { return traits_type::make_chunk(data, position, size); } + +private: + + data_type m_data; + + off_t streamPos; // Cursor location in the file + + const mpt::PathString *fileName; // Filename that corresponds to this FileReader. It is only set if this FileReader represents the whole contents of fileName. May be nullptr. Lifetime is managed outside of FileReader. + +public: + + // Initialize invalid file reader object. + FileReader() : m_data(DataInitializer()), streamPos(0), fileName(nullptr) { } + + // Initialize file reader object with pointer to data and data length. + template FileReader(mpt::span bytedata, const mpt::PathString *filename = nullptr) : m_data(DataInitializer(mpt::byte_cast(bytedata))), streamPos(0), fileName(filename) { } + + // Initialize file reader object based on an existing file reader object window. + explicit FileReader(value_data_type other, const mpt::PathString *filename = nullptr) : m_data(other), streamPos(0), fileName(filename) { } + +public: + + mpt::PathString GetFileName() const + { + if(!fileName) + { + return mpt::PathString(); + } + return *fileName; + } + + // Returns true if the object points to a valid (non-empty) stream. + operator bool() const + { + return IsValid(); + } + + // Returns true if the object points to a valid (non-empty) stream. + bool IsValid() const + { + return DataContainer().IsValid(); + } + + // Reset cursor to first byte in file. + void Rewind() + { + streamPos = 0; + } + + // Seek to a position in the mapped file. + // Returns false if position is invalid. + bool Seek(off_t position) + { + if(position <= streamPos) + { + streamPos = position; + return true; + } + if(position <= DataContainer().GetLength()) + { + streamPos = position; + return true; + } else + { + return false; + } + } + + // Increases position by skipBytes. + // Returns true if skipBytes could be skipped or false if the file end was reached earlier. + bool Skip(off_t skipBytes) + { + if(CanRead(skipBytes)) + { + streamPos += skipBytes; + return true; + } else + { + streamPos = DataContainer().GetLength(); + return false; + } + } + + // Decreases position by skipBytes. + // Returns true if skipBytes could be skipped or false if the file start was reached earlier. + bool SkipBack(off_t skipBytes) + { + if(streamPos >= skipBytes) + { + streamPos -= skipBytes; + return true; + } else + { + streamPos = 0; + return false; + } + } + + // Returns cursor position in the mapped file. + off_t GetPosition() const + { + return streamPos; + } + + // Return true IFF seeking and GetLength() is fast. + // In particular, it returns false for unseekable stream where GetLength() + // requires pre-caching. + bool HasFastGetLength() const + { + return DataContainer().HasFastGetLength(); + } + + // Returns size of the mapped file in bytes. + FILEREADER_DEPRECATED off_t GetLength() const + { + // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file + return DataContainer().GetLength(); + } + + // Return byte count between cursor position and end of file, i.e. how many bytes can still be read. + FILEREADER_DEPRECATED off_t BytesLeft() const + { + // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file + return DataContainer().GetLength() - streamPos; + } + + bool EndOfFile() const + { + return !CanRead(1); + } + + bool NoBytesLeft() const + { + return !CanRead(1); + } + + // Check if "amount" bytes can be read from the current position in the stream. + bool CanRead(off_t amount) const + { + return DataContainer().CanRead(streamPos, amount); + } + + // Check if file size is at least size, without potentially caching the whole file to query the exact file length. + bool LengthIsAtLeast(off_t size) const + { + return DataContainer().CanRead(0, size); + } + + // Check if file size is exactly size, without potentially caching the whole file if it is larger. + bool LengthIs(off_t size) const + { + return DataContainer().CanRead(0, size) && !DataContainer().CanRead(size, 1); + } + +protected: + + FileReader CreateChunk(off_t position, off_t length) const + { + off_t readableLength = DataContainer().GetReadableLength(position, length); + if(readableLength == 0) + { + return FileReader(); + } + return FileReader(CreateChunkImpl(SharedDataContainer(), position, std::min(length, DataContainer().GetLength() - position))); + } + +public: + + // Create a new FileReader object for parsing a sub chunk at a given position with a given length. + // The file cursor is not modified. + FileReader GetChunkAt(off_t position, off_t length) const + { + return CreateChunk(position, length); + } + + // Create a new FileReader object for parsing a sub chunk at the current position with a given length. + // The file cursor is not advanced. + FileReader GetChunk(off_t length) + { + return CreateChunk(streamPos, length); + } + // Create a new FileReader object for parsing a sub chunk at the current position with a given length. + // The file cursor is advanced by "length" bytes. + FileReader ReadChunk(off_t length) + { + off_t position = streamPos; + Skip(length); + return CreateChunk(position, length); + } + + class PinnedRawDataView + { + private: + std::size_t size_; + const std::byte *pinnedData; + std::vector cache; + private: + void Init(const FileReader &file, std::size_t size) + { + size_ = 0; + pinnedData = nullptr; + if(!file.CanRead(size)) + { + size = file.BytesLeft(); + } + size_ = size; + if(file.DataContainer().HasPinnedView()) + { + pinnedData = file.DataContainer().GetRawData() + file.GetPosition(); + } else + { + cache.resize(size_); + if(!cache.empty()) + { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + file.GetRaw(&(cache[0]), size); + } + } + } + public: + PinnedRawDataView() + : size_(0) + , pinnedData(nullptr) + { + } + PinnedRawDataView(const FileReader &file) + { + Init(file, file.BytesLeft()); + } + PinnedRawDataView(const FileReader &file, std::size_t size) + { + Init(file, size); + } + PinnedRawDataView(FileReader &file, bool advance) + { + Init(file, file.BytesLeft()); + if(advance) + { + file.Skip(size_); + } + } + PinnedRawDataView(FileReader &file, std::size_t size, bool advance) + { + Init(file, size); + if(advance) + { + file.Skip(size_); + } + } + public: + mpt::const_byte_span GetSpan() const + { + if(pinnedData) + { + return mpt::as_span(pinnedData, size_); + } else if(!cache.empty()) + { + return mpt::as_span(cache); + } else + { + return mpt::const_byte_span(); + } + } + mpt::const_byte_span span() const { return GetSpan(); } + void invalidate() { size_ = 0; pinnedData = nullptr; cache = std::vector(); } + const std::byte *data() const { return span().data(); } + std::size_t size() const { return size_; } + mpt::const_byte_span::pointer begin() const { return span().data(); } + mpt::const_byte_span::pointer end() const { return span().data() + span().size(); } + mpt::const_byte_span::const_pointer cbegin() const { return span().data(); } + mpt::const_byte_span::const_pointer cend() const { return span().data() + span().size(); } + }; + + // Returns a pinned view into the remaining raw data from cursor position. + PinnedRawDataView GetPinnedRawDataView() const + { + return PinnedRawDataView(*this); + } + // Returns a pinned view into the remeining raw data from cursor position, clamped at size. + PinnedRawDataView GetPinnedRawDataView(std::size_t size) const + { + return PinnedRawDataView(*this, size); + } + + // Returns a pinned view into the remeining raw data from cursor position. + // File cursor is advaned by the size of the returned pinned view. + PinnedRawDataView ReadPinnedRawDataView() + { + return PinnedRawDataView(*this, true); + } + // Returns a pinned view into the remeining raw data from cursor position, clamped at size. + // File cursor is advaned by the size of the returned pinned view. + PinnedRawDataView ReadPinnedRawDataView(std::size_t size) + { + return PinnedRawDataView(*this, size, true); + } + + // Returns raw stream data at cursor position. + // Should only be used if absolutely necessary, for example for sample reading, or when used with a small chunk of the file retrieved by ReadChunk(). + // Use GetPinnedRawDataView(size) whenever possible. + FILEREADER_DEPRECATED const std::byte *GetRawData() const + { + // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file + return DataContainer().GetRawData() + streamPos; + } + template + FILEREADER_DEPRECATED const T *GetRawData() const + { + // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file + return mpt::byte_cast(DataContainer().GetRawData() + streamPos); + } + + template + std::size_t GetRawWithOffset(std::size_t offset, T *dst, std::size_t count) const + { + return static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos + offset, count)); + } + std::size_t GetRawWithOffset(std::size_t offset, mpt::byte_span dst) const + { + return static_cast(DataContainer().Read(streamPos + offset, dst)); + } + + template + std::size_t GetRaw(T *dst, std::size_t count) const + { + return static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos, count)); + } + std::size_t GetRaw(mpt::byte_span dst) const + { + return static_cast(DataContainer().Read(streamPos, dst)); + } + + template + std::size_t ReadRaw(T *dst, std::size_t count) + { + std::size_t result = static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos, count)); + streamPos += result; + return result; + } + std::size_t ReadRaw(mpt::byte_span dst) + { + std::size_t result = static_cast(DataContainer().Read(streamPos, dst)); + streamPos += result; + return result; + } + + std::vector GetRawDataAsByteVector() const + { + PinnedRawDataView view = GetPinnedRawDataView(); + return mpt::make_vector(view.span()); + } + std::vector ReadRawDataAsByteVector() + { + PinnedRawDataView view = ReadPinnedRawDataView(); + return mpt::make_vector(view.span()); + } + std::vector GetRawDataAsByteVector(std::size_t size) const + { + PinnedRawDataView view = GetPinnedRawDataView(size); + return mpt::make_vector(view.span()); + } + std::vector ReadRawDataAsByteVector(std::size_t size) + { + PinnedRawDataView view = ReadPinnedRawDataView(size); + return mpt::make_vector(view.span()); + } + + std::string GetRawDataAsString() const + { + PinnedRawDataView view = GetPinnedRawDataView(); + mpt::span data = mpt::byte_cast>(view.span()); + return std::string(data.begin(), data.end()); + } + std::string ReadRawDataAsString() + { + PinnedRawDataView view = ReadPinnedRawDataView(); + mpt::span data = mpt::byte_cast>(view.span()); + return std::string(data.begin(), data.end()); + } + std::string GetRawDataAsString(std::size_t size) const + { + PinnedRawDataView view = GetPinnedRawDataView(size); + mpt::span data = mpt::byte_cast>(view.span()); + return std::string(data.begin(), data.end()); + } + std::string ReadRawDataAsString(std::size_t size) + { + PinnedRawDataView view = ReadPinnedRawDataView(size); + mpt::span data = mpt::byte_cast>(view.span()); + return std::string(data.begin(), data.end()); + } + + template + bool Read(T &target) + { + return mpt::FileReader::Read(*this, target); + } + + template + T ReadIntLE() + { + return mpt::FileReader::ReadIntLE(*this); + } + + template + T ReadIntBE() + { + return mpt::FileReader::ReadIntLE(*this); + } + + template + T ReadTruncatedIntLE(off_t size) + { + return mpt::FileReader::ReadTruncatedIntLE(*this, size); + } + + template + T ReadSizedIntLE(off_t size) + { + return mpt::FileReader::ReadSizedIntLE(*this, size); + } + + uint32 ReadUint32LE() + { + return mpt::FileReader::ReadUint32LE(*this); + } + + uint32 ReadUint32BE() + { + return mpt::FileReader::ReadUint32BE(*this); + } + + int32 ReadInt32LE() + { + return mpt::FileReader::ReadInt32LE(*this); + } + + int32 ReadInt32BE() + { + return mpt::FileReader::ReadInt32BE(*this); + } + + uint16 ReadUint16LE() + { + return mpt::FileReader::ReadUint16LE(*this); + } + + uint16 ReadUint16BE() + { + return mpt::FileReader::ReadUint16BE(*this); + } + + int16 ReadInt16LE() + { + return mpt::FileReader::ReadInt16LE(*this); + } + + int16 ReadInt16BE() + { + return mpt::FileReader::ReadInt16BE(*this); + } + + char ReadChar() + { + return mpt::FileReader::ReadChar(*this); + } + + uint8 ReadUint8() + { + return mpt::FileReader::ReadUint8(*this); + } + + int8 ReadInt8() + { + return mpt::FileReader::ReadInt8(*this); + } + + float ReadFloatLE() + { + return mpt::FileReader::ReadFloatLE(*this); + } + + float ReadFloatBE() + { + return mpt::FileReader::ReadFloatBE(*this); + } + + double ReadDoubleLE() + { + return mpt::FileReader::ReadDoubleLE(*this); + } + + double ReadDoubleBE() + { + return mpt::FileReader::ReadDoubleBE(*this); + } + + template + bool ReadStruct(T &target) + { + return mpt::FileReader::ReadStruct(*this, target); + } + + template + size_t ReadStructPartial(T &target, size_t partialSize = sizeof(T)) + { + return mpt::FileReader::ReadStructPartial(*this, target, partialSize); + } + + template + bool ReadString(char (&destBuffer)[destSize], const off_t srcSize) + { + return mpt::FileReader::ReadString(*this, destBuffer, srcSize); + } + + template + bool ReadString(std::string &dest, const off_t srcSize) + { + return mpt::FileReader::ReadString(*this, dest, srcSize); + } + + template + bool ReadString(mpt::charbuf &dest, const off_t srcSize) + { + return mpt::FileReader::ReadString(*this, dest, srcSize); + } + + template + bool ReadString(mpt::ustring &dest, mpt::Charset charset, const off_t srcSize) + { + return mpt::FileReader::ReadString(*this, dest, charset, srcSize); + } + + template + bool ReadSizedString(char (&destBuffer)[destSize], const off_t maxLength = std::numeric_limits::max()) + { + return mpt::FileReader::ReadSizedString(*this, destBuffer, maxLength); + } + + template + bool ReadSizedString(std::string &dest, const off_t maxLength = std::numeric_limits::max()) + { + return mpt::FileReader::ReadSizedString(*this, dest, maxLength); + } + + template + bool ReadSizedString(mpt::charbuf &dest, const off_t maxLength = std::numeric_limits::max()) + { + return mpt::FileReader::ReadSizedString(*this, dest, maxLength); + } + + bool ReadNullString(std::string &dest, const off_t maxLength = std::numeric_limits::max()) + { + return mpt::FileReader::ReadNullString(*this, dest, maxLength); + } + + bool ReadLine(std::string &dest, const off_t maxLength = std::numeric_limits::max()) + { + return mpt::FileReader::ReadLine(*this, dest, maxLength); + } + + template + bool ReadArray(T (&destArray)[destSize]) + { + return mpt::FileReader::ReadArray(*this, destArray); + } + + template + bool ReadArray(std::array &destArray) + { + return mpt::FileReader::ReadArray(*this, destArray); + } + + template + std::array ReadArray() + { + return mpt::FileReader::ReadArray(*this); + } + + template + bool ReadVector(std::vector &destVector, size_t destSize) + { + return mpt::FileReader::ReadVector(*this, destVector, destSize); + } + + template + bool ReadMagic(const char (&magic)[N]) + { + return mpt::FileReader::ReadMagic(*this, magic); + } + + bool ReadMagic(const char *const magic, off_t magicLength) + { + return mpt::FileReader::ReadMagic(*this, magic, magicLength); + } + + template + bool ReadVarInt(T &target) + { + return mpt::FileReader::ReadVarInt(*this, target); + } + +}; + +} // namespace detail + +using FileReader = detail::FileReader; + +using MemoryFileReader = detail::FileReader; + + +// Initialize file reader object with pointer to data and data length. +template inline FileReader make_FileReader(mpt::span bytedata, const mpt::PathString *filename = nullptr) +{ + return FileReader(mpt::byte_cast(bytedata), filename); +} + +#if defined(MPT_FILEREADER_CALLBACK_STREAM) + +// Initialize file reader object with a CallbackStream. +inline FileReader make_FileReader(CallbackStream s, const mpt::PathString *filename = nullptr) +{ + return FileReader( + FileDataContainerCallbackStreamSeekable::IsSeekable(s) ? + std::static_pointer_cast(std::make_shared(s)) + : + std::static_pointer_cast(std::make_shared(s)) + , filename + ); +} +#endif // MPT_FILEREADER_CALLBACK_STREAM + +// Initialize file reader object with a std::istream. +inline FileReader make_FileReader(std::istream *s, const mpt::PathString *filename = nullptr) +{ + return FileReader( + FileDataContainerStdStreamSeekable::IsSeekable(s) ? + std::static_pointer_cast(std::make_shared(s)) + : + std::static_pointer_cast(std::make_shared(s)) + , filename + ); +} + + +#if defined(MPT_ENABLE_FILEIO) +// templated in order to reduce header inter-dependencies +template +FileReader GetFileReader(TInputFile &file) +{ + if(!file.IsValid()) + { + return FileReader(); + } + if(file.IsCached()) + { + return make_FileReader(file.GetCache(), &file.GetFilenameRef()); + } else + { + return make_FileReader(file.GetStream(), &file.GetFilenameRef()); + } +} +#endif // MPT_ENABLE_FILEIO + + +#if defined(MPT_ENABLE_TEMPFILE) && MPT_OS_WINDOWS + +class OnDiskFileWrapper +{ + +private: + + mpt::PathString m_Filename; + bool m_IsTempFile; + +public: + + OnDiskFileWrapper(FileReader &file, const mpt::PathString &fileNameExtension = P_("tmp")); + + ~OnDiskFileWrapper(); + +public: + + bool IsValid() const; + + mpt::PathString GetFilename() const; + +}; // class OnDiskFileWrapper + +#endif // MPT_ENABLE_TEMPFILE && MPT_OS_WINDOWS + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/FileReaderFwd.h b/Frameworks/OpenMPT.old/OpenMPT/common/FileReaderFwd.h new file mode 100644 index 000000000..2004bd625 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/FileReaderFwd.h @@ -0,0 +1,35 @@ +/* + * FileReaderFwd.h + * --------------- + * Purpose: Forward declaration for class FileReader. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "BuildSettings.h" + + +OPENMPT_NAMESPACE_BEGIN + +class FileReaderTraitsMemory; + +class FileReaderTraitsStdStream; + +using FileReaderTraitsDefault = FileReaderTraitsStdStream; + +namespace detail { + +template +class FileReader; + +} // namespace detail + +using FileReader = detail::FileReader; + +using MemoryFileReader = detail::FileReader; + +OPENMPT_NAMESPACE_END + diff --git a/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h b/Frameworks/OpenMPT.old/OpenMPT/common/FlagSet.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/FlagSet.h rename to Frameworks/OpenMPT.old/OpenMPT/common/FlagSet.h diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/Logging.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/Logging.cpp new file mode 100644 index 000000000..6b4042c89 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/Logging.cpp @@ -0,0 +1,433 @@ +/* + * Logging.cpp + * ----------- + * Purpose: General logging + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" + +#include "Logging.h" +#include "mptFileIO.h" +#if defined(MODPLUG_TRACKER) +#include +#endif +#include "version.h" + +#include + +#include +#include + +#include + + +OPENMPT_NAMESPACE_BEGIN + + +namespace mpt +{ +namespace log +{ + + +#ifndef NO_LOGGING + + + +#if !defined(MPT_LOG_GLOBAL_LEVEL_STATIC) +#if defined(MPT_LOG_GLOBAL_LEVEL) +int GlobalLogLevel = static_cast(MPT_LOG_GLOBAL_LEVEL); +#else +int GlobalLogLevel = static_cast(LogDebug); +#endif +#endif + + + +#if defined(MODPLUG_TRACKER) && !defined(MPT_LOG_IS_DISABLED) + +bool FileEnabled = false; +bool DebuggerEnabled = true; +bool ConsoleEnabled = false; + +static char g_FacilitySolo[1024] = {0}; +static char g_FacilityBlocked[1024] = {0}; + +void SetFacilities(const std::string &solo, const std::string &blocked) +{ + std::strcpy(g_FacilitySolo, solo.c_str()); + std::strcpy(g_FacilityBlocked, blocked.c_str()); +} + +bool IsFacilityActive(const char *facility) +{ + if(facility) + { + if(std::strlen(g_FacilitySolo) > 0) + { + if(std::strcmp(facility, g_FacilitySolo) != 0) + { + return false; + } + } + if(std::strlen(g_FacilityBlocked) > 0) + { + if(std::strcmp(facility, g_FacilitySolo) == 0) + { + return false; + } + } + } + return true; +} + +#endif + + +void Logger::SendLogMessage(const mpt::source_location &loc, LogLevel level, const char *facility, const mpt::ustring &text) +{ +#ifdef MPT_LOG_IS_DISABLED + MPT_UNREFERENCED_PARAMETER(loc); + MPT_UNREFERENCED_PARAMETER(level); + MPT_UNREFERENCED_PARAMETER(facility); + MPT_UNREFERENCED_PARAMETER(text); +#else // !MPT_LOG_IS_DISABLED + MPT_MAYBE_CONSTANT_IF(mpt::log::GlobalLogLevel < level) + { + return; + } + #if defined(MODPLUG_TRACKER) + if(!IsFacilityActive(facility)) + { + return; + } + #else // !MODPLUG_TRACKER + MPT_UNREFERENCED_PARAMETER(facility); + #endif // MODPLUG_TRACKER + // remove eol if already present and add log level prefix + const mpt::ustring message = LogLevelToString(level) + U_(": ") + mpt::String::RTrim(text, U_("\r\n")); + const mpt::ustring file = mpt::ToUnicode(mpt::CharsetSource, loc.file_name() ? loc.file_name() : ""); + const mpt::ustring function = mpt::ToUnicode(mpt::CharsetSource, loc.function_name() ? loc.function_name() : ""); + const mpt::ustring line = mpt::ufmt::dec(loc.line()); + #if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) +#if MPT_OS_WINDOWS + static uint64 s_lastlogtime = 0; + uint64 cur = mpt::Date::ANSI::Now(); + uint64 diff = cur/10000 - s_lastlogtime; + s_lastlogtime = cur/10000; +#else + uint64 cur = 0; + uint64 diff = 0; +#endif + if(mpt::log::FileEnabled) + { + static mpt::ofstream s_logfile; + if(!s_logfile) + { + s_logfile.open(P_("mptrack.log"), std::ios::app); + } + if(s_logfile) + { + mpt::IO::WriteText(s_logfile, mpt::ToCharset(mpt::CharsetLogfile, mpt::format(U_("%1+%2 %3(%4): %5 [%6]\n")) + ( mpt::Date::ANSI::ToUString(cur) + , mpt::ufmt::right(6, mpt::ufmt::dec(diff)) + , file + , line + , message + , function + ))); + mpt::IO::Flush(s_logfile); + } + } + if(mpt::log::DebuggerEnabled) + { + OutputDebugStringW(mpt::ToWide(mpt::format(U_("%1(%2): +%3 %4 [%5]\n")) + ( file + , line + , mpt::ufmt::right(6, mpt::ufmt::dec(diff)) + , message + , function + )).c_str()); + } + if(mpt::log::ConsoleEnabled) + { + static bool consoleInited = false; + if(!consoleInited) + { + AllocConsole(); + consoleInited = true; + } + std::wstring consoletext = mpt::ToWide(message) + L"\r\n"; + DWORD dummy = 0; + WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), consoletext.c_str(), mpt::saturate_cast(consoletext.length()), &dummy, NULL); + } + #elif defined(MODPLUG_TRACKER) && defined(MPT_BUILD_WINESUPPORT) + std::clog + << "NativeSupport: " + << mpt::ToCharset(mpt::CharsetStdIO, file) << "(" << mpt::ToCharset(mpt::CharsetStdIO, line) << ")" << ": " + << mpt::ToCharset(mpt::CharsetStdIO, message) + << " [" << mpt::ToCharset(mpt::CharsetStdIO, function) << "]" + << std::endl; + #else // !MODPLUG_TRACKER + std::clog + << "libopenmpt: " + << mpt::ToCharset(mpt::CharsetStdIO, file) << "(" << mpt::ToCharset(mpt::CharsetStdIO, line) << ")" << ": " + << mpt::ToCharset(mpt::CharsetStdIO, message) + << " [" << mpt::ToCharset(mpt::CharsetStdIO, function) << "]" + << std::endl; + #endif // MODPLUG_TRACKER +#endif // MPT_LOG_IS_DISABLED +} + + + + +#endif // !NO_LOGGING + + + +#if defined(MODPLUG_TRACKER) + +namespace Trace { + +#if MPT_OS_WINDOWS + +// Debugging functionality will use simple globals. + +std::atomic g_Enabled = ATOMIC_VAR_INIT(false); + +static bool g_Sealed = false; + +struct Entry { + uint32 Index; + uint32 ThreadId; + uint64 Timestamp; + const char * Function; + const char * File; + int Line; + Direction Direction; +}; + +static MPT_FORCEINLINE bool operator < (const Entry &a, const Entry &b) noexcept +{ +/* + return false + || (a.Timestamp < b.Timestamp) + || (a.ThreadID < b.ThreadID) + || (a.File < b.File) + || (a.Line < b.Line) + || (a.Function < b.Function) + ; +*/ + return false + || (a.Index < b.Index) + ; +} + +static std::vector Entries; + +static std::atomic NextIndex(0); + +static uint32 ThreadIdGUI = 0; +static uint32 ThreadIdAudio = 0; +static uint32 ThreadIdNotify = 0; +static uint32 ThreadIdWatchdir = 0; + +void Enable(std::size_t numEntries) +{ + if(g_Sealed) + { + return; + } + Entries.clear(); + Entries.resize(numEntries); + NextIndex.store(0); + g_Enabled = (numEntries > 0); +} + +void Disable() +{ + if(g_Sealed) + { + return; + } + g_Enabled = false; +} + +MPT_NOINLINE void Trace(const mpt::source_location & loc, Direction direction) noexcept +{ + // This will get called in realtime contexts and hot paths. + // No blocking allowed here. + const uint32 index = NextIndex.fetch_add(1); + const std::size_t numEntries = Entries.size(); +#if 1 + LARGE_INTEGER time; + time.QuadPart = 0; + QueryPerformanceCounter(&time); + const uint64 timestamp = time.QuadPart; +#else + FILETIME time = FILETIME(); + GetSystemTimeAsFileTime(&time); + const uint64 timestamp = (static_cast(time.dwHighDateTime) << 32) | (static_cast(time.dwLowDateTime) << 0); +#endif + const uint32 threadid = static_cast(GetCurrentThreadId()); + mpt::log::Trace::Entry & entry = Entries[index % numEntries]; + entry.Index = index; + entry.ThreadId = threadid; + entry.Timestamp = timestamp; + entry.Function = loc.function_name(); + entry.File = loc.file_name(); + entry.Line = loc.line(); + entry.Direction = direction; +} + +void Seal() +{ + if(!g_Enabled) + { + return; + } + g_Enabled = false; + g_Sealed = true; + uint32 count = NextIndex.fetch_add(0); + if(count < Entries.size()) + { + Entries.resize(count); + } +} + +bool Dump(const mpt::PathString &filename) +{ + if(!g_Sealed) + { + return false; + } + + LARGE_INTEGER qpcNow; + qpcNow.QuadPart = 0; + QueryPerformanceCounter(&qpcNow); + uint64 ftNow = mpt::Date::ANSI::Now(); + + // sort according to index in case of overflows + std::stable_sort(Entries.begin(), Entries.end()); + + mpt::ofstream f(filename); + + f << "Build: OpenMPT " << mpt::ToCharset(mpt::CharsetLogfile, Build::GetVersionStringExtended()) << std::endl; + + bool qpcValid = false; + + LARGE_INTEGER qpcFreq; + qpcFreq.QuadPart = 0; + QueryPerformanceFrequency(&qpcFreq); + if(qpcFreq.QuadPart > 0) + { + qpcValid = true; + } + + f << "Dump: " << mpt::ToCharset(mpt::CharsetLogfile, mpt::Date::ANSI::ToUString(ftNow)) << std::endl; + f << "Captured events: " << Entries.size() << std::endl; + if(qpcValid && (Entries.size() > 0)) + { + double period = static_cast(Entries[Entries.size() - 1].Timestamp - Entries[0].Timestamp) / static_cast(qpcFreq.QuadPart); + double eventsPerSecond = Entries.size() / period; + f << "Period [s]: " << mpt::fmt::fix(period) << std::endl; + f << "Events/second: " << mpt::fmt::fix(eventsPerSecond) << std::endl; + } + + for(auto &entry : Entries) + { + if(!entry.Function) entry.Function = ""; + if(!entry.File) entry.File = ""; + std::string time; + if(qpcValid) + { + time = mpt::ToCharset(mpt::CharsetLogfile, mpt::Date::ANSI::ToUString( ftNow - static_cast( static_cast(qpcNow.QuadPart - entry.Timestamp) * (10000000.0 / static_cast(qpcFreq.QuadPart) ) ) ) ); + } else + { + time = mpt::format("0x%1")(mpt::fmt::hex0<16>(entry.Timestamp)); + } + f << time; + if(entry.ThreadId == ThreadIdGUI) + { + f << " -----GUI "; + } else if(entry.ThreadId == ThreadIdAudio) + { + f << " ---Audio "; + } else if(entry.ThreadId == ThreadIdNotify) + { + f << " --Notify "; + } else if(entry.ThreadId == ThreadIdWatchdir) + { + f << " WatchDir "; + } else + { + f << " " << mpt::fmt::hex0<8>(entry.ThreadId) << " "; + } + f << (entry.Direction == mpt::log::Trace::Direction::Enter ? ">" : entry.Direction == mpt::log::Trace::Direction::Leave ? "<" : " ") << " "; + f << entry.File << "(" << entry.Line << "): " << entry.Function; + f << std::endl; + } + return true; +} + +void SetThreadId(mpt::log::Trace::ThreadKind kind, uint32 id) +{ + if(id == 0) + { + return; + } + switch(kind) + { + case ThreadKindGUI: + ThreadIdGUI = id; + break; + case ThreadKindAudio: + ThreadIdAudio = id; + break; + case ThreadKindNotify: + ThreadIdNotify = id; + break; + case ThreadKindWatchdir: + ThreadIdWatchdir = id; + break; + } +} + +uint32 GetThreadId(mpt::log::Trace::ThreadKind kind) +{ + uint32 result = 0; + switch(kind) + { + case ThreadKindGUI: + result = ThreadIdGUI; + break; + case ThreadKindAudio: + result = ThreadIdAudio; + break; + case ThreadKindNotify: + result = ThreadIdNotify; + break; + case ThreadKindWatchdir: + result = ThreadIdWatchdir; + break; + } + return result; +} + +#endif // MPT_OS_WINDOWS + +} // namespace Trace + +#endif // MODPLUG_TRACKER + + +} // namespace log +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/Logging.h b/Frameworks/OpenMPT.old/OpenMPT/common/Logging.h new file mode 100644 index 000000000..9b59df89b --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/Logging.h @@ -0,0 +1,254 @@ +/* + * Logging.h + * --------- + * Purpose: General logging + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "BuildSettings.h" + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS +#include +#endif + +OPENMPT_NAMESPACE_BEGIN + + +/* + + +Build time logging configuration (in BuildSettings.h): + + * #define NO_LOGGING + Disables all logging completely. + MPT_LOG calls are not even compiled but instead completely removed via the + preprocessor. + + * #define MPT_LOG_GLOBAL_LEVEL_STATIC + #define MPT_LOG_GLOBAL_LEVEL # + Define the former (to anything) and the latter (to one of the log levels + below) in order to statically select the verbosity of logging at build time. + MPT_LOG calls that exceed the specified logging level will get dead-code + eliminated at compile time. + This especially means that, when setting MPT_LOG_GLOBAL_LEVEL to 0, no + MPT_LOG call (with a constant level parameter) remains in the resulting + binary, however, they still do get parsed and properly type checked by the + compiler. + + +Logging: + +If the context is related to a particular CSoundfile instance, use +CSoundfile::AddToLog. + +Logging a simple message: +MPT_LOG(LogWarning, "sounddev", "some message"); +MPT_LOG(LogWarning, "sounddev", U_("some message")); +Facility is some course grained code section identifier (more coarse grained +than the current file name probably), useful to do some selective logging. + +Logging a more complex message: +MPT_LOG(LogWarning, "sounddev", mpt::format(U_("Some message: foo=%1, bar=0x%2"))(foo, mpt::ufmt::hex0<8>(bar))); + +Note that even with full enabled logging and a runtime configurable logging +level, the runtime overhead of a MPT_LOG(level, facility, text) call is just a +single conditional in case the verbosity does not require logging the respective +message. Even the expression "text" is not evaluated. + + +*/ + + +enum LogLevel +{ + LogDebug = 5, + LogInformation = 4, + LogNotification = 3, + LogWarning = 2, + LogError = 1 +}; + + +inline mpt::ustring LogLevelToString(LogLevel level) +{ + switch(level) + { + case LogError: return U_("error"); break; + case LogWarning: return U_("warning"); break; + case LogNotification: return U_("notify"); break; + case LogInformation: return U_("info"); break; + case LogDebug: return U_("debug"); break; + } + return U_("unknown"); +} + + +class ILog +{ +protected: + virtual ~ILog() { } +public: + virtual void AddToLog(LogLevel level, const mpt::ustring &text) const = 0; +}; + + + +namespace mpt +{ +namespace log +{ + + + +#ifndef NO_LOGGING + + +#if defined(MPT_LOG_GLOBAL_LEVEL_STATIC) +#if (MPT_LOG_GLOBAL_LEVEL <= 0) +// Logging framework is enabled (!NO_LOGGING) but all logging has beeen statically disabled. +// All logging code gets compiled and immediately dead-code eliminated. +#define MPT_LOG_IS_DISABLED +#endif +static constexpr int GlobalLogLevel = MPT_LOG_GLOBAL_LEVEL ; +#else +extern int GlobalLogLevel; +#endif + + +#if defined(MODPLUG_TRACKER) && !defined(MPT_LOG_IS_DISABLED) +extern bool FileEnabled; +extern bool DebuggerEnabled; +extern bool ConsoleEnabled; +void SetFacilities(const std::string &solo, const std::string &blocked); +bool IsFacilityActive(const char *facility); +#else +MPT_FORCEINLINE bool IsFacilityActive(const char * /*facility*/ ) { return true; } +#endif + + +#endif // !NO_LOGGING + + + +#ifndef NO_LOGGING + + +class Logger +{ +public: + // facility:ASCII + void SendLogMessage(const mpt::source_location &loc, LogLevel level, const char *facility, const mpt::ustring &text); +}; + +#define MPT_LOG(level, facility, text) \ + MPT_DO \ + { \ + MPT_MAYBE_CONSTANT_IF(mpt::log::GlobalLogLevel >= ( level )) \ + { \ + MPT_MAYBE_CONSTANT_IF(mpt::log::IsFacilityActive(( facility ))) \ + { \ + mpt::log::Logger().SendLogMessage( MPT_SOURCE_LOCATION_CURRENT() , ( level ), ( facility ), ( text )); \ + } \ + } \ + } MPT_WHILE_0 \ +/**/ + + +#else // !NO_LOGGING + + +#define MPT_LOG(level, facility, text) MPT_DO { } MPT_WHILE_0 + + +#endif // NO_LOGGING + + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + +namespace Trace { + +// This is not strictly thread safe in all corner cases because of missing barriers. +// We do not care in order to not harm the fast path with additional barriers. +// Enabled tracing incurs a runtime overhead with multiple threads as a global atomic variable +// gets modified. +// This cacheline bouncing does not matter at all +// if there are not multiple thread adding trace points at high frequency (way greater than 1000Hz), +// which, in OpenMPT, is only ever the case for just a single thread (the audio thread), if at all. +extern std::atomic g_Enabled; +inline bool IsEnabled() { return g_Enabled; } + +enum class Direction : int8 +{ + Unknown = 0, + Enter = 1, + Leave = -1, +}; + +MPT_NOINLINE void Trace(const mpt::source_location & loc, Direction direction = Direction::Unknown) noexcept; + +enum ThreadKind { + ThreadKindGUI, + ThreadKindAudio, + ThreadKindNotify, + ThreadKindWatchdir, +}; + +void Enable(std::size_t numEntries); +void Disable(); + +void SetThreadId(mpt::log::Trace::ThreadKind kind, uint32 id); +uint32 GetThreadId(mpt::log::Trace::ThreadKind kind); + +void Seal(); +bool Dump(const mpt::PathString &filename); + +class Scope +{ +private: + const mpt::source_location loc; +public: + MPT_FORCEINLINE Scope(mpt::source_location loc) noexcept + : loc(loc) + { + if(mpt::log::Trace::g_Enabled) + { + mpt::log::Trace::Trace(loc, mpt::log::Trace::Direction::Enter); + } + } + MPT_FORCEINLINE ~Scope() noexcept + { + if(mpt::log::Trace::g_Enabled) + { + mpt::log::Trace::Trace(loc, mpt::log::Trace::Direction::Leave); + } + } +}; + +#define MPT_TRACE_CONCAT_HELPER(x, y) x ## y +#define MPT_TRACE_CONCAT(x, y) MPT_TRACE_CONCAT_HELPER(x, y) + +#define MPT_TRACE_SCOPE() mpt::log::Trace::Scope MPT_TRACE_CONCAT(MPT_TRACE_VAR, __LINE__)(MPT_SOURCE_LOCATION_CURRENT()) + +#define MPT_TRACE() MPT_DO { if(mpt::log::Trace::g_Enabled) { mpt::log::Trace::Trace(MPT_SOURCE_LOCATION_CURRENT()); } } MPT_WHILE_0 + +} // namespace Trace + +#else // !MODPLUG_TRACKER + +#define MPT_TRACE_SCOPE() MPT_DO { } MPT_WHILE_0 + +#define MPT_TRACE() MPT_DO { } MPT_WHILE_0 + +#endif // MODPLUG_TRACKER + + + +} // namespace log +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/Profiler.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/Profiler.cpp new file mode 100644 index 000000000..fd3c7f6eb --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/Profiler.cpp @@ -0,0 +1,221 @@ +/* + * Profiler.cpp + * ------------ + * Purpose: Performance measuring + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "Profiler.h" + + +OPENMPT_NAMESPACE_BEGIN + + +#ifdef USE_PROFILER + + +class Statistics +{ +public: + Profile &profile; + Profile::Data data; + double usage; + Statistics(Profile &p) : profile(p) + { + usage = 0.0; + Update(); + } + void Update() + { + data = profile.GetAndResetData(); + uint64 now = profile.GetTime(); + uint64 timewindow = now - data.Start; + if(data.Calls > 0 && timewindow > 0) + { + usage = (double)data.Sum / (double)timewindow; + } else + { + usage = 0.0; + } + } +}; + + +struct ProfileBlock +{ + class Profile * profile; + const char * name; + class Statistics * stats; +}; + +static constexpr std::size_t MAX_PROFILES = 1024; + +static ProfileBlock Profiles[ MAX_PROFILES ]; + +static std::size_t NextProfile = 0; + + +static void RegisterProfile(Profile *newprofile) +{ + if(NextProfile < MAX_PROFILES) + { + Profiles[NextProfile].profile = newprofile; + Profiles[NextProfile].stats = 0; + NextProfile++; + } +} + + +static void UnregisterProfile(Profile *oldprofile) +{ + for(std::size_t i=0; iUpdate(); + } + } +} + + +std::string Profiler::DumpProfiles() +{ + std::string ret; + for(std::size_t i=0; i Profiler::DumpCategories() +{ + std::vector ret; + ret.resize(Profiler::CategoriesCount); + for(std::size_t i=0; iCategory] += Profiles[i].stats->usage; + } + } + return ret; +} + + +uint64 Profile::GetTime() const +{ + LARGE_INTEGER ret; + ret.QuadPart = 0; + QueryPerformanceCounter(&ret); + return ret.QuadPart; +} + + +uint64 Profile::GetFrequency() const +{ + LARGE_INTEGER ret; + ret.QuadPart = 0; + QueryPerformanceFrequency(&ret); + return ret.QuadPart; +} + + +Profile::Profile(Profiler::Category category, const char *name) : Category(category), Name(name) +{ + data.Calls = 0; + data.Sum = 0; + data.Overhead = 0; + data.Start = GetTime(); + EnterTime = 0; + RegisterProfile(this); +} + + +Profile::~Profile() +{ + UnregisterProfile(this); +} + + +Profile::Data Profile::GetAndResetData() +{ + Profile::Data ret; + datamutex.lock(); + ret = data; + data.Calls = 0; + data.Sum = 0; + data.Overhead = 0; + data.Start = GetTime(); + datamutex.unlock(); + return ret; +} + + +void Profile::Reset() +{ + datamutex.lock(); + data.Calls = 0; + data.Sum = 0; + data.Overhead = 0; + data.Start = GetTime(); + datamutex.unlock(); +} + + +void Profile::Enter() +{ + EnterTime = GetTime(); +} + + +void Profile::Leave() +{ + uint64 LeaveTime = GetTime(); + datamutex.lock(); + data.Calls += 1; + data.Sum += LeaveTime - EnterTime; + datamutex.unlock(); +} + + +#else // !USE_PROFILER + +MPT_MSVC_WORKAROUND_LNK4221(Profiler) + +#endif // USE_PROFILER + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/Profiler.h b/Frameworks/OpenMPT.old/OpenMPT/common/Profiler.h new file mode 100644 index 000000000..86f11dbf9 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/Profiler.h @@ -0,0 +1,127 @@ +/* + * Profiler.h + * ---------- + * Purpose: Performance measuring + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + +#include "../common/mptMutex.h" +#include +#include + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MODPLUG_TRACKER) + +//#define USE_PROFILER + +#endif + +#ifdef USE_PROFILER + +class Profiler +{ +public: + enum Category + { + GUI, + Audio, + Notify, + CategoriesCount + }; + static std::vector GetCategoryNames() + { + std::vector ret; + ret.push_back("GUI"); + ret.push_back("Audio"); + ret.push_back("Notify"); + return ret; + } +public: + static void Update(); + static std::string DumpProfiles(); + static std::vector DumpCategories(); +}; + + +class Profile +{ +private: + mutable mpt::mutex datamutex; +public: + struct Data + { + uint64 Calls; + uint64 Sum; + int64 Overhead; + uint64 Start; + }; +public: + Data data; + uint64 EnterTime; + Profiler::Category Category; + const char * const Name; + uint64 GetTime() const; + uint64 GetFrequency() const; +public: + Profile(Profiler::Category category, const char *name); + ~Profile(); + void Reset(); + void Enter(); + void Leave(); + class Scope + { + private: + Profile &profile; + public: + Scope(Profile &p) : profile(p) { profile.Enter(); } + ~Scope() { profile.Leave(); } + }; +public: + Data GetAndResetData(); +}; + + +#define OPENMPT_PROFILE_SCOPE(cat, name) \ + static Profile OPENMPT_PROFILE_VAR(cat, name);\ + Profile::Scope OPENMPT_PROFILE_SCOPE_VAR(OPENMPT_PROFILE_VAR); \ +/**/ + + +#define OPENMPT_PROFILE_FUNCTION(cat) OPENMPT_PROFILE_SCOPE(cat, __func__) + + +#else // !USE_PROFILER + + +class Profiler +{ +public: + enum Category + { + CategoriesCount + }; + static std::vector GetCategoryNames() { return std::vector(); } +public: + static void Update() { } + static std::string DumpProfiles() { return std::string(); } + static std::vector DumpCategories() { return std::vector(); } +}; +#define OPENMPT_PROFILE_SCOPE(cat, name) MPT_DO { } MPT_WHILE_0 +#define OPENMPT_PROFILE_FUNCTION(cat) MPT_DO { } MPT_WHILE_0 + + +#endif // USE_PROFILER + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/misc_util.cpp similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp rename to Frameworks/OpenMPT.old/OpenMPT/common/misc_util.cpp diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/misc_util.h b/Frameworks/OpenMPT.old/OpenMPT/common/misc_util.h new file mode 100644 index 000000000..cfc633002 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/misc_util.h @@ -0,0 +1,247 @@ +/* + * misc_util.h + * ----------- + * Purpose: Various useful utility functions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + +#include "mptAssert.h" +#include "mptBaseMacros.h" +#include "mptBaseTypes.h" +#include "mptBaseUtils.h" +#include "mptString.h" + +// old +#include "mptBaseUtils.h" +#include "mptSpan.h" +#include "mptMemory.h" +#include "mptExceptionText.h" +#include "mptStringFormat.h" +#include "mptStringParse.h" +#include "mptCPU.h" +#include "mptOS.h" +#include "mptTime.h" +#include "mptLibrary.h" + +#include +#include + +#include + +#include + + +OPENMPT_NAMESPACE_BEGIN + + +namespace Util +{ + + // Insert a range of items [insStart, insEnd], and possibly shift item fix to the left. + template + void InsertItem(const T insStart, const T insEnd, T &fix) + { + MPT_ASSERT(insEnd >= insStart); + if(fix >= insStart) + { + fix += (insEnd - insStart + 1); + } + } + + // Insert a range of items [insStart, insEnd], and possibly shift items in range [fixStart, fixEnd] to the right. + template + void InsertRange(const T insStart, const T insEnd, T &fixStart, T &fixEnd) + { + MPT_ASSERT(insEnd >= insStart); + const T insLength = insEnd - insStart + 1; + if(fixStart >= insEnd) + { + fixStart += insLength; + } + if(fixEnd >= insEnd) + { + fixEnd += insLength; + } + } + + // Delete a range of items [delStart, delEnd], and possibly shift item fix to the left. + template + void DeleteItem(const T delStart, const T delEnd, T &fix) + { + MPT_ASSERT(delEnd >= delStart); + if(fix > delEnd) + { + fix -= (delEnd - delStart + 1); + } + } + + // Delete a range of items [delStart, delEnd], and possibly shift items in range [fixStart, fixEnd] to the left. + template + void DeleteRange(const T delStart, const T delEnd, T &fixStart, T &fixEnd) + { + MPT_ASSERT(delEnd >= delStart); + const T delLength = delEnd - delStart + 1; + if(delStart < fixStart && delEnd < fixStart) + { + // cut part is before loop start + fixStart -= delLength; + fixEnd -= delLength; + } else if(delStart < fixStart && delEnd < fixEnd) + { + // cut part is partly before loop start + fixStart = delStart; + fixEnd -= delLength; + } else if(delStart >= fixStart && delEnd < fixEnd) + { + // cut part is in the loop + fixEnd -= delLength; + } else if(delStart >= fixStart && delStart < fixEnd && delEnd > fixEnd) + { + // cut part is partly before loop end + fixEnd = delStart; + } + } + +} // namespace Util + + + +namespace Util +{ + + template + class fixed_size_queue + { + private: + T buffer[n+1]; + std::size_t read_position; + std::size_t write_position; + public: + fixed_size_queue() : read_position(0), write_position(0) + { + return; + } + void clear() + { + read_position = 0; + write_position = 0; + } + std::size_t read_size() const + { + if ( write_position > read_position ) + { + return write_position - read_position; + } else if ( write_position < read_position ) + { + return write_position - read_position + n + 1; + } else + { + return 0; + } + } + std::size_t write_size() const + { + if ( write_position > read_position ) + { + return read_position - write_position + n; + } else if ( write_position < read_position ) + { + return read_position - write_position - 1; + } else + { + return n; + } + } + bool push( const T & v ) + { + if ( !write_size() ) + { + return false; + } + buffer[write_position] = v; + write_position = ( write_position + 1 ) % ( n + 1 ); + return true; + } + bool pop() { + if ( !read_size() ) + { + return false; + } + read_position = ( read_position + 1 ) % ( n + 1 ); + return true; + } + T peek() { + if ( !read_size() ) + { + return T(); + } + return buffer[read_position]; + } + const T * peek_p() + { + if ( !read_size() ) + { + return nullptr; + } + return &(buffer[read_position]); + } + const T * peek_next_p() + { + if ( read_size() < 2 ) + { + return nullptr; + } + return &(buffer[(read_position+1)%(n+1)]); + } + }; + +} // namespace Util + + +namespace Util +{ + +std::vector HexToBin(const mpt::ustring &src); +mpt::ustring BinToHex(mpt::const_byte_span src); + +template inline mpt::ustring BinToHex(mpt::span src) { return Util::BinToHex(mpt::byte_cast(src)); } + +} // namespace Util + + +#if defined(MODPLUG_TRACKER) || (defined(LIBOPENMPT_BUILD) && defined(LIBOPENMPT_BUILD_TEST)) + +namespace mpt +{ + +// Wrapper around std::getenv. +// Instead of returning null pointer if the environment variable is not set, +// this wrapper returns the provided default value. +std::string getenv(const std::string &env_var, const std::string &def = std::string()); + +} // namespace mpt + +#endif // MODPLUG_TRACKER || (LIBOPENMPT_BUILD && LIBOPENMPT_BUILD_TEST) + + +#if MPT_OS_WINDOWS + +template +Tstring ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes(const Tbuf *buf, Tsize sizeBytes) +{ + // REG_SZ may contain a single NUL terminator, multiple NUL terminators, or no NUL terminator at all + return Tstring(reinterpret_cast(buf), reinterpret_cast(buf) + (sizeBytes / sizeof(typename Tstring::value_type))).c_str(); +} + + +#endif // MPT_OS_WINDOWS + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptAlloc.cpp similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp rename to Frameworks/OpenMPT.old/OpenMPT/common/mptAlloc.cpp diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptAlloc.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptAlloc.h diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptAssert.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptAssert.h new file mode 100644 index 000000000..7c2687adc --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptAssert.h @@ -0,0 +1,145 @@ +/* + * mptAssert.h + * ----------- + * Purpose: assert and static_assert + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + + +#include "mptBaseMacros.h" + + + +OPENMPT_NAMESPACE_BEGIN + + + +// Static code checkers might need to get the knowledge of our assertions transferred to them. +#define MPT_CHECKER_ASSUME_ASSERTIONS 1 +//#define MPT_CHECKER_ASSUME_ASSERTIONS 0 + +#ifdef MPT_BUILD_ANALYZED + +#if MPT_COMPILER_MSVC + +#if MPT_CHECKER_ASSUME_ASSERTIONS +#define MPT_CHECKER_ASSUME(x) __analysis_assume(!!(x)) +#endif + +#elif MPT_COMPILER_CLANG + +#if MPT_CHECKER_ASSUME_ASSERTIONS +#ifdef NDEBUG +#error "Builds for static analyzers depend on assert() being enabled, but the current build has #define NDEBUG. This makes no sense." +#endif +OPENMPT_NAMESPACE_END +#include +OPENMPT_NAMESPACE_BEGIN +#define MPT_CHECKER_ASSUME(x) assert(!!(x)) +#endif + +#endif // MPT_COMPILER + +#endif // MPT_BUILD_ANALYZED + +#ifndef MPT_CHECKER_ASSUME +#define MPT_CHECKER_ASSUME(x) MPT_DO { } MPT_WHILE_0 +#endif + + + +#if defined(MPT_WITH_MFC) && !defined(MPT_CPPCHECK_CUSTOM) + +#if !defined(ASSERT) +#error "MFC is expected to #define ASSERT" +#endif // !defined(ASERRT) +#define MPT_FRAMEWORK_ASSERT_IS_DEFINED + +#if defined(_DEBUG) + #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 1 +#else // !_DEBUG + #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 0 +#endif // _DEBUG + +// let MFC handle our asserts +#define MPT_ASSERT_USE_FRAMEWORK 1 + +#else // !MPT_WITH_MFC + +#if defined(ASSERT) +#define MPT_FRAMEWORK_ASSERT_IS_DEFINED +#if defined(_DEBUG) + #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 1 +#else // !_DEBUG + #define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 0 +#endif // _DEBUG +#endif // !defined(ASERRT) + +// handle assert in our own way without relying on some platform-/framework-specific assert implementation +#define MPT_ASSERT_USE_FRAMEWORK 0 + +#endif // MPT_WITH_MFC + +#if defined(MPT_FRAMEWORK_ASSERT_IS_DEFINED) && (MPT_ASSERT_USE_FRAMEWORK == 1) + +#define MPT_ASSERT_NOTREACHED() ASSERT(0) +#define MPT_ASSERT(expr) ASSERT((expr)) +#define MPT_ASSERT_MSG(expr, msg) ASSERT((expr) && (msg)) +#if (MPT_FRAMEWORK_ASSERT_IS_ACTIVE == 1) +#define MPT_ASSERT_ALWAYS(expr) ASSERT((expr)) +#define MPT_ASSERT_ALWAYS_MSG(expr, msg) ASSERT((expr) && (msg)) +#else +#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#ifndef MPT_ASSERT_HANDLER_NEEDED +#define MPT_ASSERT_HANDLER_NEEDED +#endif +#endif + +#elif defined(NO_ASSERTS) + +#define MPT_ASSERT_NOTREACHED() MPT_CHECKER_ASSUME(0) +#define MPT_ASSERT(expr) MPT_CHECKER_ASSUME(expr) +#define MPT_ASSERT_MSG(expr, msg) MPT_CHECKER_ASSUME(expr) +#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#ifndef MPT_ASSERT_HANDLER_NEEDED +#define MPT_ASSERT_HANDLER_NEEDED +#endif + +#else // !NO_ASSERTS + +#define MPT_ASSERT_NOTREACHED() MPT_DO { if constexpr(!(0)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), "0"); } MPT_CHECKER_ASSUME(0); } MPT_WHILE_0 +#define MPT_ASSERT(expr) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 +#ifndef MPT_ASSERT_HANDLER_NEEDED +#define MPT_ASSERT_HANDLER_NEEDED +#endif + +#endif // NO_ASSERTS + + +#if defined(MPT_ASSERT_HANDLER_NEEDED) +// custom assert handler needed +MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *expr, const char *msg=nullptr); +#endif // MPT_ASSERT_HANDLER_NEEDED + + + +#define MPT_CONSTEXPR11_ASSERT static_assert +#define MPT_CONSTEXPR14_ASSERT static_assert +#define MPT_CONSTEXPR17_ASSERT static_assert + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptBaseMacros.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptBaseMacros.h new file mode 100644 index 000000000..211fdaac9 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptBaseMacros.h @@ -0,0 +1,272 @@ +/* + * mptBaseMacros.h + * --------------- + * Purpose: Basic assorted compiler-related helpers. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + + +#include +#include +#include + +#include +#include + +#include +#include + + + +OPENMPT_NAMESPACE_BEGIN + + + +#define MPT_PP_DEFER(m, ...) m(__VA_ARGS__) + +#define MPT_PP_STRINGIFY(x) #x + +#define MPT_PP_JOIN_HELPER(a, b) a ## b +#define MPT_PP_JOIN(a, b) MPT_PP_JOIN_HELPER(a, b) + +#define MPT_PP_UNIQUE_IDENTIFIER(prefix) MPT_PP_JOIN(prefix , __LINE__) + + + +#if MPT_COMPILER_MSVC + +#define MPT_WARNING(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text)) +#define MPT_WARNING_STATEMENT(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text)) + +#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG + +#define MPT_WARNING(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) +#define MPT_WARNING_STATEMENT(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) + +#else + +// portable #pragma message or #warning replacement +#define MPT_WARNING(text) \ + static inline int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) () noexcept { \ + int warning [[deprecated("Warning: " text)]] = 0; \ + return warning; \ + } \ +/**/ +#define MPT_WARNING_STATEMENT(text) \ + int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) = [](){ \ + int warning [[deprecated("Warning: " text)]] = 0; \ + return warning; \ + }() \ +/**/ + +#endif + + + +// Advanced inline attributes +#if MPT_COMPILER_MSVC +#define MPT_FORCEINLINE __forceinline +#define MPT_NOINLINE __declspec(noinline) +#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG +#define MPT_FORCEINLINE __attribute__((always_inline)) inline +#define MPT_NOINLINE __attribute__((noinline)) +#else +#define MPT_FORCEINLINE inline +#define MPT_NOINLINE +#endif + + + +// constexpr +#define MPT_CONSTEXPR11_FUN constexpr MPT_FORCEINLINE +#define MPT_CONSTEXPR14_FUN constexpr MPT_FORCEINLINE +#define MPT_CONSTEXPR17_FUN constexpr MPT_FORCEINLINE +#if MPT_CXX_AT_LEAST(20) +#define MPT_CONSTEXPR20_FUN constexpr MPT_FORCEINLINE +#define MPT_CONSTEXPR20_VAR constexpr +#else // !C++20 +#define MPT_CONSTEXPR20_FUN MPT_FORCEINLINE +#define MPT_CONSTEXPR20_VAR const +#endif // C++20 + + + +namespace mpt +{ +template struct constant_value { static constexpr decltype(V) value() { return V; } }; +#define MPT_FORCE_CONSTEXPR(expr) (mpt::constant_value<( expr )>::value()) +} // namespace mpt + + + +#if MPT_CXX_AT_LEAST(20) +#define MPT_IS_CONSTANT_EVALUATED20() std::is_constant_evaluated() +#define MPT_IS_CONSTANT_EVALUATED() std::is_constant_evaluated() +#else // !C++20 +#define MPT_IS_CONSTANT_EVALUATED20() false +// this pessimizes the case for C++17 by always assuming constexpr context, which implies always running constexpr-friendly code +#define MPT_IS_CONSTANT_EVALUATED() true +#endif // C++20 + + + +namespace mpt +{ + +template +struct stdarray_extent : std::integral_constant {}; + +template +struct stdarray_extent> : std::integral_constant {}; + +template +struct is_stdarray : std::false_type {}; + +template +struct is_stdarray> : std::true_type {}; + +// mpt::extent is the same as std::extent, +// but also works for std::array, +// and asserts that the given type is actually an array type instead of returning 0. +// use as: +// mpt::extent() +// mpt::extent() +// mpt::extent() +// mpt::extent() +template +constexpr std::size_t extent() noexcept +{ + using Tarray = typename std::remove_cv::type>::type; + static_assert(std::is_array::value || mpt::is_stdarray::value); + if constexpr(mpt::is_stdarray::value) + { + return mpt::stdarray_extent(); + } else + { + return std::extent(); + } +} + +} // namespace mpt + +// legacy +#if MPT_COMPILER_MSVC +OPENMPT_NAMESPACE_END +#include +#include +OPENMPT_NAMESPACE_BEGIN +#define MPT_ARRAY_COUNT(x) _countof(x) +#else +#define MPT_ARRAY_COUNT(x) (sizeof((x))/sizeof((x)[0])) +#endif +#define CountOf(x) MPT_ARRAY_COUNT(x) + + + +// Use MPT_RESTRICT to indicate that a pointer is guaranteed to not be aliased. +#if MPT_COMPILER_MSVC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG +#define MPT_RESTRICT __restrict +#else +#define MPT_RESTRICT +#endif + + + +#define MPT_ATTR_NODISCARD [[nodiscard]] +#define MPT_DISCARD(expr) static_cast(expr) + + + +#if MPT_COMPILER_MSVC +#define MPT_MAYBE_CONSTANT_IF(x) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + if(x) \ + __pragma(warning(pop)) \ +/**/ +#endif + +#if MPT_COMPILER_GCC +#define MPT_MAYBE_CONSTANT_IF(x) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \ + if(x) \ + _Pragma("GCC diagnostic pop") \ +/**/ +#endif + +#if MPT_COMPILER_CLANG +#define MPT_MAYBE_CONSTANT_IF(x) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") \ + _Pragma("clang diagnostic ignored \"-Wtype-limits\"") \ + _Pragma("clang diagnostic ignored \"-Wtautological-constant-out-of-range-compare\"") \ + if(x) \ + _Pragma("clang diagnostic pop") \ +/**/ +#endif + +#if !defined(MPT_MAYBE_CONSTANT_IF) +// MPT_MAYBE_CONSTANT_IF disables compiler warnings for conditions that may in some case be either always false or always true (this may turn out to be useful in ASSERTions in some cases). +#define MPT_MAYBE_CONSTANT_IF(x) if(x) +#endif + + + +#if MPT_COMPILER_MSVC +// MSVC warns for the well-known and widespread "do { } while(0)" idiom with warning level 4 ("conditional expression is constant"). +// It does not warn with "while(0,0)". However this again causes warnings with other compilers. +// Solve it with a macro. +#define MPT_DO do +#define MPT_WHILE_0 while(0,0) +#endif + +#ifndef MPT_DO +#define MPT_DO do +#endif +#ifndef MPT_WHILE_0 +#define MPT_WHILE_0 while(0) +#endif + + + +#if MPT_COMPILER_MSVC && defined(UNREFERENCED_PARAMETER) +#define MPT_UNREFERENCED_PARAMETER(x) UNREFERENCED_PARAMETER(x) +#else +#define MPT_UNREFERENCED_PARAMETER(x) (void)(x) +#endif + +#define MPT_UNUSED_VARIABLE(x) MPT_UNREFERENCED_PARAMETER(x) + + + +#if MPT_COMPILER_MSVC +// warning LNK4221: no public symbols found; archive member will be inaccessible +// There is no way to selectively disable linker warnings. +// #pragma warning does not apply and a command line option does not exist. +// Some options: +// 1. Macro which generates a variable with a unique name for each file (which means we have to pass the filename to the macro) +// 2. unnamed namespace containing any symbol (does not work for c++11 compilers because they actually have internal linkage now) +// 3. An unused trivial inline function. +// Option 3 does not actually solve the problem though, which leaves us with option 1. +// In any case, for optimized builds, the linker will just remove the useless symbol. +#define MPT_MSVC_WORKAROUND_LNK4221_CONCAT_DETAIL(x,y) x##y +#define MPT_MSVC_WORKAROUND_LNK4221_CONCAT(x,y) MPT_MSVC_WORKAROUND_LNK4221_CONCAT_DETAIL(x,y) +#define MPT_MSVC_WORKAROUND_LNK4221(x) int MPT_MSVC_WORKAROUND_LNK4221_CONCAT(mpt_msvc_workaround_lnk4221_,x) = 0; +#endif + +#ifndef MPT_MSVC_WORKAROUND_LNK4221 +#define MPT_MSVC_WORKAROUND_LNK4221(x) +#endif + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptBaseTypes.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptBaseTypes.h new file mode 100644 index 000000000..6e2f13bb5 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptBaseTypes.h @@ -0,0 +1,290 @@ +/* + * mptBaseTypes.h + * -------------- + * Purpose: Basic data type definitions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + + +#include "mptBaseMacros.h" + +#include +#include +#if MPT_CXX_AT_LEAST(20) +#include +#endif // C++20 + +#include +#include + +#include + + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt +{ +template +struct select_type +{ +}; +template +struct select_type +{ + using type = Ta; +}; +template +struct select_type +{ + using type = Tb; +}; +} // namespace mpt + + + +using int8 = std::int8_t; +using int16 = std::int16_t; +using int32 = std::int32_t; +using int64 = std::int64_t; +using uint8 = std::uint8_t; +using uint16 = std::uint16_t; +using uint32 = std::uint32_t; +using uint64 = std::uint64_t; + +constexpr int8 int8_min = std::numeric_limits::min(); +constexpr int16 int16_min = std::numeric_limits::min(); +constexpr int32 int32_min = std::numeric_limits::min(); +constexpr int64 int64_min = std::numeric_limits::min(); + +constexpr int8 int8_max = std::numeric_limits::max(); +constexpr int16 int16_max = std::numeric_limits::max(); +constexpr int32 int32_max = std::numeric_limits::max(); +constexpr int64 int64_max = std::numeric_limits::max(); + +constexpr uint8 uint8_max = std::numeric_limits::max(); +constexpr uint16 uint16_max = std::numeric_limits::max(); +constexpr uint32 uint32_max = std::numeric_limits::max(); +constexpr uint64 uint64_max = std::numeric_limits::max(); + + + +// fp half +// n/a + +// fp single +using single = float; +constexpr single operator"" _fs(long double lit) +{ + return static_cast(lit); +} + +// fp double +constexpr double operator"" _fd(long double lit) +{ + return static_cast(lit); +} + +// fp extended +constexpr long double operator"" _fe(long double lit) +{ + return static_cast(lit); +} + +// fp quad +// n/a + +using float32 = mpt::select_type::type + >::type + >::type; +constexpr float32 operator"" _f32(long double lit) +{ + return static_cast(lit); +} + +using float64 = mpt::select_type::type + >::type + >::type; +constexpr float64 operator"" _f64(long double lit) +{ + return static_cast(lit); +} + +namespace mpt +{ +template +struct float_traits +{ + static constexpr bool is_float = !std::numeric_limits::is_integer; + static constexpr bool is_hard = is_float && !MPT_COMPILER_QUIRK_FLOAT_EMULATED; + static constexpr bool is_soft = is_float && MPT_COMPILER_QUIRK_FLOAT_EMULATED; + static constexpr bool is_float32 = is_float && (sizeof(T) == 4); + static constexpr bool is_float64 = is_float && (sizeof(T) == 8); + static constexpr bool is_native_endian = is_float && !MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN; + static constexpr bool is_ieee754_binary = is_float && std::numeric_limits::is_iec559 && !MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754; + static constexpr bool is_ieee754_binary32 = is_float && is_ieee754_binary && is_float32; + static constexpr bool is_ieee754_binary64 = is_float && is_ieee754_binary && is_float64; + static constexpr bool is_ieee754_binary32ne = is_float && is_ieee754_binary && is_float32 && is_native_endian; + static constexpr bool is_ieee754_binary64ne = is_float && is_ieee754_binary && is_float64 && is_native_endian; +}; +} // namespace mpt + +#if MPT_COMPILER_QUIRK_FLOAT_PREFER32 +using nativefloat = float32; +#elif MPT_COMPILER_QUIRK_FLOAT_PREFER64 +using nativefloat = float64; +#else +// prefer smaller floats, but try to use IEEE754 floats +using nativefloat = mpt::select_type::is_iec559, + float + , + mpt::select_type::is_iec559, + double + , + mpt::select_type::is_iec559, + long double + , + float + >::type + >::type + >::type; +#endif +constexpr nativefloat operator"" _nf(long double lit) +{ + return static_cast(lit); +} + + + +static_assert(sizeof(std::uintptr_t) == sizeof(void*)); + + + +static_assert(std::numeric_limits::digits == 8); + +static_assert(sizeof(char) == 1); + +static_assert(sizeof(std::byte) == 1); +static_assert(alignof(std::byte) == 1); + + +namespace mpt { +inline constexpr int arch_bits = sizeof(void*) * 8; +inline constexpr std::size_t pointer_size = sizeof(void*); +} // namespace mpt + +static_assert(mpt::arch_bits == static_cast(mpt::pointer_size) * 8); + + + +namespace mpt { + +template +struct limits +{ + static constexpr typename std::remove_cv::type min() noexcept { return std::numeric_limits::type>::min(); } + static constexpr typename std::remove_cv::type max() noexcept { return std::numeric_limits::type>::max(); } +}; + +} // namespace mpt + + + +namespace mpt +{ + +#if MPT_CXX_AT_LEAST(20) + +using std::source_location; + +#define MPT_SOURCE_LOCATION_CURRENT() std::source_location::current() + +#else // !C++20 + +// compatible with std::experimental::source_location from Library Fundamentals TS v2. +struct source_location +{ +private: + const char* m_file_name; + const char* m_function_name; + uint32 m_line; + uint32 m_column; +public: + constexpr source_location() noexcept + : m_file_name("") + , m_function_name("") + , m_line(0) + , m_column(0) + { + } + constexpr source_location(const char* file, const char* function, uint32 line, uint32 column) noexcept + : m_file_name(file) + , m_function_name(function) + , m_line(line) + , m_column(column) + { + } + source_location(const source_location&) = default; + source_location(source_location&&) = default; + //static constexpr current() noexcept; // use MPT_SOURCE_LOCATION_CURRENT() + static constexpr source_location current(const char* file, const char* function, uint32 line, uint32 column) noexcept + { + return source_location(file, function, line, column); + } + constexpr uint32 line() const noexcept + { + return m_line; + } + constexpr uint32 column() const noexcept + { + return m_column; + } + constexpr const char* file_name() const noexcept + { + return m_file_name; + } + constexpr const char* function_name() const noexcept + { + return m_function_name; + } +}; + +#define MPT_SOURCE_LOCATION_CURRENT() mpt::source_location::current( __FILE__ , __func__ , __LINE__ , 0 ) + +#endif // C++20 + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptBaseUtils.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptBaseUtils.h new file mode 100644 index 000000000..a5f37a31b --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptBaseUtils.h @@ -0,0 +1,680 @@ +/* + * mptBaseUtils.h + * -------------- + * Purpose: Various useful utility functions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + +#include "mptBaseMacros.h" +#include "mptBaseTypes.h" + +#include +#if MPT_CXX_AT_LEAST(20) +#include +#endif +#include +#include +#include + +#include +#include + +#include +#include + +#if MPT_COMPILER_MSVC +#include +#endif + + + +OPENMPT_NAMESPACE_BEGIN + + + +// cmath fixups +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 +#endif +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif + + + +namespace mpt +{ +template +MPT_CONSTEXPR14_FUN std::array init_array(const Tx & x) +{ + std::array result{}; + for(std::size_t i = 0; i < N; ++i) + { + result[i] = x; + } + return result; +} +} // namespace mpt + + + +namespace mpt +{ + +// Work-around for the requirement of at least 1 non-throwing function argument combination in C++ (17,2a). + +template +MPT_CONSTEXPR14_FUN bool constexpr_throw_helper(Exception && e, bool really = true) +{ + //return !really ? really : throw std::forward(e); + if(really) + { + throw std::forward(e); + } + // cppcheck-suppress identicalConditionAfterEarlyExit + return really; +} +template +MPT_CONSTEXPR14_FUN bool constexpr_throw(Exception && e) +{ + return mpt::constexpr_throw_helper(std::forward(e)); +} + +template +constexpr T constexpr_throw_helper(Exception && e, bool really = true) +{ + //return !really ? really : throw std::forward(e); + if(really) + { + throw std::forward(e); + } + return T{}; +} +template +constexpr T constexpr_throw(Exception && e) +{ + return mpt::constexpr_throw_helper(std::forward(e)); +} + +} // namespace mpt + + + +namespace mpt { + +// Modulo with more intuitive behaviour for some contexts: +// Instead of being symmetrical around 0, the pattern for positive numbers is repeated in the negative range. +// For example, wrapping_modulo(-1, m) == (m - 1). +// Behaviour is undefined if m<=0. +template +MPT_CONSTEXPR11_FUN auto wrapping_modulo(T x, M m) -> decltype(x % m) +{ + return (x >= 0) ? (x % m) : (m - 1 - ((-1 - x) % m)); +} + +template +MPT_CONSTEXPR11_FUN auto wrapping_divide(T x, D d) -> decltype(x / d) +{ + return (x >= 0) ? (x / d) : (((x + 1) / d) - 1); +} + +} // namespace mpt + + + +namespace mpt { + + + +// Saturate the value of src to the domain of Tdst +template +inline Tdst saturate_cast(Tsrc src) +{ + // This code tries not only to obviously avoid overflows but also to avoid signed/unsigned comparison warnings and type truncation warnings (which in fact would be safe here) by explicit casting. + static_assert(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); + if constexpr(std::numeric_limits::is_signed && std::numeric_limits::is_signed) + { + if constexpr(sizeof(Tdst) >= sizeof(Tsrc)) + { + return static_cast(src); + } else + { + return static_cast(std::max(static_cast(std::numeric_limits::min()), std::min(src, static_cast(std::numeric_limits::max())))); + } + } else if constexpr(!std::numeric_limits::is_signed && !std::numeric_limits::is_signed) + { + if constexpr(sizeof(Tdst) >= sizeof(Tsrc)) + { + return static_cast(src); + } else + { + return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); + } + } else if constexpr(std::numeric_limits::is_signed && !std::numeric_limits::is_signed) + { + if constexpr(sizeof(Tdst) > sizeof(Tsrc)) + { + return static_cast(src); + } else if constexpr(sizeof(Tdst) == sizeof(Tsrc)) + { + return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); + } else + { + return static_cast(std::min(src, static_cast(std::numeric_limits::max()))); + } + } else // Tdst unsigned, Tsrc signed + { + if constexpr(sizeof(Tdst) >= sizeof(Tsrc)) + { + return static_cast(std::max(static_cast(0), src)); + } else + { + return static_cast(std::max(static_cast(0), std::min(src, static_cast(std::numeric_limits::max())))); + } + } +} + +template +inline Tdst saturate_cast(double src) +{ + if(src >= static_cast(std::numeric_limits::max())) + { + return std::numeric_limits::max(); + } + if(src <= static_cast(std::numeric_limits::min())) + { + return std::numeric_limits::min(); + } + return static_cast(src); +} + +template +inline Tdst saturate_cast(float src) +{ + if(src >= static_cast(std::numeric_limits::max())) + { + return std::numeric_limits::max(); + } + if(src <= static_cast(std::numeric_limits::min())) + { + return std::numeric_limits::min(); + } + return static_cast(src); +} + + +#if MPT_CXX_AT_LEAST(20) + +using std::popcount; +using std::has_single_bit; +using std::bit_ceil; +using std::bit_floor; +using std::bit_width; +using std::rotl; +using std::rotr; + +#else + +// C++20 header. +// Note that we do not use SFINAE here but instead rely on static_assert. + +template +MPT_CONSTEXPR14_FUN int popcount(T val) noexcept +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + int result = 0; + while(val > 0) + { + if(val & 0x1) + { + result++; + } + val >>= 1; + } + return result; +} + +template +MPT_CONSTEXPR14_FUN bool has_single_bit(T x) noexcept +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + return mpt::popcount(x) == 1; +} + +template +MPT_CONSTEXPR14_FUN T bit_ceil(T x) noexcept +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + T result = 1; + while(result < x) + { + T newresult = result << 1; + if(newresult < result) + { + return 0; + } + result = newresult; + } + return result; +} + +template +MPT_CONSTEXPR14_FUN T bit_floor(T x) noexcept +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + if(x == 0) + { + return 0; + } + T result = 1; + do + { + T newresult = result << 1; + if(newresult < result) + { + return result; + } + result = newresult; + } while(result <= x); + return result >> 1; +} + +template +MPT_CONSTEXPR14_FUN T bit_width(T x) noexcept +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + T result = 0; + while(x > 0) + { + x >>= 1; + result += 1; + } + return result; +} + +namespace detail +{ + +template +MPT_CONSTEXPR14_FUN T rotl(T x, int r) noexcept +{ + auto N = std::numeric_limits::digits; + return (x >> (N - r)) | (x << r); +} + +template +MPT_CONSTEXPR14_FUN T rotr(T x, int r) noexcept +{ + auto N = std::numeric_limits::digits; + return (x << (N - r)) | (x >> r); +} + +} // namespace detail + +template +MPT_CONSTEXPR14_FUN T rotl(T x, int s) noexcept +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + auto N = std::numeric_limits::digits; + auto r = s % N; + return (s < 0) ? detail::rotr(x, -s) : ((x >> (N - r)) | (x << r)); +} + +template +MPT_CONSTEXPR14_FUN T rotr(T x, int s) noexcept +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + auto N = std::numeric_limits::digits; + auto r = s % N; + return (s < 0) ? detail::rotl(x, -s) : ((x << (N - r)) | (x >> r)); +} + +#endif + +} // namespace mpt + + +namespace Util +{ + +namespace detail +{ +template +struct ModIfNotZeroImpl +{ + template + inline Tval mod(Tval x) + { + static_assert(std::numeric_limits::is_integer); + static_assert(!std::numeric_limits::is_signed); + static_assert(std::numeric_limits::is_integer); + static_assert(!std::numeric_limits::is_signed); + return static_cast(x % m); + } +}; +template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; +template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; +template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; +template <> struct ModIfNotZeroImpl { template inline Tval mod(Tval x) { return x; } }; +} // namespace detail +// Returns x % m if m != 0, x otherwise. +// i.e. "return (m == 0) ? x : (x % m);", but without causing a warning with stupid older compilers +template +inline Tval ModIfNotZero(Tval x) +{ + return detail::ModIfNotZeroImpl().mod(x); +} + +// Returns true iff Tdst can represent the value val. +// Use as if(Util::TypeCanHoldValue(-1)). +template +inline bool TypeCanHoldValue(Tsrc val) +{ + return (static_cast(mpt::saturate_cast(val)) == val); +} + +// Grows x with an exponential factor suitable for increasing buffer sizes. +// Clamps the result at limit. +// And avoids integer overflows while doing its business. +// The growth factor is 1.5, rounding down, execpt for the initial x==1 case. +template +inline T ExponentialGrow(const T &x, const Tlimit &limit) +{ + MPT_ASSERT(x > 0); + MPT_ASSERT(limit > 0); + if(x == 1) + { + return 2; + } + T add = std::min(x >> 1, std::numeric_limits::max() - x); + return std::min(x + add, mpt::saturate_cast(limit)); +} + +template +inline T ExponentialGrow(const T &x) +{ + return Util::ExponentialGrow(x, std::numeric_limits::max()); +} + +} //namespace Util + + +// Limits 'val' to given range. If 'val' is less than 'lowerLimit', 'val' is set to value 'lowerLimit'. +// Similarly if 'val' is greater than 'upperLimit', 'val' is set to value 'upperLimit'. +// If 'lowerLimit' > 'upperLimit', 'val' won't be modified. +template +inline void Limit(T& val, const C lowerLimit, const C upperLimit) +{ + if(lowerLimit > upperLimit) return; + if(val < lowerLimit) val = lowerLimit; + else if(val > upperLimit) val = upperLimit; +} + + +// Like Limit, but returns value +template +inline T Clamp(T val, const C lowerLimit, const C upperLimit) +{ + if(val < lowerLimit) return lowerLimit; + else if(val > upperLimit) return upperLimit; + else return val; +} + +// Check if val is in [lo,hi] without causing compiler warnings +// if theses checks are always true due to the domain of T. +// GCC does not warn if the type is templated. +template +inline bool IsInRange(T val, C lo, C hi) +{ + return lo <= val && val <= hi; +} + +// Like Limit, but with upperlimit only. +template +inline void LimitMax(T& val, const C upperLimit) +{ + if(val > upperLimit) + val = upperLimit; +} + + +// Returns sign of a number (-1 for negative numbers, 1 for positive numbers, 0 for 0) +template +int sgn(T value) +{ + return (value > T(0)) - (value < T(0)); +} + + + +// mpt::rshift_signed +// mpt::lshift_signed +// Shift a signed integer value in a well-defined manner. +// Does the same thing as MSVC would do. This is verified by the test suite. + +namespace mpt +{ + +template +MPT_FORCEINLINE auto rshift_signed_standard(T x, int y) -> decltype(x >> y) +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_signed); + typedef decltype(x >> y) result_type; + typedef typename std::make_unsigned::type unsigned_result_type; + const unsigned_result_type roffset = static_cast(1) << ((sizeof(result_type) * 8) - 1); + result_type rx = x; + unsigned_result_type urx = static_cast(rx); + urx += roffset; + urx >>= y; + urx -= roffset >> y; + return static_cast(urx); +} + +template +MPT_FORCEINLINE auto lshift_signed_standard(T x, int y) -> decltype(x << y) +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_signed); + typedef decltype(x << y) result_type; + typedef typename std::make_unsigned::type unsigned_result_type; + const unsigned_result_type roffset = static_cast(1) << ((sizeof(result_type) * 8) - 1); + result_type rx = x; + unsigned_result_type urx = static_cast(rx); + urx += roffset; + urx <<= y; + urx -= roffset << y; + return static_cast(urx); +} + +#if MPT_COMPILER_SHIFT_SIGNED + +template +MPT_FORCEINLINE auto rshift_signed_undefined(T x, int y) -> decltype(x >> y) +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_signed); + return x >> y; +} + +template +MPT_FORCEINLINE auto lshift_signed_undefined(T x, int y) -> decltype(x << y) +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_signed); + return x << y; +} + +template +MPT_FORCEINLINE auto rshift_signed(T x, int y) -> decltype(x >> y) +{ + return mpt::rshift_signed_undefined(x, y); +} + +template +MPT_FORCEINLINE auto lshift_signed(T x, int y) -> decltype(x << y) +{ + return mpt::lshift_signed_undefined(x, y); +} + +#else + +template +MPT_FORCEINLINE auto rshift_signed(T x, int y) -> decltype(x >> y) +{ + return mpt::rshift_signed_standard(x, y); +} + +template +MPT_FORCEINLINE auto lshift_signed(T x, int y) -> decltype(x << y) +{ + return mpt::lshift_signed_standard(x, y); +} + +#endif + +template +struct array_size; + +template +struct array_size> +{ + static constexpr std::size_t size = N; +}; + +template +struct array_size +{ + static constexpr std::size_t size = N; +}; + +} // namespace mpt + +namespace Util +{ + + // Returns maximum value of given integer type. + template constexpr T MaxValueOfType(const T&) {static_assert(std::numeric_limits::is_integer == true, "Only integer types are allowed."); return (std::numeric_limits::max)();} + +} // namespace Util + +namespace mpt +{ + +#if MPT_OS_DJGPP + + inline double round(const double& val) { return ::round(val); } + inline float round(const float& val) { return ::roundf(val); } + +#else // !MPT_OS_DJGPP + + // C++11 std::round + using std::round; + +#endif // MPT_OS_DJGPP + + + // Rounds given double value to nearest integer value of type T. + // Out-of-range values are saturated to the specified integer type's limits. + template inline T saturate_round(double val) + { + static_assert(std::numeric_limits::is_integer == true, "Type is a not an integer"); + return mpt::saturate_cast(mpt::round(val)); + } + + template inline T saturate_round(float val) + { + static_assert(std::numeric_limits::is_integer == true, "Type is a not an integer"); + return mpt::saturate_cast(mpt::round(val)); + } + +} + + +namespace Util { + + // Multiply two 32-bit integers, receive 64-bit result. + // MSVC generates unnecessarily complicated code for the unoptimized variant using _allmul. + MPT_FORCEINLINE int64 mul32to64(int32 a, int32 b) + { +#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64)) + return __emul(a, b); +#else + return static_cast(a) * b; +#endif + } + + MPT_FORCEINLINE uint64 mul32to64_unsigned(uint32 a, uint32 b) + { +#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64)) + return __emulu(a, b); +#else + return static_cast(a) * b; +#endif + } + + MPT_FORCEINLINE int32 muldiv(int32 a, int32 b, int32 c) + { + return mpt::saturate_cast( mul32to64( a, b ) / c ); + } + + MPT_FORCEINLINE int32 muldivr(int32 a, int32 b, int32 c) + { + return mpt::saturate_cast( ( mul32to64( a, b ) + ( c / 2 ) ) / c ); + } + + // Do not use overloading because catching unsigned version by accident results in slower X86 code. + MPT_FORCEINLINE uint32 muldiv_unsigned(uint32 a, uint32 b, uint32 c) + { + return mpt::saturate_cast( mul32to64_unsigned( a, b ) / c ); + } + MPT_FORCEINLINE uint32 muldivr_unsigned(uint32 a, uint32 b, uint32 c) + { + return mpt::saturate_cast( ( mul32to64_unsigned( a, b ) + ( c / 2u ) ) / c ); + } + + MPT_FORCEINLINE int32 muldivrfloor(int64 a, uint32 b, uint32 c) + { + a *= b; + a += c / 2u; + return (a >= 0) ? mpt::saturate_cast(a / c) : mpt::saturate_cast((a - (c - 1)) / c); + } + + // rounds x up to multiples of target + template + inline T AlignUp(T x, T target) + { + return ((x + (target - 1)) / target) * target; + } + + // rounds x down to multiples of target + template + inline T AlignDown(T x, T target) + { + return (x / target) * target; + } + +} // namespace Util + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptCPU.cpp similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp rename to Frameworks/OpenMPT.old/OpenMPT/common/mptCPU.cpp diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptCPU.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptCPU.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptCPU.h diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptCRC.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptCRC.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptCRC.h diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptException.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptException.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptException.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptException.h diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptExceptionText.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptExceptionText.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptExceptionText.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptExceptionText.h diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptFileIO.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptFileIO.cpp new file mode 100644 index 000000000..c5119f6fb --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptFileIO.cpp @@ -0,0 +1,449 @@ +/* + * mptFileIO.cpp + * ------------- + * Purpose: File I/O wrappers + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "mptFileIO.h" + +#ifdef MODPLUG_TRACKER +#if MPT_OS_WINDOWS +#include +#include +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + +#if defined(MPT_ENABLE_FILEIO) +#if MPT_COMPILER_MSVC +#include +#endif // MPT_COMPILER_MSVC +#endif // MPT_ENABLE_FILEIO + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MPT_ENABLE_FILEIO) + + + +#ifdef MODPLUG_TRACKER +#if MPT_OS_WINDOWS +bool SetFilesystemCompression(HANDLE hFile) +{ + if(hFile == INVALID_HANDLE_VALUE) + { + return false; + } + USHORT format = COMPRESSION_FORMAT_DEFAULT; + DWORD dummy = 0; + BOOL result = DeviceIoControl(hFile, FSCTL_SET_COMPRESSION, (LPVOID)&format, sizeof(format), NULL, 0, &dummy /*required*/ , NULL); + return result != FALSE; +} +bool SetFilesystemCompression(int fd) +{ + if(fd < 0) + { + return false; + } + uintptr_t fhandle = _get_osfhandle(fd); + HANDLE hFile = (HANDLE)fhandle; + if(hFile == INVALID_HANDLE_VALUE) + { + return false; + } + return SetFilesystemCompression(hFile); +} +bool SetFilesystemCompression(const mpt::PathString &filename) +{ + DWORD attributes = GetFileAttributes(filename.AsNativePrefixed().c_str()); + if(attributes == INVALID_FILE_ATTRIBUTES) + { + return false; + } + if(attributes & FILE_ATTRIBUTE_COMPRESSED) + { + return true; + } + HANDLE hFile = CreateFile(filename.AsNativePrefixed().c_str(), GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if(hFile == INVALID_HANDLE_VALUE) + { + return false; + } + bool result = SetFilesystemCompression(hFile); + CloseHandle(hFile); + return result; +} +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + + + +#ifdef MODPLUG_TRACKER + +namespace mpt { + +#if MPT_COMPILER_MSVC + +mpt::tstring SafeOutputFile::convert_mode(std::ios_base::openmode mode, FlushMode flushMode) +{ + mpt::tstring fopen_mode; + switch(mode & ~(std::ios_base::ate | std::ios_base::binary)) + { + case std::ios_base::in: + fopen_mode = _T("r"); + break; + case std::ios_base::out: + [[fallthrough]]; + case std::ios_base::out | std::ios_base::trunc: + fopen_mode = _T("w"); + break; + case std::ios_base::app: + [[fallthrough]]; + case std::ios_base::out | std::ios_base::app: + fopen_mode = _T("a"); + break; + case std::ios_base::out | std::ios_base::in: + fopen_mode = _T("r+"); + break; + case std::ios_base::out | std::ios_base::in | std::ios_base::trunc: + fopen_mode = _T("w+"); + break; + case std::ios_base::out | std::ios_base::in | std::ios_base::app: + [[fallthrough]]; + case std::ios_base::in | std::ios_base::app: + fopen_mode = _T("a+"); + break; + } + if(fopen_mode.empty()) + { + return fopen_mode; + } + if(mode & std::ios_base::binary) + { + fopen_mode += _T("b"); + } + if(flushMode == FlushMode::Full) + { + fopen_mode += _T("c"); // force commit on fflush (MSVC specific) + } + return fopen_mode; +} + +FILE * SafeOutputFile::internal_fopen(const mpt::PathString &filename, std::ios_base::openmode mode, FlushMode flushMode) +{ + mpt::tstring fopen_mode = convert_mode(mode, flushMode); + if(fopen_mode.empty()) + { + return nullptr; + } + FILE *f = +#ifdef UNICODE + _wfopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str()) +#else + fopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str()) +#endif + ; + if(!f) + { + return nullptr; + } + if(mode & std::ios_base::ate) + { + if(fseek(f, 0, SEEK_END) != 0) + { + fclose(f); + f = nullptr; + return nullptr; + } + } + m_f = f; + return f; +} + +#endif // MPT_COMPILER_MSVC + +// cppcheck-suppress exceptThrowInDestructor +SafeOutputFile::~SafeOutputFile() noexcept(false) +{ + const bool mayThrow = (std::uncaught_exceptions() == 0); + if(!stream()) + { + #if MPT_COMPILER_MSVC + if(m_f) + { + fclose(m_f); + } + #endif // MPT_COMPILER_MSVC + return; + } + if(!stream().rdbuf()) + { + #if MPT_COMPILER_MSVC + if(m_f) + { + fclose(m_f); + } + #endif // MPT_COMPILER_MSVC + return; + } +#if MPT_COMPILER_MSVC + if(!m_f) + { + return; + } +#endif // MPT_COMPILER_MSVC + bool errorOnFlush = false; + if(m_FlushMode != FlushMode::None) + { + try + { + if(stream().rdbuf()->pubsync() != 0) + { + errorOnFlush = true; + } + } catch(const std::exception &) + { + errorOnFlush = true; +#if MPT_COMPILER_MSVC + if(m_FlushMode != FlushMode::None) + { + if(fflush(m_f) != 0) + { + errorOnFlush = true; + } + } + if(fclose(m_f) != 0) + { + errorOnFlush = true; + } +#endif // MPT_COMPILER_MSVC + if(mayThrow) + { + // ignore errorOnFlush here, and re-throw the earlier exception + // cppcheck-suppress exceptThrowInDestructor + throw; + } + } + } +#if MPT_COMPILER_MSVC + if(m_FlushMode != FlushMode::None) + { + if(fflush(m_f) != 0) + { + errorOnFlush = true; + } + } + if(fclose(m_f) != 0) + { + errorOnFlush = true; + } +#endif // MPT_COMPILER_MSVC + if(mayThrow && errorOnFlush && (stream().exceptions() & (std::ios::badbit | std::ios::failbit))) + { + // cppcheck-suppress exceptThrowInDestructor + throw std::ios_base::failure(std::string("Error flushing file buffers.")); + } +} + +} // namespace mpt + +#endif // MODPLUG_TRACKER + + + +#ifdef MODPLUG_TRACKER + +namespace mpt { + +LazyFileRef & LazyFileRef::operator = (const std::vector &data) +{ + mpt::ofstream file(m_Filename, std::ios::binary); + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::WriteRaw(file, mpt::as_span(data)); + mpt::IO::Flush(file); + return *this; +} + +LazyFileRef & LazyFileRef::operator = (const std::vector &data) +{ + mpt::ofstream file(m_Filename, std::ios::binary); + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::WriteRaw(file, mpt::as_span(data)); + mpt::IO::Flush(file); + return *this; +} + +LazyFileRef & LazyFileRef::operator = (const std::string &data) +{ + mpt::ofstream file(m_Filename, std::ios::binary); + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::WriteRaw(file, mpt::as_span(data)); + mpt::IO::Flush(file); + return *this; +} + +LazyFileRef::operator std::vector () const +{ + mpt::ifstream file(m_Filename, std::ios::binary); + if(!mpt::IO::IsValid(file)) + { + return std::vector(); + } + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::SeekEnd(file); + std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); + mpt::IO::SeekBegin(file); + mpt::IO::ReadRaw(file, mpt::as_span(buf)); + return buf; +} + +LazyFileRef::operator std::vector () const +{ + mpt::ifstream file(m_Filename, std::ios::binary); + if(!mpt::IO::IsValid(file)) + { + return std::vector(); + } + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::SeekEnd(file); + std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); + mpt::IO::SeekBegin(file); + mpt::IO::ReadRaw(file, mpt::as_span(buf)); + return buf; +} + +LazyFileRef::operator std::string () const +{ + mpt::ifstream file(m_Filename, std::ios::binary); + if(!mpt::IO::IsValid(file)) + { + return std::string(); + } + file.exceptions(std::ios_base::failbit | std::ios_base::badbit); + mpt::IO::SeekEnd(file); + std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); + mpt::IO::SeekBegin(file); + mpt::IO::ReadRaw(file, mpt::as_span(buf)); + return std::string(buf.begin(), buf.end()); +} + +} // namespace mpt + +#endif // MODPLUG_TRACKER + + +bool InputFile::DefaultToLargeAddressSpaceUsage() +{ + return false; +} + + +InputFile::InputFile() + : m_IsCached(false) +{ + return; +} + +InputFile::InputFile(const mpt::PathString &filename, bool allowWholeFileCaching) + : m_IsCached(false) +{ + Open(filename, allowWholeFileCaching); +} + +InputFile::~InputFile() +{ + return; +} + + +bool InputFile::Open(const mpt::PathString &filename, bool allowWholeFileCaching) +{ + m_IsCached = false; + m_Cache.resize(0); + m_Cache.shrink_to_fit(); + m_Filename = filename; + m_File.open(m_Filename, std::ios::binary | std::ios::in); + if(allowWholeFileCaching) + { + if(mpt::IO::IsReadSeekable(m_File)) + { + if(!mpt::IO::SeekEnd(m_File)) + { + m_File.close(); + return false; + } + mpt::IO::Offset filesize = mpt::IO::TellRead(m_File); + if(!mpt::IO::SeekBegin(m_File)) + { + m_File.close(); + return false; + } + if(Util::TypeCanHoldValue(filesize)) + { + std::size_t buffersize = mpt::saturate_cast(filesize); + m_Cache.resize(buffersize); + if(mpt::IO::ReadRaw(m_File, mpt::as_span(m_Cache)) != filesize) + { + m_File.close(); + return false; + } + if(!mpt::IO::SeekBegin(m_File)) + { + m_File.close(); + return false; + } + m_IsCached = true; + return true; + } + } + } + return m_File.good(); +} + + +bool InputFile::IsValid() const +{ + return m_File.good(); +} + + +bool InputFile::IsCached() const +{ + return m_IsCached; +} + + +const mpt::PathString& InputFile::GetFilenameRef() const +{ + return m_Filename; +} + + +std::istream* InputFile::GetStream() +{ + MPT_ASSERT(!m_IsCached); + return &m_File; +} + + +mpt::const_byte_span InputFile::GetCache() +{ + MPT_ASSERT(m_IsCached); + return mpt::as_span(m_Cache); +} + + +#else // !MPT_ENABLE_FILEIO + +MPT_MSVC_WORKAROUND_LNK4221(mptFileIO) + +#endif // MPT_ENABLE_FILEIO + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptFileIO.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptFileIO.h new file mode 100644 index 000000000..19a2a342a --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptFileIO.h @@ -0,0 +1,291 @@ +/* + * mptFileIO.h + * ----------- + * Purpose: A wrapper around std::fstream, enforcing usage of mpt::PathString. + * Notes : You should only ever use these wrappers instead of plain std::fstream classes. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "BuildSettings.h" + +#if defined(MPT_ENABLE_FILEIO) + +#include "../common/mptString.h" +#include "../common/mptPathString.h" +#include "../common/mptIO.h" + +#include +#include +#include +#include +#include + +#if MPT_COMPILER_MSVC +#include +#endif // !MPT_COMPILER_MSVC + +#if MPT_COMPILER_MSVC +#include +#endif // !MPT_COMPILER_MSVC + +#endif // MPT_ENABLE_FILEIO + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MPT_ENABLE_FILEIO) + + +// Sets the NTFS compression attribute on the file or directory. +// Requires read and write permissions for already opened files. +// Returns true if the attribute has been set. +// In almost all cases, the return value should be ignored because most filesystems other than NTFS do not support compression. +#ifdef MODPLUG_TRACKER +#if MPT_OS_WINDOWS +bool SetFilesystemCompression(HANDLE hFile); +bool SetFilesystemCompression(int fd); +bool SetFilesystemCompression(const mpt::PathString &filename); +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + + +namespace mpt +{ + +#if MPT_COMPILER_GCC && MPT_OS_WINDOWS +// GCC C++ library has no wchar_t overloads +#define MPT_FSTREAM_DO_CONVERSIONS_ANSI +#endif + +namespace detail +{ + +template +inline void fstream_open(Tbase & base, const mpt::PathString & filename, std::ios_base::openmode mode) +{ +#if defined(MPT_FSTREAM_DO_CONVERSIONS_ANSI) + base.open(mpt::ToCharset(mpt::Charset::Locale, filename.AsNative()).c_str(), mode); +#else + base.open(filename.AsNativePrefixed().c_str(), mode); +#endif +} + +} // namespace detail + +class SafeOutputFile; + +class fstream + : public std::fstream +{ +private: + typedef std::fstream Tbase; +public: + friend SafeOutputFile; +public: + fstream() {} + fstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + { + detail::fstream_open(*this, filename, mode); + } + void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + { + detail::fstream_open(*this, filename, mode); + } + void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; + void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; +#if MPT_OS_WINDOWS + void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; + void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete; +#endif +}; + +class ifstream + : public std::ifstream +{ +private: + typedef std::ifstream Tbase; +public: + friend SafeOutputFile; +public: + ifstream() {} + ifstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in) + { + detail::fstream_open(*this, filename, mode); + } + void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in) + { + detail::fstream_open(*this, filename, mode); + } + void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in) = delete; + void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in) = delete; +#if MPT_OS_WINDOWS + void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in) = delete; + void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in) = delete; +#endif +}; + +class ofstream + : public std::ofstream +{ +private: + typedef std::ofstream Tbase; +public: + friend SafeOutputFile; +public: + ofstream() {} + ofstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out) + { + detail::fstream_open(*this, filename, mode); + } +#if MPT_COMPILER_MSVC +protected: + ofstream(FILE * file) + : std::ofstream(file) + { + } + +#endif // MPT_COMPILER_MSVC +public: + void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out) + { + detail::fstream_open(*this, filename, mode); + } + void open(const char * filename, std::ios_base::openmode mode = std::ios_base::out) = delete; + void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::out) = delete; +#if MPT_OS_WINDOWS + void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::out) = delete; + void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::out) = delete; +#endif +}; + +enum class FlushMode +{ + None = 0, // no explicit flushes at all + Single = 1, // explicitly flush higher-leverl API layers + Full = 2, // explicitly flush *all* layers, up to and including disk write caches +}; + +inline FlushMode FlushModeFromBool(bool flush) +{ + return flush ? FlushMode::Full : FlushMode::None; +} + +#ifdef MODPLUG_TRACKER + +class SafeOutputFile +{ +private: + FlushMode m_FlushMode; +#if MPT_COMPILER_MSVC + FILE *m_f = nullptr; +#endif // MPT_COMPILER_MSVC + mpt::ofstream m_s; +#if MPT_COMPILER_MSVC + static mpt::tstring convert_mode(std::ios_base::openmode mode, FlushMode flushMode); + FILE * internal_fopen(const mpt::PathString &filename, std::ios_base::openmode mode, FlushMode flushMode); +#endif // MPT_COMPILER_MSVC +public: + SafeOutputFile() = delete; + explicit SafeOutputFile(const mpt::PathString &filename, std::ios_base::openmode mode = std::ios_base::out, FlushMode flushMode = FlushMode::Full) + : m_FlushMode(flushMode) +#if MPT_COMPILER_MSVC + , m_s(internal_fopen(filename, mode | std::ios_base::out, flushMode)) +#else // !MPT_COMPILER_MSVC + , m_s(filename, mode) +#endif // MPT_COMPILER_MSVC + { + if(!stream().is_open()) + stream().setstate(mpt::ofstream::failbit); + } + mpt::ofstream& stream() + { + return m_s; + } + operator mpt::ofstream& () + { + return stream(); + } + const mpt::ofstream& stream() const + { + return m_s; + } + operator const mpt::ofstream& () const + { + return stream(); + } + operator bool() const + { + return stream() ? true : false; + } + bool operator!() const + { + return stream().operator!(); + } + ~SafeOutputFile() noexcept(false); +}; + +#endif // MODPLUG_TRACKER + + + +#ifdef MODPLUG_TRACKER + +// LazyFileRef is a simple reference to an on-disk file by the means of a +// filename which allows easy assignment of the whole file contents to and from +// byte buffers. +class LazyFileRef { +private: + const mpt::PathString m_Filename; +public: + LazyFileRef(const mpt::PathString &filename) + : m_Filename(filename) + { + return; + } +public: + LazyFileRef & operator = (const std::vector &data); + LazyFileRef & operator = (const std::vector &data); + LazyFileRef & operator = (const std::string &data); + operator std::vector () const; + operator std::vector () const; + operator std::string () const; +}; + +#endif // MODPLUG_TRACKER + + +} // namespace mpt + + +class InputFile +{ +private: + mpt::PathString m_Filename; + mpt::ifstream m_File; + bool m_IsCached; + std::vector m_Cache; +public: + static bool DefaultToLargeAddressSpaceUsage(); +public: + InputFile(); + InputFile(const mpt::PathString &filename, bool allowWholeFileCaching = DefaultToLargeAddressSpaceUsage()); + ~InputFile(); + bool Open(const mpt::PathString &filename, bool allowWholeFileCaching = DefaultToLargeAddressSpaceUsage()); + bool IsValid() const; + bool IsCached() const; + const mpt::PathString& GetFilenameRef() const; + std::istream* GetStream(); + mpt::const_byte_span GetCache(); +}; + + +#endif // MPT_ENABLE_FILEIO + + + +OPENMPT_NAMESPACE_END + diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptIO.cpp similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp rename to Frameworks/OpenMPT.old/OpenMPT/common/mptIO.cpp diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptIO.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptIO.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptIO.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptIO.h diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptLibrary.cpp similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp rename to Frameworks/OpenMPT.old/OpenMPT/common/mptLibrary.cpp diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptLibrary.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptLibrary.h diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptMemory.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptMemory.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptMemory.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptMemory.h diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptMutex.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptMutex.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptMutex.h diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptOS.cpp similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp rename to Frameworks/OpenMPT.old/OpenMPT/common/mptOS.cpp diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOS.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptOS.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptOS.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptOS.h diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOSError.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptOSError.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptOSError.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptOSError.h diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOSException.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptOSException.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptOSException.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptOSException.h diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptPathString.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptPathString.cpp new file mode 100644 index 000000000..3361c5440 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptPathString.cpp @@ -0,0 +1,867 @@ +/* + * mptPathString.cpp + * ----------------- + * Purpose: Wrapper class around the platform-native representation of path names. Should be the only type that is used to store path names. + * Notes : Currently none. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "mptPathString.h" + +#include "misc_util.h" + +#include "mptUUID.h" + +#if MPT_OS_WINDOWS +#include +#if defined(MODPLUG_TRACKER) +#include +#endif +#include +#endif + +OPENMPT_NAMESPACE_BEGIN + +#if MPT_OS_WINDOWS + +namespace mpt +{ + + +RawPathString PathString::AsNativePrefixed() const +{ +#if MPT_OS_WINDOWS_WINRT && (_WIN32_WINNT < 0x0a00) + // For WinRT on Windows 8, there is no official wy to determine an absolute path. + return path; +#else + if(path.length() < MAX_PATH || path.substr(0, 4) == PL_("\\\\?\\")) + { + // Path is short enough or already in prefixed form + return path; + } + const RawPathString absPath = mpt::GetAbsolutePath(*this).AsNative(); + if(absPath.substr(0, 2) == PL_("\\\\")) + { + // Path is a network share: \\server\foo.bar -> \\?\UNC\server\foo.bar + return PL_("\\\\?\\UNC") + absPath.substr(1); + } else + { + // Regular file: C:\foo.bar -> \\?\C:\foo.bar + return PL_("\\\\?\\") + absPath; + } +#endif +} + + +#if !MPT_OS_WINDOWS_WINRT + +int PathString::CompareNoCase(const PathString & a, const PathString & b) +{ + return lstrcmpi(a.path.c_str(), b.path.c_str()); +} + +#endif // !MPT_OS_WINDOWS_WINRT + + +// Convert a path to its simplified form, i.e. remove ".\" and "..\" entries +// Note: We use our own implementation as PathCanonicalize is limited to MAX_PATH +// and unlimited versions are only available on Windows 8 and later. +// Furthermore, we also convert forward-slashes to backslashes and always remove trailing slashes. +PathString PathString::Simplify() const +{ + if(path.empty()) + return PathString(); + + std::vector components; + RawPathString root; + RawPathString::size_type startPos = 0; + if(path.size() >= 2 && path[1] == PC_(':')) + { + // Drive letter + root = path.substr(0, 2) + PC_('\\'); + startPos = 2; + } else if(path.substr(0, 2) == PL_("\\\\")) + { + // Network share + root = PL_("\\\\"); + startPos = 2; + } else if(path.substr(0, 2) == PL_(".\\") || path.substr(0, 2) == PL_("./")) + { + // Special case for relative paths + root = PL_(".\\"); + startPos = 2; + } else if(path.size() >= 1 && (path[0] == PC_('\\') || path[0] == PC_('/'))) + { + // Special case for relative paths + root = PL_("\\"); + startPos = 1; + } + + while(startPos < path.size()) + { + auto pos = path.find_first_of(PL_("\\/"), startPos); + if(pos == RawPathString::npos) + pos = path.size(); + mpt::RawPathString dir = path.substr(startPos, pos - startPos); + if(dir == PL_("..")) + { + // Go back one directory + if(!components.empty()) + { + components.pop_back(); + } + } else if(dir == PL_(".")) + { + // nop + } else if(!dir.empty()) + { + components.push_back(std::move(dir)); + } + startPos = pos + 1; + } + + RawPathString result = root; + result.reserve(path.size()); + for(const auto &component : components) + { + result += component + PL_("\\"); + } + if(!components.empty()) + result.pop_back(); + return mpt::PathString(result); +} + +} // namespace mpt + +#endif // MPT_OS_WINDOWS + + +namespace mpt +{ + + +#if MPT_OS_WINDOWS && (defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE)) + +void PathString::SplitPath(PathString *drive, PathString *dir, PathString *fname, PathString *ext) const +{ + // We cannot use CRT splitpath here, because: + // * limited to _MAX_PATH or similar + // * no support for UNC paths + // * no support for \\?\ prefixed paths + + if(drive) *drive = mpt::PathString(); + if(dir) *dir = mpt::PathString(); + if(fname) *fname = mpt::PathString(); + if(ext) *ext = mpt::PathString(); + + mpt::RawPathString p = path; + + // remove \\?\\ prefix + if(p.substr(0, 8) == PL_("\\\\?\\UNC\\")) + { + p = PL_("\\\\") + p.substr(8); + } else if(p.substr(0, 4) == PL_("\\\\?\\")) + { + p = p.substr(4); + } + + if (p.length() >= 2 && ( + p.substr(0, 2) == PL_("\\\\") + || p.substr(0, 2) == PL_("\\/") + || p.substr(0, 2) == PL_("/\\") + || p.substr(0, 2) == PL_("//") + )) + { // UNC + mpt::RawPathString::size_type first_slash = p.substr(2).find_first_of(PL_("\\/")); + if(first_slash != mpt::RawPathString::npos) + { + mpt::RawPathString::size_type second_slash = p.substr(2 + first_slash + 1).find_first_of(PL_("\\/")); + if(second_slash != mpt::RawPathString::npos) + { + if(drive) *drive = mpt::PathString::FromNative(p.substr(0, 2 + first_slash + 1 + second_slash)); + p = p.substr(2 + first_slash + 1 + second_slash); + } else + { + if(drive) *drive = mpt::PathString::FromNative(p); + p = mpt::RawPathString(); + } + } else + { + if(drive) *drive = mpt::PathString::FromNative(p); + p = mpt::RawPathString(); + } + } else + { // local + if(p.length() >= 2 && (p[1] == PC_(':'))) + { + if(drive) *drive = mpt::PathString::FromNative(p.substr(0, 2)); + p = p.substr(2); + } else + { + if(drive) *drive = mpt::PathString(); + } + } + mpt::RawPathString::size_type last_slash = p.find_last_of(PL_("\\/")); + if(last_slash != mpt::RawPathString::npos) + { + if(dir) *dir = mpt::PathString::FromNative(p.substr(0, last_slash + 1)); + p = p.substr(last_slash + 1); + } else + { + if(dir) *dir = mpt::PathString(); + } + mpt::RawPathString::size_type last_dot = p.find_last_of(PL_(".")); + if(last_dot == mpt::RawPathString::npos) + { + if(fname) *fname = mpt::PathString::FromNative(p); + if(ext) *ext = mpt::PathString(); + } else if(last_dot == 0) + { + if(fname) *fname = mpt::PathString::FromNative(p); + if(ext) *ext = mpt::PathString(); + } else if(p == PL_(".") || p == PL_("..")) + { + if(fname) *fname = mpt::PathString::FromNative(p); + if(ext) *ext = mpt::PathString(); + } else + { + if(fname) *fname = mpt::PathString::FromNative(p.substr(0, last_dot)); + if(ext) *ext = mpt::PathString::FromNative(p.substr(last_dot)); + } + +} + +PathString PathString::GetDrive() const +{ + PathString drive; + SplitPath(&drive, nullptr, nullptr, nullptr); + return drive; +} +PathString PathString::GetDir() const +{ + PathString dir; + SplitPath(nullptr, &dir, nullptr, nullptr); + return dir; +} +PathString PathString::GetPath() const +{ + PathString drive, dir; + SplitPath(&drive, &dir, nullptr, nullptr); + return drive + dir; +} +PathString PathString::GetFileName() const +{ + PathString fname; + SplitPath(nullptr, nullptr, &fname, nullptr); + return fname; +} +PathString PathString::GetFileExt() const +{ + PathString ext; + SplitPath(nullptr, nullptr, nullptr, &ext); + return ext; +} +PathString PathString::GetFullFileName() const +{ + PathString name, ext; + SplitPath(nullptr, nullptr, &name, &ext); + return name + ext; +} + + +bool PathString::IsDirectory() const +{ + // Using PathIsDirectoryW here instead would increase libopenmpt dependencies by shlwapi.dll. + // GetFileAttributesW also does the job just fine. + #if MPT_OS_WINDOWS_WINRT + WIN32_FILE_ATTRIBUTE_DATA data; + MemsetZero(data); + if(::GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &data) == 0) + { + return false; + } + DWORD dwAttrib = data.dwFileAttributes; + #else // !MPT_OS_WINDOWS_WINRT + DWORD dwAttrib = ::GetFileAttributes(path.c_str()); + #endif // MPT_OS_WINDOWS_WINRT + return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +} + +bool PathString::IsFile() const +{ + #if MPT_OS_WINDOWS_WINRT + WIN32_FILE_ATTRIBUTE_DATA data; + MemsetZero(data); + if (::GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &data) == 0) + { + return false; + } + DWORD dwAttrib = data.dwFileAttributes; + #else // !MPT_OS_WINDOWS_WINRT + DWORD dwAttrib = ::GetFileAttributes(path.c_str()); + #endif // MPT_OS_WINDOWS_WINRT + return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +} + +#endif // MPT_OS_WINDOWS && (MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE) + + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + +bool PathString::FileOrDirectoryExists() const +{ + return ::PathFileExists(path.c_str()) != FALSE; +} + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + +PathString PathString::ReplaceExt(const mpt::PathString &newExt) const +{ + return GetDrive() + GetDir() + GetFileName() + newExt; +} + + +PathString PathString::SanitizeComponent() const +{ + PathString result = *this; + SanitizeFilename(result); + return result; +} + + +// Convert an absolute path to a path that's relative to "&relativeTo". +PathString PathString::AbsolutePathToRelative(const PathString &relativeTo) const +{ + mpt::PathString result = *this; + if(path.empty()) + { + return result; + } + if(!_tcsncicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), relativeTo.AsNative().length())) + { + // Path is OpenMPT's directory or a sub directory ("C:\OpenMPT\Somepath" => ".\Somepath") + result = P_(".\\"); // ".\" + result += mpt::PathString::FromNative(AsNative().substr(relativeTo.AsNative().length())); + } else if(!_tcsncicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), 2)) + { + // Path is on the same drive as OpenMPT ("C:\Somepath" => "\Somepath") + result = mpt::PathString::FromNative(AsNative().substr(2)); + } + return result; +} + + +// Convert a path that is relative to "&relativeTo" to an absolute path. +PathString PathString::RelativePathToAbsolute(const PathString &relativeTo) const +{ + mpt::PathString result = *this; + if(path.empty()) + { + return result; + } + if(path.length() >= 2 && path.at(0) == PC_('\\') && path.at(1) != PC_('\\')) + { + // Path is on the same drive as OpenMPT ("\Somepath\" => "C:\Somepath\"), but ignore network paths starting with "\\" + result = mpt::PathString::FromNative(relativeTo.AsNative().substr(0, 2)); + result += mpt::PathString(path); + } else if(path.length() >= 2 && path.substr(0, 2) == PL_(".\\")) + { + // Path is OpenMPT's directory or a sub directory (".\Somepath\" => "C:\OpenMPT\Somepath\") + result = relativeTo; // "C:\OpenMPT\" + result += mpt::PathString::FromNative(AsNative().substr(2)); + } + return result; +} + + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + + +bool PathString::IsPathSeparator(RawPathString::value_type c) +{ +#if MPT_OS_WINDOWS + return (c == PC_('\\')) || (c == PC_('/')); +#else + return c == PC_('/'); +#endif +} + +RawPathString::value_type PathString::GetDefaultPathSeparator() +{ +#if MPT_OS_WINDOWS + return PC_('\\'); +#else + return PC_('/'); +#endif +} + + +} // namespace mpt + + +namespace mpt +{ + +bool PathIsAbsolute(const mpt::PathString &path) { + mpt::RawPathString rawpath = path.AsNative(); +#if MPT_OS_WINDOWS + if(rawpath.substr(0, 8) == PL_("\\\\?\\UNC\\")) + { + return true; + } + if(rawpath.substr(0, 4) == PL_("\\\\?\\")) + { + return true; + } + if(rawpath.substr(0, 2) == PL_("\\\\")) + { + return true; // UNC + } + if(rawpath.substr(0, 2) == PL_("//")) + { + return true; // UNC + } + return (rawpath.length()) >= 3 && (rawpath[1] == ':') && mpt::PathString::IsPathSeparator(rawpath[2]); +#else + return (rawpath.length() >= 1) && mpt::PathString::IsPathSeparator(rawpath[0]); +#endif +} + + +#if MPT_OS_WINDOWS + +#if !(MPT_OS_WINDOWS_WINRT && (_WIN32_WINNT < 0x0a00)) + +mpt::PathString GetAbsolutePath(const mpt::PathString &path) +{ + DWORD size = GetFullPathName(path.AsNative().c_str(), 0, nullptr, nullptr); + if(size == 0) + { + return path; + } + std::vector fullPathName(size, TEXT('\0')); + if(GetFullPathName(path.AsNative().c_str(), size, fullPathName.data(), nullptr) == 0) + { + return path; + } + return mpt::PathString::FromNative(fullPathName.data()); +} + +#endif + +#ifdef MODPLUG_TRACKER + +bool DeleteWholeDirectoryTree(mpt::PathString path) +{ + if(path.AsNative().empty()) + { + return false; + } + if(PathIsRelative(path.AsNative().c_str()) == TRUE) + { + return false; + } + if(!path.FileOrDirectoryExists()) + { + return true; + } + if(!path.IsDirectory()) + { + return false; + } + path.EnsureTrailingSlash(); + HANDLE hFind = NULL; + WIN32_FIND_DATA wfd; + MemsetZero(wfd); + hFind = FindFirstFile((path + P_("*.*")).AsNative().c_str(), &wfd); + if(hFind != NULL && hFind != INVALID_HANDLE_VALUE) + { + do + { + mpt::PathString filename = mpt::PathString::FromNative(wfd.cFileName); + if(filename != P_(".") && filename != P_("..")) + { + filename = path + filename; + if(filename.IsDirectory()) + { + if(!DeleteWholeDirectoryTree(filename)) + { + return false; + } + } else if(filename.IsFile()) + { + if(DeleteFile(filename.AsNative().c_str()) == 0) + { + return false; + } + } + } + } while(FindNextFile(hFind, &wfd)); + FindClose(hFind); + } + if(RemoveDirectory(path.AsNative().c_str()) == 0) + { + return false; + } + return true; +} + +#endif // MODPLUG_TRACKER + +#endif // MPT_OS_WINDOWS + + + +#if MPT_OS_WINDOWS + +#if defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE) + +mpt::PathString GetExecutablePath() +{ + std::vector exeFileName(MAX_PATH); + while(GetModuleFileName(0, exeFileName.data(), mpt::saturate_cast(exeFileName.size())) >= exeFileName.size()) + { + if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + return mpt::PathString(); + } + exeFileName.resize(exeFileName.size() * 2); + } + return mpt::GetAbsolutePath(mpt::PathString::FromNative(exeFileName.data()).GetPath()); +} + +#endif // MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE + + +#if defined(MPT_ENABLE_DYNBIND) + +#if !MPT_OS_WINDOWS_WINRT + +mpt::PathString GetSystemPath() +{ + DWORD size = GetSystemDirectory(nullptr, 0); + std::vector path(size + 1); + if(!GetSystemDirectory(path.data(), size + 1)) + { + return mpt::PathString(); + } + return mpt::PathString::FromNative(path.data()) + P_("\\"); +} + +#endif // !MPT_OS_WINDOWS_WINRT + +#endif // MPT_ENABLE_DYNBIND + +#endif // MPT_OS_WINDOWS + + + +#if defined(MPT_ENABLE_TEMPFILE) +#if MPT_OS_WINDOWS + +mpt::PathString GetTempDirectory() +{ + DWORD size = GetTempPath(0, nullptr); + if(size) + { + std::vector tempPath(size + 1); + if(GetTempPath(size + 1, tempPath.data())) + { + return mpt::PathString::FromNative(tempPath.data()); + } + } + // use exe directory as fallback + return mpt::GetExecutablePath(); +} + +mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix, const mpt::PathString &fileNameExtension) +{ + mpt::PathString filename = mpt::GetTempDirectory(); + filename += (!fileNamePrefix.empty() ? fileNamePrefix + P_("_") : mpt::PathString()); + filename += mpt::PathString::FromUnicode(mpt::UUID::GenerateLocalUseOnly().ToUString()); + filename += (!fileNameExtension.empty() ? P_(".") + fileNameExtension : mpt::PathString()); + return filename; +} + +TempFileGuard::TempFileGuard(const mpt::PathString &filename) + : filename(filename) +{ + return; +} + +mpt::PathString TempFileGuard::GetFilename() const +{ + return filename; +} + +TempFileGuard::~TempFileGuard() +{ + if(!filename.empty()) + { + DeleteFile(filename.AsNative().c_str()); + } +} + +#ifdef MODPLUG_TRACKER + +TempDirGuard::TempDirGuard(const mpt::PathString &dirname_) + : dirname(dirname_.WithTrailingSlash()) +{ + if(dirname.empty()) + { + return; + } + if(::CreateDirectory(dirname.AsNative().c_str(), NULL) == 0) + { // fail + dirname = mpt::PathString(); + } +} + +mpt::PathString TempDirGuard::GetDirname() const +{ + return dirname; +} + +TempDirGuard::~TempDirGuard() +{ + if(!dirname.empty()) + { + DeleteWholeDirectoryTree(dirname); + } +} + +#endif // MODPLUG_TRACKER + +#endif // MPT_OS_WINDOWS +#endif // MPT_ENABLE_TEMPFILE + +} // namespace mpt + + + +#if defined(MODPLUG_TRACKER) + +static inline char SanitizeFilenameChar(char c) +{ + if( c == '\\' || + c == '\"' || + c == '/' || + c == ':' || + c == '?' || + c == '<' || + c == '>' || + c == '|' || + c == '*') + { + c = '_'; + } + return c; +} + +static inline wchar_t SanitizeFilenameChar(wchar_t c) +{ + if( c == L'\\' || + c == L'\"' || + c == L'/' || + c == L':' || + c == L'?' || + c == L'<' || + c == L'>' || + c == L'|' || + c == L'*') + { + c = L'_'; + } + return c; +} + +void SanitizeFilename(mpt::PathString &filename) +{ + mpt::RawPathString tmp = filename.AsNative(); + for(auto &c : tmp) + { + c = SanitizeFilenameChar(c); + } + filename = mpt::PathString::FromNative(tmp); +} + +void SanitizeFilename(char *beg, char *end) +{ + for(char *it = beg; it != end; ++it) + { + *it = SanitizeFilenameChar(*it); + } +} + +void SanitizeFilename(wchar_t *beg, wchar_t *end) +{ + for(wchar_t *it = beg; it != end; ++it) + { + *it = SanitizeFilenameChar(*it); + } +} + +void SanitizeFilename(std::string &str) +{ + for(size_t i = 0; i < str.length(); i++) + { + str[i] = SanitizeFilenameChar(str[i]); + } +} + +void SanitizeFilename(std::wstring &str) +{ + for(size_t i = 0; i < str.length(); i++) + { + str[i] = SanitizeFilenameChar(str[i]); + } +} + +#if MPT_USTRING_MODE_UTF8 +void SanitizeFilename(mpt::u8string &str) +{ + for(size_t i = 0; i < str.length(); i++) + { + str[i] = SanitizeFilenameChar(str[i]); + } +} +#endif // MPT_USTRING_MODE_UTF8 + +#if defined(MPT_WITH_MFC) +void SanitizeFilename(CString &str) +{ + for(int i = 0; i < str.GetLength(); i++) + { + str.SetAt(i, SanitizeFilenameChar(str.GetAt(i))); + } +} +#endif // MPT_WITH_MFC + +#endif // MODPLUG_TRACKER + + +#if defined(MODPLUG_TRACKER) + + +mpt::PathString FileType::AsFilterString(FlagSet format) const +{ + mpt::PathString filter; + if(GetShortName().empty() || GetExtensions().empty()) + { + return filter; + } + if(!GetDescription().empty()) + { + filter += mpt::PathString::FromUnicode(GetDescription()); + } else + { + filter += mpt::PathString::FromUnicode(GetShortName()); + } + const auto extensions = GetExtensions(); + if(format[FileTypeFormatShowExtensions]) + { + filter += P_(" ("); + bool first = true; + for(const auto &ext : extensions) + { + if(first) + { + first = false; + } else + { + filter += P_(","); + } + filter += P_("*."); + filter += ext; + } + filter += P_(")"); + } + filter += P_("|"); + { + bool first = true; + for(const auto &ext : extensions) + { + if(first) + { + first = false; + } else + { + filter += P_(";"); + } + filter += P_("*."); + filter += ext; + } + } + filter += P_("|"); + return filter; +} + + +mpt::PathString FileType::AsFilterOnlyString() const +{ + mpt::PathString filter; + const auto extensions = GetExtensions(); + { + bool first = true; + for(const auto &ext : extensions) + { + if(first) + { + first = false; + } else + { + filter += P_(";"); + } + filter += P_("*."); + filter += ext; + } + } + return filter; +} + + +mpt::PathString ToFilterString(const FileType &fileType, FlagSet format) +{ + return fileType.AsFilterString(format); +} + + +mpt::PathString ToFilterString(const std::vector &fileTypes, FlagSet format) +{ + mpt::PathString filter; + for(const auto &type : fileTypes) + { + filter += type.AsFilterString(format); + } + return filter; +} + + +mpt::PathString ToFilterOnlyString(const FileType &fileType, bool prependSemicolonWhenNotEmpty) +{ + mpt::PathString filter = fileType.AsFilterOnlyString(); + return filter.empty() ? filter : (prependSemicolonWhenNotEmpty ? P_(";") : P_("")) + filter; +} + + +mpt::PathString ToFilterOnlyString(const std::vector &fileTypes, bool prependSemicolonWhenNotEmpty) +{ + mpt::PathString filter; + for(const auto &type : fileTypes) + { + filter += type.AsFilterOnlyString(); + } + return filter.empty() ? filter : (prependSemicolonWhenNotEmpty ? P_(";") : P_("")) + filter; +} + + +#endif // MODPLUG_TRACKER + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptPathString.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptPathString.h new file mode 100644 index 000000000..9d522b1f6 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptPathString.h @@ -0,0 +1,508 @@ +/* + * mptPathString.h + * --------------- + * Purpose: Wrapper class around the platform-native representation of path names. Should be the only type that is used to store path names. + * Notes : Currently none. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + +#include + +#include "FlagSet.h" + +OPENMPT_NAMESPACE_BEGIN + + + +#define MPT_DEPRECATED_PATH +//#define MPT_DEPRECATED_PATH [[deprecated]] + + + +namespace mpt +{ + +#if MPT_OS_WINDOWS +typedef mpt::winstring RawPathString; +#else // !MPT_OS_WINDOWS +typedef std::string RawPathString; +#endif // if MPT_OS_WINDOWS + + + +class PathString +{ + +private: + + RawPathString path; + +private: + + explicit PathString(const RawPathString & path) + : path(path) + { + return; + } + +public: + + PathString() + { + return; + } + PathString(const PathString & other) + : path(other.path) + { + return; + } + PathString & assign(const PathString & other) + { + path = other.path; + return *this; + } + PathString & operator = (const PathString & other) + { + return assign(other); + } + PathString & append(const PathString & other) + { + path.append(other.path); + return *this; + } + PathString & operator += (const PathString & other) + { + return append(other); + } + + friend PathString operator + (const PathString & a, const PathString & b) + { + return PathString(a).append(b); + } + + friend bool operator < (const PathString & a, const PathString & b) + { + return a.AsNative() < b.AsNative(); + } + friend bool operator == (const PathString & a, const PathString & b) + { + return a.AsNative() == b.AsNative(); + } + friend bool operator != (const PathString & a, const PathString & b) + { + return a.AsNative() != b.AsNative(); + } + + bool empty() const { return path.empty(); } + + std::size_t Length() const { return path.size(); } + + + +public: + +#if MPT_OS_WINDOWS +#if !MPT_OS_WINDOWS_WINRT + static int CompareNoCase(const PathString & a, const PathString & b); +#endif // !MPT_OS_WINDOWS_WINRT +#endif + +#if MPT_OS_WINDOWS && (defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE)) + + void SplitPath(PathString *drive, PathString *dir, PathString *fname, PathString *ext) const; + // \\?\ prefixes will be removed and \\?\\UNC prefixes converted to canonical \\ form. + PathString GetDrive() const; // Drive letter + colon, e.g. "C:" or \\server\\share + PathString GetDir() const; // Directory, e.g. "\OpenMPT\" + PathString GetPath() const; // Drive + Dir, e.g. "C:\OpenMPT\" + PathString GetFileName() const; // File name without extension, e.g. "OpenMPT" + PathString GetFileExt() const; // Extension including dot, e.g. ".exe" + PathString GetFullFileName() const; // File name + extension, e.g. "OpenMPT.exe" + + // Verify if this path represents a valid directory on the file system. + bool IsDirectory() const; + // Verify if this path exists and is a file on the file system. + bool IsFile() const; + +#endif // MPT_OS_WINDOWS && (MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE) + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + + bool FileOrDirectoryExists() const; + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + + static bool IsPathSeparator(RawPathString::value_type c); + static RawPathString::value_type GetDefaultPathSeparator(); + +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS + + // Return the same path string with a different (or appended) extension (including "."), e.g. "foo.bar",".txt" -> "foo.txt" or "C:\OpenMPT\foo",".txt" -> "C:\OpenMPT\foo.txt" + PathString ReplaceExt(const mpt::PathString &newExt) const; + + // Removes special characters from a filename component and replaces them with a safe replacement character ("_" on windows). + // Returns the result. + // Note that this also removes path component separators, so this should only be used on single-component PathString objects. + PathString SanitizeComponent() const; + + bool HasTrailingSlash() const + { + if(path.empty()) + { + return false; + } + RawPathString::value_type c = path[path.length() - 1]; + return IsPathSeparator(c); + } + mpt::PathString &EnsureTrailingSlash() + { + if(!path.empty() && !HasTrailingSlash()) + { + path += GetDefaultPathSeparator(); + } + return *this; + } + + mpt::PathString WithoutTrailingSlash() const + { + mpt::PathString result = *this; + while(result.HasTrailingSlash()) + { + if(result.Length() == 1) + { + return result; + } + result = mpt::PathString(result.AsNative().substr(0, result.AsNative().length() - 1)); + } + return result; + } + + mpt::PathString WithTrailingSlash() const + { + mpt::PathString result = *this; + result.EnsureTrailingSlash(); + return result; + } + + // Relative / absolute paths conversion + mpt::PathString AbsolutePathToRelative(const mpt::PathString &relativeTo) const; + mpt::PathString RelativePathToAbsolute(const mpt::PathString &relativeTo) const; + +#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS + +public: + +#if MPT_OS_WINDOWS + +#if !(MPT_WSTRING_CONVERT) +#error "mpt::PathString on Windows depends on MPT_WSTRING_CONVERT)" +#endif + // conversions +#if defined(MPT_ENABLE_CHARSET_LOCALE) + MPT_DEPRECATED_PATH std::string ToLocale() const { return mpt::ToCharset(mpt::Charset::Locale, path); } +#endif + std::string ToUTF8() const { return mpt::ToCharset(mpt::Charset::UTF8, path); } + std::wstring ToWide() const { return mpt::ToWide(path); } + mpt::ustring ToUnicode() const { return mpt::ToUnicode(path); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) + MPT_DEPRECATED_PATH static PathString FromLocale(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::Locale, path)); } + static PathString FromLocaleSilent(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::Locale, path)); } +#endif + static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::UTF8, path)); } + static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToWin(path)); } + static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToWin(path)); } + RawPathString AsNative() const { return path; } + // Return native string, with possible \\?\ prefix if it exceeds MAX_PATH characters. + RawPathString AsNativePrefixed() const; + static PathString FromNative(const RawPathString &path) { return PathString(path); } +#if defined(MPT_WITH_MFC) + // CString TCHAR, so this is CHAR or WCHAR, depending on UNICODE + CString ToCString() const { return mpt::ToCString(path); } + static PathString FromCString(const CString &path) { return PathString(mpt::ToWin(path)); } +#endif // MPT_WITH_MFC + + // Convert a path to its simplified form, i.e. remove ".\" and "..\" entries + mpt::PathString Simplify() const; + +#else // !MPT_OS_WINDOWS + + // conversions +#if defined(MPT_ENABLE_CHARSET_LOCALE) + std::string ToLocale() const { return path; } + std::string ToUTF8() const { return mpt::ToCharset(mpt::Charset::UTF8, mpt::Charset::Locale, path); } +#if MPT_WSTRING_CONVERT + std::wstring ToWide() const { return mpt::ToWide(mpt::Charset::Locale, path); } +#endif + mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::Charset::Locale, path); } + static PathString FromLocale(const std::string &path) { return PathString(path); } + static PathString FromLocaleSilent(const std::string &path) { return PathString(path); } + static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, mpt::Charset::UTF8, path)); } +#if MPT_WSTRING_CONVERT + static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, path)); } +#endif + static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, path)); } + RawPathString AsNative() const { return path; } + RawPathString AsNativePrefixed() const { return path; } + static PathString FromNative(const RawPathString &path) { return PathString(path); } +#else // !MPT_ENABLE_CHARSET_LOCALE + std::string ToUTF8() const { return path; } +#if MPT_WSTRING_CONVERT + std::wstring ToWide() const { return mpt::ToWide(mpt::Charset::UTF8, path); } +#endif + mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::Charset::UTF8, path); } + static PathString FromUTF8(const std::string &path) { return PathString(path); } +#if MPT_WSTRING_CONVERT + static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::Charset::UTF8, path)); } +#endif + static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::Charset::UTF8, path)); } + RawPathString AsNative() const { return path; } + RawPathString AsNativePrefixed() const { return path; } + static PathString FromNative(const RawPathString &path) { return PathString(path); } +#endif // MPT_ENABLE_CHARSET_LOCALE + + // Convert a path to its simplified form (currently only implemented on Windows) + [[deprecated]] mpt::PathString Simplify() const { return PathString(path); } + +#endif // MPT_OS_WINDOWS + +}; + + + +#if defined(MPT_ENABLE_CHARSET_LOCALE) +#if MPT_OS_WINDOWS +#ifdef UNICODE +[[deprecated]] inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); } +#else +MPT_DEPRECATED_PATH inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.AsNative()); } +#endif +#else +MPT_DEPRECATED_PATH inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); } +#endif +#endif +inline mpt::ustring ToUString(const mpt::PathString & x) { return x.ToUnicode(); } +#if MPT_WSTRING_FORMAT +inline std::wstring ToWString(const mpt::PathString & x) { return x.ToWide(); } +#endif + +} // namespace mpt + +#if MPT_OS_WINDOWS + +#ifdef UNICODE +#define MPT_PATHSTRING_LITERAL(x) ( L ## x ) +#define MPT_PATHSTRING(x) mpt::PathString::FromNative( L ## x ) +#else +#define MPT_PATHSTRING_LITERAL(x) ( x ) +#define MPT_PATHSTRING(x) mpt::PathString::FromNative( x ) +#endif + +#else // !MPT_OS_WINDOWS + +#define MPT_PATHSTRING_LITERAL(x) ( x ) +#define MPT_PATHSTRING(x) mpt::PathString::FromNative( x ) + +#endif // MPT_OS_WINDOWS + +#define PC_(x) MPT_PATHSTRING_LITERAL(x) +#define PL_(x) MPT_PATHSTRING_LITERAL(x) +#define P_(x) MPT_PATHSTRING(x) + +namespace mpt +{ + + +bool PathIsAbsolute(const mpt::PathString &path); + +#if MPT_OS_WINDOWS + +#if !(MPT_OS_WINDOWS_WINRT && (_WIN32_WINNT < 0x0a00)) + +// Returns the absolute path for a potentially relative path and removes ".." or "." components. (same as GetFullPathNameW) +mpt::PathString GetAbsolutePath(const mpt::PathString &path); + +#endif + +#ifdef MODPLUG_TRACKER + +// Deletes a complete directory tree. Handle with EXTREME care. +// Returns false if any file could not be removed and aborts as soon as it +// encounters any error. path must be absolute. +bool DeleteWholeDirectoryTree(mpt::PathString path); + +#endif // MODPLUG_TRACKER + +#endif // MPT_OS_WINDOWS + +#if MPT_OS_WINDOWS + +#if defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE) + +// Returns the application executable path or an empty string (if unknown), e.g. "C:\mptrack\" +mpt::PathString GetExecutablePath(); + +#endif // MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE + +#if defined(MPT_ENABLE_DYNBIND) + +#if !MPT_OS_WINDOWS_WINRT +// Returns the system directory path, e.g. "C:\Windows\System32\" +mpt::PathString GetSystemPath(); +#endif // !MPT_OS_WINDOWS_WINRT + +#endif // MPT_ENABLE_DYNBIND + +#endif // MPT_OS_WINDOWS + +#if defined(MPT_ENABLE_TEMPFILE) +#if MPT_OS_WINDOWS + +// Returns temporary directory (with trailing backslash added) (e.g. "C:\TEMP\") +mpt::PathString GetTempDirectory(); + +// Returns a new unique absolute path. +mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix = mpt::PathString(), const mpt::PathString &fileNameExtension = P_("tmp")); + + + +// Scoped temporary file guard. Deletes the file when going out of scope. +// The file itself is not created automatically. +class TempFileGuard +{ +private: + const mpt::PathString filename; +public: + TempFileGuard(const mpt::PathString &filename = CreateTempFileName()); + mpt::PathString GetFilename() const; + ~TempFileGuard(); +}; + +#ifdef MODPLUG_TRACKER + +// Scoped temporary directory guard. Deletes the directory when going out of scope. +// The directory itself is created automatically. +class TempDirGuard +{ +private: + mpt::PathString dirname; +public: + TempDirGuard(const mpt::PathString &dirname_ = CreateTempFileName()); + mpt::PathString GetDirname() const; + ~TempDirGuard(); +}; + +#endif // MODPLUG_TRACKER + +#endif // MPT_OS_WINDOWS +#endif // MPT_ENABLE_TEMPFILE + +} // namespace mpt + + + +#if defined(MODPLUG_TRACKER) + +// Sanitize a filename (remove special chars) +void SanitizeFilename(mpt::PathString &filename); + +void SanitizeFilename(char *beg, char *end); +void SanitizeFilename(wchar_t *beg, wchar_t *end); + +void SanitizeFilename(std::string &str); +void SanitizeFilename(std::wstring &str); +#if MPT_USTRING_MODE_UTF8 +void SanitizeFilename(mpt::u8string &str); +#endif // MPT_USTRING_MODE_UTF8 + +template +void SanitizeFilename(char (&buffer)[size]) +{ + static_assert(size > 0); + SanitizeFilename(buffer, buffer + size); +} + +template +void SanitizeFilename(wchar_t (&buffer)[size]) +{ + static_assert(size > 0); + SanitizeFilename(buffer, buffer + size); +} + +#if defined(MPT_WITH_MFC) +void SanitizeFilename(CString &str); +#endif // MPT_WITH_MFC + +#endif // MODPLUG_TRACKER + + +#if defined(MODPLUG_TRACKER) + +enum FileTypeFormat +{ + FileTypeFormatNone = 0 , // do not show extensions after description, i.e. "Foo Files" + FileTypeFormatShowExtensions = 1<<0, // show extensions after descripten, i.e. "Foo Files (*.foo,*.bar)" +}; +MPT_DECLARE_ENUM(FileTypeFormat) + +class FileType +{ +private: + mpt::ustring m_ShortName; // "flac", "mod" (lowercase) + mpt::ustring m_Description; // "FastTracker 2 Module" + std::vector m_MimeTypes; // "audio/ogg" (in ASCII) + std::vector m_Extensions; // "mod", "xm" (lowercase) + std::vector m_Prefixes; // "mod" for "mod.*" +public: + FileType() { } + FileType(const std::vector &group) + { + for(const auto &type : group) + { + m_MimeTypes.insert(m_MimeTypes.end(), type.m_MimeTypes.begin(), type.m_MimeTypes.end()); + m_Extensions.insert(m_Extensions.end(), type.m_Extensions.begin(), type.m_Extensions.end()); + m_Prefixes.insert(m_Prefixes.end(), type.m_Prefixes.begin(), type.m_Prefixes.end()); + } + } + static FileType Any() + { + return FileType().ShortName(U_("*")).Description(U_("All Files")).AddExtension(P_("*")); + } +public: + FileType& ShortName(const mpt::ustring &shortName) { m_ShortName = shortName; return *this; } + FileType& Description(const mpt::ustring &description) { m_Description = description; return *this; } + FileType& MimeTypes(const std::vector &mimeTypes) { m_MimeTypes = mimeTypes; return *this; } + FileType& Extensions(const std::vector &extensions) { m_Extensions = extensions; return *this; } + FileType& Prefixes(const std::vector &prefixes) { m_Prefixes = prefixes; return *this; } + FileType& AddMimeType(const std::string &mimeType) { m_MimeTypes.push_back(mimeType); return *this; } + FileType& AddExtension(const mpt::PathString &extension) { m_Extensions.push_back(extension); return *this; } + FileType& AddPrefix(const mpt::PathString &prefix) { m_Prefixes.push_back(prefix); return *this; } +public: + mpt::ustring GetShortName() const { return m_ShortName; } + mpt::ustring GetDescription() const { return m_Description; } + std::vector GetMimeTypes() const { return m_MimeTypes; } + std::vector GetExtensions() const { return m_Extensions; } + std::vector GetPrefixes() const { return m_Prefixes; } +public: + mpt::PathString AsFilterString(FlagSet format = FileTypeFormatNone) const; + mpt::PathString AsFilterOnlyString() const; +}; // class FileType + + +// "Ogg Vorbis|*.ogg;*.oga|" // FileTypeFormatNone +// "Ogg Vorbis (*.ogg,*.oga)|*.ogg;*.oga|" // FileTypeFormatShowExtensions +mpt::PathString ToFilterString(const FileType &fileType, FlagSet format = FileTypeFormatNone); +mpt::PathString ToFilterString(const std::vector &fileTypes, FlagSet format = FileTypeFormatNone); + +// "*.ogg;*.oga" / ";*.ogg;*.oga" +mpt::PathString ToFilterOnlyString(const FileType &fileType, bool prependSemicolonWhenNotEmpty = false); +mpt::PathString ToFilterOnlyString(const std::vector &fileTypes, bool prependSemicolonWhenNotEmpty = false); + +#endif // MODPLUG_TRACKER + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptRandom.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptRandom.cpp new file mode 100644 index 000000000..1cf4e7e4e --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptRandom.cpp @@ -0,0 +1,344 @@ +/* + * mptRandom.cpp + * ------------- + * Purpose: PRNG + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" + +#include "mptRandom.h" + +#include "Endianness.h" +#include "mptCRC.h" + +#include + +#include +#include + + +OPENMPT_NAMESPACE_BEGIN + + +namespace mpt +{ + + +#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE) + +template +static T log2(T x) +{ + return std::log(x) / std::log(static_cast(2)); +} + + +static MPT_CONSTEXPR14_FUN int lower_bound_entropy_bits(unsigned int x) +{ + return detail::lower_bound_entropy_bits(x); +} + + +template +static MPT_CONSTEXPR14_FUN bool is_mask(T x) +{ + static_assert(std::numeric_limits::is_integer); + typedef typename std::make_unsigned::type unsigned_T; + unsigned_T ux = static_cast(x); + unsigned_T mask = 0; + for(std::size_t bits = 0; bits <= (sizeof(unsigned_T) * 8); ++bits) + { + mask = (mask << 1) | 1u; + if(ux == mask) + { + return true; + } + } + return false; +} + +#endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE + + +namespace { +template struct default_hash { }; +template <> struct default_hash { typedef mpt::checksum::crc16 type; }; +template <> struct default_hash { typedef mpt::checksum::crc16 type; }; +template <> struct default_hash { typedef mpt::checksum::crc32c type; }; +template <> struct default_hash { typedef mpt::checksum::crc64_jones type; }; +} + +template +static T generate_timeseed() +{ + // Note: CRC is actually not that good a choice here, but it is simple and we + // already have an implementaion available. Better choices for mixing entropy + // would be a hash function with proper avalanche characteristics or a block + // or stream cipher with any pre-choosen random key and IV. The only aspect we + // really need here is whitening of the bits. + typename mpt::default_hash::type hash; + +#ifdef MPT_BUILD_FUZZER + + return static_cast(mpt::FUZZER_RNG_SEED); + +#else // !MPT_BUILD_FUZZER + + { + uint64be time; + time = std::chrono::duration_cast(std::chrono::system_clock().now().time_since_epoch()).count(); + std::byte bytes[sizeof(time)]; + std::memcpy(bytes, &time, sizeof(time)); + hash(std::begin(bytes), std::end(bytes)); + } + +#if !defined(MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK) + { + uint64be time; + time = std::chrono::duration_cast(std::chrono::high_resolution_clock().now().time_since_epoch()).count(); + std::byte bytes[sizeof(time)]; + std::memcpy(bytes, &time, sizeof(time)); + hash(std::begin(bytes), std::end(bytes)); + } +#endif // !MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK + + return static_cast(hash.result()); + +#endif // MPT_BUILD_FUZZER + +} + + +#ifdef MODPLUG_TRACKER + +namespace rng +{ + +void crand::reseed(uint32 seed) +{ + std::srand(seed); +} + +crand::result_type crand::operator()() +{ + return std::rand(); +} + +} // namespace rng + +#endif // MODPLUG_TRACKER + +sane_random_device::sane_random_device() +#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE) + : rd_reliable(false) +#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE +{ +#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE) + try + { + prd = std::make_unique(); + rd_reliable = ((*prd).entropy() > 0.0); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } catch(const std::exception &) + { + rd_reliable = false; + } + if(!rd_reliable) +#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE + { + init_fallback(); + } +} + +sane_random_device::sane_random_device(const std::string & token_) + : token(token_) +#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE) + , rd_reliable(false) +#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE +{ +#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE) + try + { + prd = std::make_unique(token); + rd_reliable = ((*prd).entropy() > 0.0); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } catch(const std::exception &) + { + rd_reliable = false; + } + if(!rd_reliable) +#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE + { + init_fallback(); + } +} + +void sane_random_device::init_fallback() +{ + if(!rd_fallback) + { + if(token.length() > 0) + { + uint64 seed_val = mpt::generate_timeseed(); + std::vector seeds; + seeds.push_back(static_cast(seed_val >> 32)); + seeds.push_back(static_cast(seed_val >> 0)); + for(std::size_t i = 0; i < token.length(); ++i) + { + seeds.push_back(static_cast(static_cast(token[i]))); + } + std::seed_seq seed(seeds.begin(), seeds.end()); + rd_fallback = std::make_unique(seed); + } else + { + uint64 seed_val = mpt::generate_timeseed(); + unsigned int seeds[2]; + seeds[0] = static_cast(seed_val >> 32); + seeds[1] = static_cast(seed_val >> 0); + std::seed_seq seed(seeds + 0, seeds + 2); + rd_fallback = std::make_unique(seed); + } + } +} + +sane_random_device::result_type sane_random_device::operator()() +{ + mpt::lock_guard l(m); + result_type result = 0; +#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE) + if(prd) + { + try + { + if constexpr(std::random_device::min() != 0 || !mpt::is_mask(std::random_device::max())) + { // insane std::random_device + // This implementation is not exactly uniformly distributed but good enough + // for OpenMPT. + double rd_min = static_cast(std::random_device::min()); + double rd_max = static_cast(std::random_device::max()); + double rd_range = rd_max - rd_min; + double rd_size = rd_range + 1.0; + double rd_entropy = mpt::log2(rd_size); + int iterations = static_cast(std::ceil(result_bits() / rd_entropy)); + double tmp = 0.0; + for(int i = 0; i < iterations; ++i) + { + tmp = (tmp * rd_size) + (static_cast((*prd)()) - rd_min); + } + double result_01 = std::floor(tmp / std::pow(rd_size, iterations)); + result = static_cast(std::floor(result_01 * (static_cast(max() - min()) + 1.0))) + min(); + } else + { // sane std::random_device + result = 0; + std::size_t rd_bits = mpt::lower_bound_entropy_bits(std::random_device::max()); + for(std::size_t entropy = 0; entropy < (sizeof(result_type) * 8); entropy += rd_bits) + { + if(rd_bits < (sizeof(result_type) * 8)) + { + result = (result << rd_bits) | static_cast((*prd)()); + } else + { + result = result | static_cast((*prd)()); + } + } + } + } catch(const std::exception &) + { + rd_reliable = false; + init_fallback(); + } + } else + { + rd_reliable = false; + } + if(!rd_reliable) +#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE + { // std::random_device is unreliable + // XOR the generated random number with more entropy from the time-seeded + // PRNG. + // Note: This is safe even if the std::random_device itself is implemented + // as a std::mt19937 PRNG because we are very likely using a different + // seed. + result ^= mpt::random(*rd_fallback); + } + return result; +} + +prng_random_device_seeder::prng_random_device_seeder() +{ + return; +} + +uint8 prng_random_device_seeder::generate_seed8() +{ + return mpt::generate_timeseed(); +} + +uint16 prng_random_device_seeder::generate_seed16() +{ + return mpt::generate_timeseed(); +} + +uint32 prng_random_device_seeder::generate_seed32() +{ + return mpt::generate_timeseed(); +} + +uint64 prng_random_device_seeder::generate_seed64() +{ + return mpt::generate_timeseed(); +} + +#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) + +static mpt::random_device *g_rd = nullptr; +static mpt::thread_safe_prng *g_global_prng = nullptr; + +void set_global_random_device(mpt::random_device *rd) +{ + g_rd = rd; +} + +void set_global_prng(mpt::thread_safe_prng *prng) +{ + g_global_prng = prng; +} + +mpt::random_device & global_random_device() +{ + return *g_rd; +} + +mpt::thread_safe_prng & global_prng() +{ + return *g_global_prng; +} + +#else + +mpt::random_device & global_random_device() +{ + static mpt::random_device g_rd; + return g_rd; +} + +mpt::thread_safe_prng & global_prng() +{ + static mpt::thread_safe_prng g_global_prng(mpt::make_prng(global_random_device())); + return g_global_prng; +} + +#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT + + +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptRandom.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptRandom.h new file mode 100644 index 000000000..972ce623b --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptRandom.h @@ -0,0 +1,733 @@ +/* + * mptRandom.h + * ----------- + * Purpose: PRNG + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + +#include "mptMutex.h" + +#include +#include + +#ifdef MODPLUG_TRACKER +#include +#endif // MODPLUG_TRACKER + + +OPENMPT_NAMESPACE_BEGIN + + +// NOTE: +// We implement our own PRNG and distribution functions as the implementations +// of std::uniform_int_distribution is either wrong (not uniform in MSVC2010) or +// not guaranteed to be livelock-free for bad PRNGs (in GCC, Clang, boost). +// We resort to a simpler implementation with only power-of-2 result ranges for +// both the underlying PRNG and our interface function. This saves us from +// complicated code having to deal with partial bits of entropy. +// Our interface still somewhat follows the mindset of C++11 (with the +// addition of a simple wrapper function mpt::random which saves the caller from +// instantiating distribution objects for the common uniform distribution case. +// We are still using std::random_device for initial seeding when avalable and +// after working around its set of problems. + + +namespace mpt +{ + + +#ifdef MPT_BUILD_FUZZER +static constexpr uint32 FUZZER_RNG_SEED = 3141592653u; // pi +#endif // MPT_BUILD_FUZZER + + +namespace detail +{ + +MPT_CONSTEXPR14_FUN int lower_bound_entropy_bits(unsigned int x) +{ + if(x >= 0xffffffffu) + { + return 32; + } else if(x >= 0x7fffffffu) + { + return 31; + } else if(x >= 0x3fffffffu) + { + return 30; + } else if(x >= 0x1fffffffu) + { + return 29; + } else if(x >= 0x0fffffffu) + { + return 28; + } else if(x >= 0x07ffffffu) + { + return 27; + } else if(x >= 0x03ffffffu) + { + return 26; + } else if(x >= 0x01ffffffu) + { + return 25; + } else if(x >= 0x00ffffffu) + { + return 24; + } else if(x >= 0x007fffffu) + { + return 23; + } else if(x >= 0x003fffffu) + { + return 22; + } else if(x >= 0x001fffffu) + { + return 21; + } else if(x >= 0x000fffffu) + { + return 20; + } else if(x >= 0x0007ffffu) + { + return 19; + } else if(x >= 0x0003ffffu) + { + return 18; + } else if(x >= 0x0001ffffu) + { + return 17; + } else if(x >= 0x0000ffffu) + { + return 16; + } else if(x >= 0x00007fffu) + { + return 15; + } else if(x >= 0x00003fffu) + { + return 14; + } else if(x >= 0x00001fffu) + { + return 13; + } else if(x >= 0x00000fffu) + { + return 12; + } else if(x >= 0x000007ffu) + { + return 11; + } else if(x >= 0x000003ffu) + { + return 10; + } else if(x >= 0x000001ffu) + { + return 9; + } else if(x >= 0x000000ffu) + { + return 8; + } else if(x >= 0x0000007fu) + { + return 7; + } else if(x >= 0x0000003fu) + { + return 6; + } else if(x >= 0x0000001fu) + { + return 5; + } else if(x >= 0x0000000fu) + { + return 4; + } else if(x >= 0x00000007u) + { + return 3; + } else if(x >= 0x00000003u) + { + return 2; + } else if(x >= 0x00000001u) + { + return 1; + } + return 0; +} + +} + + +template struct engine_traits +{ + typedef typename Trng::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() + { + return Trng::result_bits(); + } + template + static inline Trng make(Trd & rd) + { + return Trng(rd); + } +}; + + +template +inline T random(Trng & rng) +{ + static_assert(std::numeric_limits::is_integer); + typedef typename std::make_unsigned::type unsigned_T; + const unsigned int rng_bits = mpt::engine_traits::result_bits(); + unsigned_T result = 0; + for(std::size_t entropy = 0; entropy < (sizeof(T) * 8); entropy += rng_bits) + { + if constexpr(rng_bits < (sizeof(T) * 8)) + { + constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) + result = (result << shift_bits) ^ static_cast(rng()); + } else + { + result = static_cast(rng()); + } + } + return static_cast(result); +} + +template +inline T random(Trng & rng) +{ + static_assert(std::numeric_limits::is_integer); + typedef typename std::make_unsigned::type unsigned_T; + const unsigned int rng_bits = mpt::engine_traits::result_bits(); + unsigned_T result = 0; + for(std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) + { + if constexpr(rng_bits < (sizeof(T) * 8)) + { + constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) + result = (result << shift_bits) ^ static_cast(rng()); + } else + { + result = static_cast(rng()); + } + } + if constexpr(required_entropy_bits >= (sizeof(T) * 8)) + { + return static_cast(result); + } else + { + return static_cast(result & ((static_cast(1) << required_entropy_bits) - static_cast(1))); + } +} + +template +inline T random(Trng & rng, std::size_t required_entropy_bits) +{ + static_assert(std::numeric_limits::is_integer); + typedef typename std::make_unsigned::type unsigned_T; + const unsigned int rng_bits = mpt::engine_traits::result_bits(); + unsigned_T result = 0; + for(std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) + { + if constexpr(rng_bits < (sizeof(T) * 8)) + { + constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however) + result = (result << shift_bits) ^ static_cast(rng()); + } else + { + result = static_cast(rng()); + } + } + if(required_entropy_bits >= (sizeof(T) * 8)) + { + return static_cast(result); + } else + { + return static_cast(result & ((static_cast(1) << required_entropy_bits) - static_cast(1))); + } +} + +template +struct uniform_real_distribution +{ +private: + T a; + T b; +public: + inline uniform_real_distribution(T a, T b) + : a(a) + , b(b) + { + return; + } + template + inline T operator()(Trng & rng) const + { + const int mantissa_bits = std::numeric_limits::digits; + return ((b - a) * static_cast(mpt::random(rng)) / static_cast((static_cast(1u) << mantissa_bits))) + a; + } +}; + + +template +inline T random(Trng & rng, T min, T max) +{ + static_assert(!std::numeric_limits::is_integer); + typedef mpt::uniform_real_distribution dis_type; + dis_type dis(min, max); + return static_cast(dis(rng)); +} + + +namespace rng +{ + +#if MPT_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:4724) // potential mod by 0 +#endif // MPT_COMPILER_MSVC + +template +class lcg +{ +public: + typedef Tstate state_type; + typedef Tvalue result_type; +private: + state_type state; +public: + template + explicit inline lcg(Trng & rd) + : state(mpt::random(rd)) + { + operator()(); // we return results from the current state and update state after returning. results in better pipelining. + } + explicit inline lcg(state_type seed) + : state(seed) + { + operator()(); // we return results from the current state and update state after returning. results in better pipelining. + } +public: + static MPT_CONSTEXPR11_FUN result_type min() + { + return static_cast(0); + } + static MPT_CONSTEXPR11_FUN result_type max() + { + static_assert(((result_mask >> result_shift) << result_shift) == result_mask); + return static_cast(result_mask >> result_shift); + } + static MPT_CONSTEXPR11_FUN int result_bits() + { + static_assert(((static_cast(1) << result_bits_) - 1) == (result_mask >> result_shift)); + return result_bits_; + } + inline result_type operator()() + { + // we return results from the current state and update state after returning. results in better pipelining. + state_type s = state; + result_type result = static_cast((s & result_mask) >> result_shift); + s = Util::ModIfNotZero((a * s) + c); + state = s; + return result; + } +}; + +#if MPT_COMPILER_MSVC +#pragma warning(pop) +#endif // MPT_COMPILER_MSVC + +typedef lcg lcg_msvc; +typedef lcg lcg_c99; +typedef lcg lcg_musl; + +template +class modplug +{ +public: + typedef Tstate state_type; + typedef Tvalue result_type; +private: + state_type state1; + state_type state2; +public: + template + explicit inline modplug(Trng &rd) + : state1(mpt::random(rd)) + , state2(mpt::random(rd)) + { + } + explicit inline modplug(state_type seed1, state_type seed2) + : state1(seed1) + , state2(seed2) + { + } +public: + static MPT_CONSTEXPR11_FUN result_type min() + { + return static_cast(0); + } + static MPT_CONSTEXPR11_FUN result_type max() + { + return std::numeric_limits::max(); + } + static MPT_CONSTEXPR11_FUN int result_bits() + { + static_assert(std::is_integral::value); + static_assert(std::is_unsigned::value); + return std::numeric_limits::digits; + } + inline result_type operator()() + { + state_type a = state1; + state_type b = state2; + a = mpt::rotl(a, rol1); + a ^= x1; + a += x2 + (b * x3); + b += mpt::rotl(a, rol2) * x4; + state1 = a; + state2 = b; + result_type result = static_cast(b); + return result; + } +}; + +typedef modplug modplug_dither; + +} // namespace rng + + +#ifdef MODPLUG_TRACKER + +namespace rng +{ + +class crand +{ +public: + typedef void state_type; + typedef int result_type; +private: + static void reseed(uint32 seed); +public: + template + static void reseed(Trd & rd) + { + reseed(mpt::random(rd)); + } +public: + crand() { } + explicit crand(const std::string &) { } +public: + static MPT_CONSTEXPR11_FUN result_type min() + { + return 0; + } + static MPT_CONSTEXPR11_FUN result_type max() + { + return RAND_MAX; + } + static MPT_CONSTEXPR14_FUN int result_bits() + { + return detail::lower_bound_entropy_bits(RAND_MAX); + } + result_type operator()(); +}; + +} // namespace rng + +#endif // MODPLUG_TRACKER + + +// C++11 std::random_device may be implemented as a deterministic PRNG. +// There is no way to seed this PRNG and it is allowed to be seeded with the +// same value on each program invocation. This makes std::random_device +// completely useless even as a non-cryptographic entropy pool. +// We fallback to time-seeded std::mt19937 if std::random_device::entropy() is +// 0 or less. +class sane_random_device +{ +private: + mpt::mutex m; + std::string token; +#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE) + std::unique_ptr prd; + bool rd_reliable; +#endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE + std::unique_ptr rd_fallback; +public: + typedef unsigned int result_type; +private: + void init_fallback(); +public: + sane_random_device(); + sane_random_device(const std::string & token); + static MPT_CONSTEXPR11_FUN result_type min() + { + return std::numeric_limits::min(); + } + static MPT_CONSTEXPR11_FUN result_type max() + { + return std::numeric_limits::max(); + } + static MPT_CONSTEXPR11_FUN int result_bits() + { + return sizeof(result_type) * 8; + } + result_type operator()(); +}; + + +template +class seed_seq_values +{ +private: + unsigned int seeds[N]; +public: + template + explicit seed_seq_values(Trd & rd) + { + for(std::size_t i = 0; i < N; ++i) + { + seeds[i] = rd(); + } + } + const unsigned int * begin() const + { + return seeds + 0; + } + const unsigned int * end() const + { + return seeds + N; + } +}; + + +// C++11 random does not provide any sane way to determine the amount of entropy +// required to seed a particular engine. VERY STUPID. +// List the ones we are likely to use. + +template <> struct engine_traits { + enum : std::size_t { seed_bits = sizeof(std::mt19937::result_type) * 8 * std::mt19937::state_size }; + typedef std::mt19937 rng_type; + typedef rng_type::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } + template static inline rng_type make(Trd & rd) + { + std::unique_ptr> values = std::make_unique>(rd); + std::seed_seq seed(values->begin(), values->end()); + return rng_type(seed); + } +}; + +template <> struct engine_traits { + enum : std::size_t { seed_bits = sizeof(std::mt19937_64::result_type) * 8 * std::mt19937_64::state_size }; + typedef std::mt19937_64 rng_type; + typedef rng_type::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } + template static inline rng_type make(Trd & rd) + { + std::unique_ptr> values = std::make_unique>(rd); + std::seed_seq seed(values->begin(), values->end()); + return rng_type(seed); + } +}; + +template <> struct engine_traits { + enum : std::size_t { seed_bits = std::ranlux24_base::word_size }; + typedef std::ranlux24_base rng_type; + typedef rng_type::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } + template static inline rng_type make(Trd & rd) + { + mpt::seed_seq_values values(rd); + std::seed_seq seed(values.begin(), values.end()); + return rng_type(seed); + } +}; + +template <> struct engine_traits { + enum : std::size_t { seed_bits = std::ranlux48_base::word_size }; + typedef std::ranlux48_base rng_type; + typedef rng_type::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } + template static inline rng_type make(Trd & rd) + { + mpt::seed_seq_values values(rd); + std::seed_seq seed(values.begin(), values.end()); + return rng_type(seed); + } +}; + +template <> struct engine_traits { + enum : std::size_t { seed_bits = std::ranlux24_base::word_size }; + typedef std::ranlux24 rng_type; + typedef rng_type::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() { return std::ranlux24_base::word_size; } + template static inline rng_type make(Trd & rd) + { + mpt::seed_seq_values values(rd); + std::seed_seq seed(values.begin(), values.end()); + return rng_type(seed); + } +}; + +template <> struct engine_traits { + enum : std::size_t { seed_bits = std::ranlux48_base::word_size }; + typedef std::ranlux48 rng_type; + typedef rng_type::result_type result_type; + static MPT_CONSTEXPR11_FUN int result_bits() { return std::ranlux48_base::word_size; } + template static inline rng_type make(Trd & rd) + { + mpt::seed_seq_values values(rd); + std::seed_seq seed(values.begin(), values.end()); + return rng_type(seed); + } +}; + + +class prng_random_device_seeder +{ +private: + uint8 generate_seed8(); + uint16 generate_seed16(); + uint32 generate_seed32(); + uint64 generate_seed64(); +protected: + template inline T generate_seed(); +protected: + prng_random_device_seeder(); +}; + +template <> inline uint8 prng_random_device_seeder::generate_seed() { return generate_seed8(); } +template <> inline uint16 prng_random_device_seeder::generate_seed() { return generate_seed16(); } +template <> inline uint32 prng_random_device_seeder::generate_seed() { return generate_seed32(); } +template <> inline uint64 prng_random_device_seeder::generate_seed() { return generate_seed64(); } + +template +class prng_random_device + : public prng_random_device_seeder +{ +public: + typedef unsigned int result_type; +private: + mpt::mutex m; + Trng rng; +public: + prng_random_device() + : rng(generate_seed()) + { + return; + } + prng_random_device(const std::string &) + : rng(generate_seed()) + { + return; + } + static MPT_CONSTEXPR11_FUN result_type min() + { + return std::numeric_limits::min(); + } + static MPT_CONSTEXPR11_FUN result_type max() + { + return std::numeric_limits::max(); + } + static MPT_CONSTEXPR11_FUN int result_bits() + { + return sizeof(unsigned int) * 8; + } + result_type operator()() + { + mpt::lock_guard l(m); + return mpt::random(rng); + } +}; + + +#ifdef MPT_BUILD_FUZZER + +// 1. Use deterministic seeding +typedef mpt::prng_random_device random_device; + +// 2. Use fast PRNGs in order to not waste time fuzzing more complex PRNG +// implementations. +typedef mpt::rng::lcg_msvc fast_prng; +typedef mpt::rng::lcg_musl good_prng; + +#else // !MPT_BUILD_FUZZER + +// mpt::random_device always generates 32 bits of entropy +typedef mpt::sane_random_device random_device; + +// We cannot use std::minstd_rand here because it has not a power-of-2 sized +// output domain which we rely upon. +typedef mpt::rng::lcg_msvc fast_prng; // about 3 ALU operations, ~32bit of state, suited for inner loops +typedef std::ranlux48 good_prng; + +#endif // MPT_BUILD_FUZZER + + +typedef mpt::good_prng default_prng; + + +template +inline Trng make_prng(Trd & rd) +{ + return mpt::engine_traits::make(rd); +} + + +template +class thread_safe_prng + : private Trng +{ +private: + mpt::mutex m; +public: + typedef typename Trng::result_type result_type; +public: + template + explicit thread_safe_prng(Trd & rd) + : Trng(mpt::make_prng(rd)) + { + return; + } + thread_safe_prng(Trng rng) + : Trng(rng) + { + return; + } +public: + static MPT_CONSTEXPR11_FUN typename engine_traits::result_type min() + { + return Trng::min(); + } + static MPT_CONSTEXPR11_FUN typename engine_traits::result_type max() + { + return Trng::max(); + } + static MPT_CONSTEXPR11_FUN int result_bits() + { + return engine_traits::result_bits(); + } +public: + typename engine_traits::result_type operator()() + { + mpt::lock_guard l(m); + return Trng::operator()(); + } +}; + + +mpt::random_device & global_random_device(); +mpt::thread_safe_prng & global_prng(); + +#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) +void set_global_random_device(mpt::random_device *rd); +void set_global_prng(mpt::thread_safe_prng *rng); +#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT + + +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptSpan.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptSpan.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptSpan.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptSpan.h diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptString.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptString.cpp new file mode 100644 index 000000000..1d93485e0 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptString.cpp @@ -0,0 +1,1613 @@ +/* + * mptString.cpp + * ------------- + * Purpose: Small string-related utilities, number and message formatting. + * Notes : Currently none. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "mptString.h" + +#include "Endianness.h" + +#include +#include +#include +#include + +#include + +#if defined(MODPLUG_TRACKER) +#include +#endif // MODPLUG_TRACKER + +#if defined(MODPLUG_TRACKER) +#include +#endif // MODPLUG_TRACKER + +#if MPT_OS_WINDOWS +#include +#endif + + +OPENMPT_NAMESPACE_BEGIN + + + +/* + + + +Quick guide to the OpenMPT string type jungle +============================================= + + + +This quick guide is only meant as a hint. There may be valid reasons to not +honor the recommendations found here. Staying consistent with surrounding and/or +related code sections may also be important. + + + +List of string types +-------------------- + + * std::string (OpenMPT, libopenmpt) + C++ string of unspecifed 8bit encoding. Try to always document the + encoding if not clear from context. Do not use unless there is an obvious + reason to do so. + + * std::wstring (OpenMPT) + UTF16 (on windows) or UTF32 (otherwise). Do not use unless there is an + obvious reason to do so. + + * mpt::lstring (OpenMPT) + OpenMPT locale string type. The encoding is always CP_ACP. Do not use unless + there is an obvious reason to do so. + + * char* (OpenMPT, libopenmpt) + C string of unspecified encoding. Use only for static literals or in + performance critical inner loops where full control and avoidance of memory + allocations is required. + + * wchar_t* (OpenMPT) + C wide string. Use only if Unicode is required for static literals or in + performance critical inner loops where full control and avoidance of memory + allocation is required. + + * mpt::winstring (OpenMPT) + OpenMPT type-safe string to interface with native WinAPI, either encoded in + locale/CP_ACP (if !UNICODE) or UTF16 (if UNICODE). + + * CString (OpenMPT) + MFC string type, either encoded in locale/CP_ACP (if !UNICODE) or UTF16 (if + UNICODE). Specify literals with _T(""). Use in MFC GUI code. + + * CStringA (OpenMPT) + MFC ANSI string type. The encoding is always CP_ACP. Do not use. + + * CStringW (OpenMPT) + MFC Unicode string type. Do not use. + + * mpt::PathString (OpenMPT, libopenmpt) + String type representing paths and filenames. Always use for these in order + to avoid potentially lossy conversions. Use P_("") macro for + literals. + + * mpt::ustring (OpenMPT, libopenmpt) + The default unicode string type. Can be encoded in UTF8 or UTF16 or UTF32, + depending on MPT_USTRING_MODE_* and sizeof(wchar_t). Literals can written as + U_(""). Use as your default string type if no other string type is + a measurably better fit. + + * MPT_UTF8 (OpenMPT, libopenmpt) + Macro that generates a mpt::ustring from string literals containing + non-ascii characters. In order to keep the source code in ascii encoding, + always express non-ascii characters using explicit \x23 escaping. Note that + depending on the underlying type of mpt::ustring, MPT_UTF8 *requires* a + runtime conversion. Only use for string literals containing non-ascii + characters (use MPT_USTRING otherwise). + + * MPT_ULITERAL / MPT_UCHAR / mpt::uchar (OpenMPT, libopenmpt) + Macros which generate string literals, char literals and the char literal + type respectively. These are especially useful in constexpr contexts or + global data where MPT_USTRING is either unusable or requires a global + contructor to run. Do NOT use as a performance optimization in place of + MPT_USTRING however, because MPT_USTRING can be converted to C++11/14 user + defined literals eventually, while MPT_ULITERAL cannot because of constexpr + requirements. + + * mpt::RawPathString (OpenMPT, libopenmpt) + Internal representation of mpt::PathString. Only use for parsing path + fragments. + + * mpt::u8string (OpenMPT, libopenmpt) + Internal representation of mpt::ustring. Do not use directly. Ever. + + * std::basic_string (OpenMPT) + Same as std::string. Do not use std::basic_string in the templated form. + + * std::basic_string (OpenMPT) + Same as std::wstring. Do not use std::basic_string in the templated form. + +The following string types are available in order to avoid the need to overload +functions on a huge variety of string types. Use only ever as function argument +types. +Note that the locale charset is not available on all libopenmpt builds (in which +case the option is ignored or a sensible fallback is used; these types are +always available). +All these types publicly inherit from mpt::ustring and do not contain any +additional state. This means that they work the same way as mpt::ustring does +and do support type-slicing for both, read and write accesses. +These types only add conversion constructors for all string types that have a +defined encoding and for all 8bit string types using the specified encoding +heuristic. + + * AnyUnicodeString (OpenMPT, libopenmpt) + Is constructible from any Unicode string. + + * AnyString (OpenMPT, libopenmpt) + Tries to do the smartest auto-magic we can do. + + * AnyStringLocale (OpenMPT, libopenmpt) + char-based strings are assumed to be in locale encoding. + + * AnyStringUTF8orLocale (OpenMPT, libopenmpt) + char-based strings are tried in UTF8 first, if this fails, locale is used. + + * AnyStringUTF8 (OpenMPT, libopenmpt) + char-based strings are assumed to be in UTF8. + + + +Encoding of 8bit strings +------------------------ + +8bit strings have an unspecified encoding. When the string is contained within a +CSoundFile object, the encoding is most likely CSoundFile::GetCharsetInternal(), +otherwise, try to gather the most probable encoding from surrounding or related +code sections. + + + +Decision tree to help deciding which string type to use +------------------------------------------------------- + +if in libopenmpt + if in libopenmpt c++ interface + T = std::string, the encoding is utf8 + elif in libopenmpt c interface + T = char*, the encoding is utf8 + elif performance critical inner loop + T = char*, document the encoding if not clear from context + elif string literal containing non-ascii characters + T = MPT_UTF8 + elif path or file + if parsing path fragments + T = mpt::RawPathString + template your function on the concrete underlying string type + (std::string and std::wstring) or use preprocessor MPT_OS_WINDOWS + else + T = mpt::PathString + fi + else + T = mpt::ustring + fi +else + if performance critical inner loop + if needs unicode support + T = mpt::uchar* / MPT_ULITERAL + else + T = char*, document the encoding if not clear from context + fi + elif string literal containing non-ascii characters + T = MPT_UTF8 + elif path or file + if parsing path fragments + T = mpt::RawPathString + template your function on the concrete underlying string type + (std::string and std::wstring) or use preprocessor MPT_OS_WINDOWS + else + T = mpt::PathString + fi + elif winapi interfacing code + T = mpt::winstring + elif mfc/gui code + T = CString + else + if constexpr context or global data + T = mpt::uchar* / MPT_ULITERAL + else + T = mpt::ustring + fi + fi +fi + +This boils down to: Prefer mpt::PathString and mpt::ustring, and only use any +other string type if there is an obvious reason to do so. + + + +Character set conversions +------------------------- + +Character set conversions in OpenMPT are always fuzzy. + +Behaviour in case of an invalid source encoding and behaviour in case of an +unrepresentable destination encoding can be any of the following: + * The character is replaced by some replacement character ('?' or L'\ufffd' in + most cases). + * The character is replaced by a similar character (either semantically + similiar or visually similar). + * The character is transcribed with some ASCII text. + * The character is discarded. + * Conversion stops at this very character. + +Additionally. conversion may stop or continue on \0 characters in the middle of +the string. + +Behaviour can vary from one conversion tuple to any other. + +If you need to ensure lossless conversion, do a roundtrip conversion and check +for equality. + + + +Unicode handling +---------------- + +OpenMPT is generally not aware of and does not handle different Unicode +normalization forms. +You should be aware of the following possibilities: + * Conversion between UTF8, UTF16, UTF32 may or may not change between NFC and + NFD. + * Conversion from any non-Unicode 8bit encoding can result in both, NFC or NFD + forms. + * Conversion to any non-Unicode 8bit encoding may or may not involve + conversion to NFC, NFD, NFKC or NFKD during the conversion. This in + particular means that conversion of decomposed german umlauts to ISO8859-1 + may fail. + * Changing the normalization form of path strings may render the file + inaccessible. + +Unicode BOM may or may not be preserved and/or discarded during conversion. + +Invalid Unicode code points may be treated as invalid or as valid characters +when converting between different Unicode encodings. + + + +Interfacing with WinAPI +----------------------- + +When in MFC code, use CString. +When in non MFC code, either use std::wstring when directly interfacing with +APIs only available in WCHAR variants, or use mpt::winstring and +mpt::WinStringBuf helpers otherwise. +Specify TCHAR string literals with _T("foo") in mptrack/, and with TEXT("foo") +in common/ or sounddev/. _T() requires which is specific to the MSVC +runtime and not portable across compilers. TEXT() is from . We use +_T() in mptrack/ only because it is shorter. + + + +*/ + + + +namespace mpt { namespace String { + + + +/* +default 1:1 mapping +static constexpr char32_t CharsetTableISO8859_1[256] = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, + 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f, + 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f, + 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af, + 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf, + 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf, + 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df, + 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef, + 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff +}; +*/ + +static constexpr char32_t CharsetTableISO8859_15[256] = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, + 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f, + 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f, + 0x00a0,0x00a1,0x00a2,0x00a3,0x20ac,0x00a5,0x0160,0x00a7,0x0161,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af, + 0x00b0,0x00b1,0x00b2,0x00b3,0x017d,0x00b5,0x00b6,0x00b7,0x017e,0x00b9,0x00ba,0x00bb,0x0152,0x0153,0x0178,0x00bf, + 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf, + 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df, + 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef, + 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff +}; + +static constexpr char32_t CharsetTableWindows1252[256] = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f, + 0x20ac,0x0081,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,0x02c6,0x2030,0x0160,0x2039,0x0152,0x008d,0x017d,0x008f, + 0x0090,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,0x02dc,0x2122,0x0161,0x203a,0x0153,0x009d,0x017e,0x0178, + 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af, + 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf, + 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf, + 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df, + 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef, + 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff +}; + +static constexpr char32_t CharsetTableCP437[256] = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2302, + 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5, + 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192, + 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, + 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, + 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, + 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, + 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, + 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + + +#define C(x) (static_cast((x))) + +// AMS1 actually only supports ASCII plus the modified control characters and no high chars at all. +// Just default to CP437 for those to keep things simple. +static constexpr char32_t CharsetTableCP437AMS[256] = { + C(' '),0x0001,0x0002,0x0003,0x00e4,0x0005,0x00e5,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x00c4,0x00c5, // differs from CP437 + 0x0010,0x0011,0x0012,0x0013,0x00f6,0x0015,0x0016,0x0017,0x0018,0x00d6,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, // differs from CP437 + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2302, + 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5, + 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192, + 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, + 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, + 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, + 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, + 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, + 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + +// AMS2: Looking at Velvet Studio's bitmap font (TPIC32.PCX), these appear to be the only supported non-ASCII chars. +static constexpr char32_t CharsetTableCP437AMS2[256] = { + C(' '),0x00a9,0x221a,0x00b7,C('0'),C('1'),C('2'),C('3'),C('4'),C('5'),C('6'),C('7'),C('8'),C('9'),C('A'),C('B'), // differs from CP437 + C('C'),C('D'),C('E'),C('F'),C(' '),0x00a7,C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '), // differs from CP437 + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2302, + 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5, + 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192, + 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, + 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510, + 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567, + 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580, + 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229, + 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0 +}; + +#undef C + + +#if defined(MPT_COMPILER_QUIRK_NO_WCHAR) +using widechar = char32_t; +using widestring = std::u32string; +static constexpr widechar wide_default_replacement = 0xFFFD; +#else // !MPT_COMPILER_QUIRK_NO_WCHAR +using widechar = wchar_t; +using widestring = std::wstring; +static constexpr widechar wide_default_replacement = L'\uFFFD'; +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR + + +#if MPT_OS_WINDOWS + +static bool TestCodePage(UINT cp) +{ + return IsValidCodePage(cp) ? true : false; +} + +static bool HasCharset(Charset charset) +{ + bool result = false; + switch(charset) + { +#if defined(MPT_ENABLE_CHARSET_LOCALE) + case Charset::Locale: result = true; break; +#endif + case Charset::UTF8: result = TestCodePage(CP_UTF8); break; + case Charset::ASCII: result = TestCodePage(20127); break; + case Charset::ISO8859_1: result = TestCodePage(28591); break; + case Charset::ISO8859_15: result = TestCodePage(28605); break; + case Charset::CP437: result = TestCodePage(437); break; + case Charset::Windows1252: result = TestCodePage(1252); break; + case Charset::CP437AMS: result = false; break; + case Charset::CP437AMS2: result = false; break; + } + return result; +} + +static UINT CharsetToCodepage(Charset charset) +{ + switch(charset) + { +#if defined(MPT_ENABLE_CHARSET_LOCALE) + case Charset::Locale: return CP_ACP; break; +#endif + case Charset::UTF8: return CP_UTF8; break; + case Charset::ASCII: return 20127; break; + case Charset::ISO8859_1: return 28591; break; + case Charset::ISO8859_15: return 28605; break; + case Charset::CP437: return 437; break; + case Charset::CP437AMS: return 437; break; // fallback, should not happen + case Charset::CP437AMS2: return 437; break; // fallback, should not happen + case Charset::Windows1252: return 1252; break; + } + return 0; +} + +template +static Tdststring EncodeCodepage(UINT codepage, const widestring &src) +{ + static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); + static_assert((std::is_same::value)); + Tdststring encoded_string; + int required_size = WideCharToMultiByte(codepage, 0, src.data(), mpt::saturate_cast(src.size()), nullptr, 0, nullptr, nullptr); + if(required_size > 0) + { + encoded_string.resize(required_size); + WideCharToMultiByte(codepage, 0, src.data(), mpt::saturate_cast(src.size()), reinterpret_cast(encoded_string.data()), required_size, nullptr, nullptr); + } + return encoded_string; +} + +template +static widestring DecodeCodepage(UINT codepage, const Tsrcstring &src) +{ + static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); + static_assert((std::is_same::value)); + widestring decoded_string; + int required_size = MultiByteToWideChar(codepage, 0, reinterpret_cast(src.data()), mpt::saturate_cast(src.size()), nullptr, 0); + if(required_size > 0) + { + decoded_string.resize(required_size); + MultiByteToWideChar(codepage, 0, reinterpret_cast(src.data()), mpt::saturate_cast(src.size()), decoded_string.data(), required_size); + } + return decoded_string; +} + +#endif // MPT_OS_WINDOWS + + +template +static widestring From8bit(const Tsrcstring &str, const char32_t (&table)[256], widechar replacement = wide_default_replacement) +{ + widestring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + std::size_t c = static_cast(static_cast(str[i])); + if(c < std::size(table)) + { + res.push_back(static_cast(table[c])); + } else + { + res.push_back(replacement); + } + } + return res; +} + +template +static Tdststring To8bit(const widestring &str, const char32_t (&table)[256], char replacement = '?') +{ + Tdststring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + char32_t c = static_cast(str[i]); + bool found = false; + // Try non-control characters first. + // In cases where there are actual characters mirrored in this range (like in AMS/AMS2 character sets), + // characters in the common range are preferred this way. + for(std::size_t x = 0x20; x < std::size(table); ++x) + { + if(c == table[x]) + { + res.push_back(static_cast(static_cast(x))); + found = true; + break; + } + } + if(!found) + { + // try control characters + for(std::size_t x = 0x00; x < std::size(table) && x < 0x20; ++x) + { + if(c == table[x]) + { + res.push_back(static_cast(static_cast(x))); + found = true; + break; + } + } + } + if(!found) + { + res.push_back(replacement); + } + } + return res; +} + +template +static widestring FromAscii(const Tsrcstring &str, widechar replacement = wide_default_replacement) +{ + widestring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + uint8 c = str[i]; + if(c <= 0x7f) + { + res.push_back(static_cast(static_cast(c))); + } else + { + res.push_back(replacement); + } + } + return res; +} + +template +static Tdststring ToAscii(const widestring &str, char replacement = '?') +{ + Tdststring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + char32_t c = static_cast(str[i]); + if(c <= 0x7f) + { + res.push_back(static_cast(static_cast(c))); + } else + { + res.push_back(replacement); + } + } + return res; +} + +template +static widestring FromISO_8859_1(const Tsrcstring &str, widechar replacement = wide_default_replacement) +{ + MPT_UNREFERENCED_PARAMETER(replacement); + widestring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + uint8 c = str[i]; + res.push_back(static_cast(static_cast(c))); + } + return res; +} + +template +static Tdststring ToISO_8859_1(const widestring &str, char replacement = '?') +{ + Tdststring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + char32_t c = static_cast(str[i]); + if(c <= 0xff) + { + res.push_back(static_cast(static_cast(c))); + } else + { + res.push_back(replacement); + } + } + return res; +} + + +#if defined(MPT_ENABLE_CHARSET_LOCALE) && !defined(MPT_LOCALE_ASSUME_CHARSET) + +// Note: +// +// std::codecvt::out in LLVM libc++ does not advance in and out pointers when +// running into a non-convertible character. This can happen when no locale is +// set on FreeBSD or MacOSX. This behaviour violates the C++ standard. +// +// We apply the following (albeit costly, even on other platforms) work-around: +// If the conversion errors out and does not advance the pointers at all, we +// retry the conversion with a space character prepended to the string. If it +// still does error out, we retry the whole conversion character by character. +// This is costly even on other platforms in one single case: The first +// character is an invalid Unicode code point or otherwise not convertible. Any +// following non-convertible characters are not a problem. + +static std::wstring LocaleDecode(const std::string &str, const std::locale & locale, wchar_t replacement = L'\uFFFD', int retry = 0, bool * progress = nullptr) +{ + if(str.empty()) + { + return std::wstring(); + } + std::vector out; + using codecvt_type = std::codecvt; + std::mbstate_t state = std::mbstate_t(); + const codecvt_type & facet = std::use_facet(locale); + codecvt_type::result result = codecvt_type::partial; + const char * in_begin = str.data(); + const char * in_end = in_begin + str.size(); + out.resize((in_end - in_begin) * (facet.max_length() + 1)); + wchar_t * out_begin = &(out[0]); + wchar_t * out_end = &(out[0]) + out.size(); + const char * in_next = nullptr; + wchar_t * out_next = nullptr; + do + { + if(retry == 2) + { + for(;;) + { + in_next = nullptr; + out_next = nullptr; + result = facet.in(state, in_begin, in_begin + 1, in_next, out_begin, out_end, out_next); + if(result == codecvt_type::partial && in_next == in_begin + 1) + { + in_begin = in_next; + out_begin = out_next; + continue; + } else + { + break; + } + } + } else + { + in_next = nullptr; + out_next = nullptr; + result = facet.in(state, in_begin, in_end, in_next, out_begin, out_end, out_next); + } + if(result == codecvt_type::partial || (result == codecvt_type::error && out_next == out_end)) + { + out.resize(out.size() * 2); + in_begin = in_next; + out_begin = &(out[0]) + (out_next - out_begin); + out_end = &(out[0]) + out.size(); + continue; + } + if(retry == 0) + { + if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) + { + bool made_progress = true; + LocaleDecode(std::string(" ") + str, locale, replacement, 1, &made_progress); + if(!made_progress) + { + return LocaleDecode(str, locale, replacement, 2); + } + } + } else if(retry == 1) + { + if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) + { + *progress = false; + } else + { + *progress = true; + } + return std::wstring(); + } + if(result == codecvt_type::error) + { + ++in_next; + *out_next = replacement; + ++out_next; + } + in_begin = in_next; + out_begin = out_next; + } while((result == codecvt_type::error && in_next < in_end && out_next < out_end) || (retry == 2 && in_next < in_end)); + return std::wstring(&(out[0]), out_next); +} + +static std::string LocaleEncode(const std::wstring &str, const std::locale & locale, char replacement = '?', int retry = 0, bool * progress = nullptr) +{ + if(str.empty()) + { + return std::string(); + } + std::vector out; + using codecvt_type = std::codecvt; + std::mbstate_t state = std::mbstate_t(); + const codecvt_type & facet = std::use_facet(locale); + codecvt_type::result result = codecvt_type::partial; + const wchar_t * in_begin = str.data(); + const wchar_t * in_end = in_begin + str.size(); + out.resize((in_end - in_begin) * (facet.max_length() + 1)); + char * out_begin = &(out[0]); + char * out_end = &(out[0]) + out.size(); + const wchar_t * in_next = nullptr; + char * out_next = nullptr; + do + { + if(retry == 2) + { + for(;;) + { + in_next = nullptr; + out_next = nullptr; + result = facet.out(state, in_begin, in_begin + 1, in_next, out_begin, out_end, out_next); + if(result == codecvt_type::partial && in_next == in_begin + 1) + { + in_begin = in_next; + out_begin = out_next; + continue; + } else + { + break; + } + } + } else + { + in_next = nullptr; + out_next = nullptr; + result = facet.out(state, in_begin, in_end, in_next, out_begin, out_end, out_next); + } + if(result == codecvt_type::partial || (result == codecvt_type::error && out_next == out_end)) + { + out.resize(out.size() * 2); + in_begin = in_next; + out_begin = &(out[0]) + (out_next - out_begin); + out_end = &(out[0]) + out.size(); + continue; + } + if(retry == 0) + { + if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) + { + bool made_progress = true; + LocaleEncode(std::wstring(L" ") + str, locale, replacement, 1, &made_progress); + if(!made_progress) + { + return LocaleEncode(str, locale, replacement, 2); + } + } + } else if(retry == 1) + { + if(result == codecvt_type::error && in_next == in_begin && out_next == out_begin) + { + *progress = false; + } else + { + *progress = true; + } + return std::string(); + } + if(result == codecvt_type::error) + { + ++in_next; + *out_next = replacement; + ++out_next; + } + in_begin = in_next; + out_begin = out_next; + } while((result == codecvt_type::error && in_next < in_end && out_next < out_end) || (retry == 2 && in_next < in_end)); + return std::string(&(out[0]), out_next); +} + +static std::wstring FromLocaleCpp(const std::string &str, wchar_t replacement) +{ + try + { + std::locale locale(""); // user locale + return String::LocaleDecode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } catch(...) + { + // nothing + } + try + { + std::locale locale; // current c++ locale + return String::LocaleDecode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } catch(...) + { + // nothing + } + try + { + std::locale locale = std::locale::classic(); // "C" locale + return String::LocaleDecode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } catch(...) + { + // nothing + } + MPT_ASSERT_NOTREACHED(); + return String::FromAscii(str, replacement); // fallback +} + +static std::string ToLocaleCpp(const std::wstring &str, char replacement) +{ + try + { + std::locale locale(""); // user locale + return String::LocaleEncode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } catch(...) + { + // nothing + } + try + { + std::locale locale; // current c++ locale + return String::LocaleEncode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } catch(...) + { + // nothing + } + try + { + std::locale locale = std::locale::classic(); // "C" locale + return String::LocaleEncode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } catch(...) + { + // nothing + } + MPT_ASSERT_NOTREACHED(); + return String::ToAscii(str, replacement); // fallback +} + + +template +static std::wstring FromLocale(const Tsrcstring &str, wchar_t replacement = L'\uFFFD') +{ + std::string tmp(str.begin(), str.end()); + return FromLocaleCpp(tmp, replacement); +} +template <> +std::wstring FromLocale(const std::string &str, wchar_t replacement) +{ + return FromLocaleCpp(str, replacement); +} + +template +static Tdststring ToLocale(const std::wstring &str, char replacement = '?') +{ + std::string tmp = ToLocaleCpp(str, replacement); + return Tdststring(tmp.begin(), tmp.end()); +} +template <> +std::string ToLocale(const std::wstring &str, char replacement) +{ + return ToLocaleCpp(str, replacement); +} + + +#endif // MPT_ENABLE_CHARSET_LOCALE && !MPT_LOCALE_ASSUME_CHARSET + +template +static widestring FromUTF8(const Tsrcstring &str, widechar replacement = wide_default_replacement) +{ + const Tsrcstring &in = str; + + widestring out; + + // state: + std::size_t charsleft = 0; + char32_t ucs4 = 0; + + for ( uint8 c : in ) { + + if ( charsleft == 0 ) { + + if ( ( c & 0x80 ) == 0x00 ) { + out.push_back( (widechar)c ); + } else if ( ( c & 0xE0 ) == 0xC0 ) { + ucs4 = c & 0x1F; + charsleft = 1; + } else if ( ( c & 0xF0 ) == 0xE0 ) { + ucs4 = c & 0x0F; + charsleft = 2; + } else if ( ( c & 0xF8 ) == 0xF0 ) { + ucs4 = c & 0x07; + charsleft = 3; + } else { + out.push_back( replacement ); + ucs4 = 0; + charsleft = 0; + } + + } else { + + if ( ( c & 0xC0 ) != 0x80 ) { + out.push_back( replacement ); + ucs4 = 0; + charsleft = 0; + } + ucs4 <<= 6; + ucs4 |= c & 0x3F; + charsleft--; + + if ( charsleft == 0 ) { + if constexpr ( sizeof( widechar ) == 2 ) { + if ( ucs4 > 0x1fffff ) { + out.push_back( replacement ); + ucs4 = 0; + charsleft = 0; + } + if ( ucs4 <= 0xffff ) { + out.push_back( static_cast(ucs4) ); + } else { + uint32 surrogate = static_cast(ucs4) - 0x10000; + uint16 hi_sur = static_cast( ( 0x36 << 10 ) | ( (surrogate>>10) & ((1<<10)-1) ) ); + uint16 lo_sur = static_cast( ( 0x37 << 10 ) | ( (surrogate>> 0) & ((1<<10)-1) ) ); + out.push_back( hi_sur ); + out.push_back( lo_sur ); + } + } else { + out.push_back( static_cast( ucs4 ) ); + } + ucs4 = 0; + } + + } + + } + + if ( charsleft != 0 ) { + out.push_back( replacement ); + ucs4 = 0; + charsleft = 0; + } + + return out; + +} + +template +static Tdststring ToUTF8(const widestring &str, char replacement = '?') +{ + const widestring &in = str; + + Tdststring out; + + for ( std::size_t i=0; i( wc ); + if ( i + 1 < in.length() ) { + // check for surrogate pair + uint16 hi_sur = in[i+0]; + uint16 lo_sur = in[i+1]; + if ( hi_sur >> 10 == 0x36 && lo_sur >> 10 == 0x37 ) { + // surrogate pair + ++i; + hi_sur &= (1<<10)-1; + lo_sur &= (1<<10)-1; + ucs4 = ( static_cast(hi_sur) << 10 ) | ( static_cast(lo_sur) << 0 ); + } else { + // no surrogate pair + ucs4 = static_cast( c ); + } + } else { + // no surrogate possible + ucs4 = static_cast( c ); + } + } else { + ucs4 = static_cast( wc ); + } + + if ( ucs4 > 0x1fffff ) { + out.push_back( replacement ); + continue; + } + + uint8 utf8[6]; + std::size_t numchars = 0; + for ( numchars = 0; numchars < 6; numchars++ ) { + utf8[numchars] = ucs4 & 0x3F; + ucs4 >>= 6; + if ( ucs4 == 0 ) { + break; + } + } + numchars++; + + if ( numchars == 1 ) { + out.push_back( utf8[0] ); + continue; + } + + if ( numchars == 2 && utf8[numchars-1] == 0x01 ) { + // generate shortest form + out.push_back( utf8[0] | 0x40 ); + continue; + } + + std::size_t charsleft = numchars; + while ( charsleft > 0 ) { + if ( charsleft == numchars ) { + out.push_back( utf8[ charsleft - 1 ] | ( ((1< +static Tdststring EncodeImpl(Charset charset, const widestring &src) +{ + static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); + static_assert((std::is_same::value)); + #if defined(MPT_ENABLE_CHARSET_LOCALE) + #if defined(MPT_LOCALE_ASSUME_CHARSET) + if(charset == Charset::Locale) + { + charset = MPT_LOCALE_ASSUME_CHARSET; + } + #endif + #endif + #if MPT_OS_WINDOWS + if(HasCharset(charset)) + { + return EncodeCodepage(CharsetToCodepage(charset), src); + } + #endif + switch(charset) + { +#if defined(MPT_ENABLE_CHARSET_LOCALE) + #if defined(MPT_LOCALE_ASSUME_CHARSET) + case Charset::Locale: MPT_ASSERT_NOTREACHED(); break; + #else + case Charset::Locale: return String::ToLocale(src); break; + #endif +#endif + case Charset::UTF8: return String::ToUTF8(src); break; + case Charset::ASCII: return String::ToAscii(src); break; + case Charset::ISO8859_1: return String::ToISO_8859_1(src); break; + case Charset::ISO8859_15: return String::To8bit(src, CharsetTableISO8859_15); break; + case Charset::CP437: return String::To8bit(src, CharsetTableCP437); break; + case Charset::CP437AMS: return String::To8bit(src, CharsetTableCP437AMS); break; + case Charset::CP437AMS2: return String::To8bit(src, CharsetTableCP437AMS2); break; + case Charset::Windows1252: return String::To8bit(src, CharsetTableWindows1252); break; + } + return Tdststring(); +} + + +// templated on 8bit strings because of type-safe variants +template +static widestring DecodeImpl(Charset charset, const Tsrcstring &src) +{ + static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); + static_assert((std::is_same::value)); + #if defined(MPT_ENABLE_CHARSET_LOCALE) + #if defined(MPT_LOCALE_ASSUME_CHARSET) + if(charset == Charset::Locale) + { + charset = MPT_LOCALE_ASSUME_CHARSET; + } + #endif + #endif + #if MPT_OS_WINDOWS + if(HasCharset(charset)) + { + return DecodeCodepage(CharsetToCodepage(charset), src); + } + #endif + switch(charset) + { +#if defined(MPT_ENABLE_CHARSET_LOCALE) + #if defined(MPT_LOCALE_ASSUME_CHARSET) + case Charset::Locale: MPT_ASSERT_NOTREACHED(); break; + #else + case Charset::Locale: return String::FromLocale(src); break; + #endif +#endif + case Charset::UTF8: return String::FromUTF8(src); break; + case Charset::ASCII: return String::FromAscii(src); break; + case Charset::ISO8859_1: return String::FromISO_8859_1(src); break; + case Charset::ISO8859_15: return String::From8bit(src, CharsetTableISO8859_15); break; + case Charset::CP437: return String::From8bit(src, CharsetTableCP437); break; + case Charset::CP437AMS: return String::From8bit(src, CharsetTableCP437AMS); break; + case Charset::CP437AMS2: return String::From8bit(src, CharsetTableCP437AMS2); break; + case Charset::Windows1252: return String::From8bit(src, CharsetTableWindows1252); break; + } + return widestring(); +} + + +// templated on 8bit strings because of type-safe variants +template +static Tdststring ConvertImpl(Charset to, Charset from, const Tsrcstring &src) +{ + static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); + static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); + if(to == from) + { + const typename Tsrcstring::value_type * src_beg = src.data(); + const typename Tsrcstring::value_type * src_end = src_beg + src.size(); + return Tdststring(reinterpret_cast(src_beg), reinterpret_cast(src_end)); + } + return EncodeImpl(to, DecodeImpl(from, src)); +} + + + +} // namespace String + + +bool IsUTF8(const std::string &str) +{ + return (str == String::EncodeImpl(mpt::Charset::UTF8, String::DecodeImpl(mpt::Charset::UTF8, str))); +} + + +#if MPT_WSTRING_CONVERT +std::wstring ToWide(Charset from, const std::string &str) +{ + return String::DecodeImpl(from, str); +} +#if defined(MPT_ENABLE_CHARSET_LOCALE) +std::wstring ToWide(const mpt::lstring &str) +{ + return String::DecodeImpl(Charset::Locale, str); +} +#endif // MPT_ENABLE_CHARSET_LOCALE +#endif + +#if MPT_WSTRING_CONVERT +std::string ToCharset(Charset to, const std::wstring &str) +{ + return String::EncodeImpl(to, str); +} +#endif +std::string ToCharset(Charset to, Charset from, const std::string &str) +{ + return String::ConvertImpl(to, from, str); +} +#if defined(MPT_ENABLE_CHARSET_LOCALE) +std::string ToCharset(Charset to, const mpt::lstring &str) +{ + return String::ConvertImpl(to, Charset::Locale, str); +} +#endif // MPT_ENABLE_CHARSET_LOCALE + +#if defined(MPT_ENABLE_CHARSET_LOCALE) +#if MPT_WSTRING_CONVERT +mpt::lstring ToLocale(const std::wstring &str) +{ + return String::EncodeImpl(Charset::Locale, str); +} +#endif +mpt::lstring ToLocale(Charset from, const std::string &str) +{ + return String::ConvertImpl(Charset::Locale, from, str); +} +#endif // MPT_ENABLE_CHARSET_LOCALE + +#if MPT_OS_WINDOWS +#if MPT_WSTRING_CONVERT +mpt::winstring ToWin(const std::wstring &str) +{ + #ifdef UNICODE + return str; + #else + return ToLocale(str); + #endif +} +#endif +mpt::winstring ToWin(Charset from, const std::string &str) +{ + #ifdef UNICODE + return ToWide(from, str); + #else + return ToLocale(from, str); + #endif +} +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::winstring ToWin(const mpt::lstring &str) +{ + #ifdef UNICODE + return ToWide(str); + #else + return str; + #endif +} +#endif // MPT_ENABLE_CHARSET_LOCALE +#endif // MPT_OS_WINDOWS + + +#if defined(MPT_WITH_MFC) + +CString ToCString(const std::wstring &str) +{ + #ifdef UNICODE + return str.c_str(); + #else + return ToCharset(Charset::Locale, str).c_str(); + #endif +} +CString ToCString(Charset from, const std::string &str) +{ + #ifdef UNICODE + return ToWide(from, str).c_str(); + #else + return ToCharset(Charset::Locale, from, str).c_str(); + #endif +} +std::wstring ToWide(const CString &str) +{ + #ifdef UNICODE + return str.GetString(); + #else + return ToWide(Charset::Locale, str.GetString()); + #endif +} +std::string ToCharset(Charset to, const CString &str) +{ + #ifdef UNICODE + return ToCharset(to, str.GetString()); + #else + return ToCharset(to, Charset::Locale, str.GetString()); + #endif +} +#if defined(MPT_ENABLE_CHARSET_LOCALE) +CString ToCString(const mpt::lstring &str) +{ + #ifdef UNICODE + return ToWide(str).c_str(); + #else + return str.c_str(); + #endif +} +mpt::lstring ToLocale(const CString &str) +{ + #ifdef UNICODE + return String::EncodeImpl(Charset::Locale, str.GetString()); + #else + return str.GetString(); + #endif +} +#endif // MPT_ENABLE_CHARSET_LOCALE +#if MPT_OS_WINDOWS +mpt::winstring ToWin(const CString &str) +{ + return str.GetString(); +} +#endif // MPT_OS_WINDOWS + +#endif // MPT_WITH_MFC + + +#if MPT_USTRING_MODE_WIDE +// inline +#else // !MPT_USTRING_MODE_WIDE +#if MPT_WSTRING_CONVERT +mpt::ustring ToUnicode(const std::wstring &str) +{ + return String::EncodeImpl(mpt::Charset::UTF8, str); +} +#endif +mpt::ustring ToUnicode(Charset from, const std::string &str) +{ + return String::ConvertImpl(mpt::Charset::UTF8, from, str); +} +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::ustring ToUnicode(const mpt::lstring &str) +{ + return String::ConvertImpl(mpt::Charset::UTF8, mpt::Charset::Locale, str); +} +#endif // MPT_ENABLE_CHARSET_LOCALE +#if defined(MPT_WITH_MFC) +mpt::ustring ToUnicode(const CString &str) +{ + #ifdef UNICODE + return String::EncodeImpl(mpt::Charset::UTF8, str.GetString()); + #else // !UNICODE + return String::ConvertImpl(mpt::Charset::UTF8, mpt::Charset::Locale, str.GetString()); + #endif // UNICODE +} +#endif // MPT_WITH_MFC +#endif // MPT_USTRING_MODE_WIDE + +#if MPT_USTRING_MODE_WIDE +// nothing, std::wstring overloads will catch all stuff +#else // !MPT_USTRING_MODE_WIDE +#if MPT_WSTRING_CONVERT +std::wstring ToWide(const mpt::ustring &str) +{ + return String::DecodeImpl(mpt::Charset::UTF8, str); +} +#endif +std::string ToCharset(Charset to, const mpt::ustring &str) +{ + return String::ConvertImpl(to, mpt::Charset::UTF8, str); +} +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::lstring ToLocale(const mpt::ustring &str) +{ + return String::ConvertImpl(mpt::Charset::Locale, mpt::Charset::UTF8, str); +} +#endif // MPT_ENABLE_CHARSET_LOCALE +#if MPT_OS_WINDOWS +mpt::winstring ToWin(const mpt::ustring &str) +{ + #ifdef UNICODE + return String::DecodeImpl(mpt::Charset::UTF8, str); + #else + return String::ConvertImpl(mpt::Charset::Locale, mpt::Charset::UTF8, str); + #endif +} +#endif // MPT_OS_WINDOWS +#if defined(MPT_WITH_MFC) +CString ToCString(const mpt::ustring &str) +{ + #ifdef UNICODE + return String::DecodeImpl(mpt::Charset::UTF8, str).c_str(); + #else // !UNICODE + return String::ConvertImpl(mpt::Charset::Locale, mpt::Charset::UTF8, str).c_str(); + #endif // UNICODE +} +#endif // MPT_WITH_MFC +#endif // MPT_USTRING_MODE_WIDE + + + + + +static mpt::Charset CharsetFromCodePage(uint16 codepage, mpt::Charset fallback, bool * isFallback = nullptr) +{ + mpt::Charset result = fallback; + switch(codepage) + { + case 65001: + result = mpt::Charset::UTF8; + if(isFallback) *isFallback = false; + break; + case 20127: + result = mpt::Charset::ASCII; + if(isFallback) *isFallback = false; + break; + case 28591: + result = mpt::Charset::ISO8859_1; + if(isFallback) *isFallback = false; + break; + case 28605: + result = mpt::Charset::ISO8859_15; + if(isFallback) *isFallback = false; + break; + case 437: + result = mpt::Charset::CP437; + if(isFallback) *isFallback = false; + break; + case 1252: + result = mpt::Charset::Windows1252; + if(isFallback) *isFallback = false; + break; + default: + result = fallback; + if(isFallback) *isFallback = true; + break; + } + return result; +} + +mpt::ustring ToUnicode(uint16 codepage, mpt::Charset fallback, const std::string &str) +{ + #if MPT_OS_WINDOWS + mpt::ustring result; + bool noCharsetMatch = true; + mpt::Charset charset = mpt::CharsetFromCodePage(codepage, fallback, &noCharsetMatch); + if(noCharsetMatch && mpt::String::TestCodePage(codepage)) + { + result = mpt::ToUnicode(mpt::String::DecodeCodepage(codepage, str)); + } else + { + result = mpt::ToUnicode(charset, str); + } + return result; + #else // !MPT_OS_WINDOWS + return mpt::ToUnicode(mpt::CharsetFromCodePage(codepage, fallback), str); + #endif // MPT_OS_WINDOWS +} + + + + + +char ToLowerCaseAscii(char c) +{ + if('A' <= c && c <= 'Z') + { + c += 'a' - 'A'; + } + return c; +} + +char ToUpperCaseAscii(char c) +{ + if('a' <= c && c <= 'z') + { + c -= 'a' - 'A'; + } + return c; +} + +std::string ToLowerCaseAscii(std::string s) +{ + std::transform(s.begin(), s.end(), s.begin(), static_cast(&mpt::ToLowerCaseAscii)); + return s; +} + +std::string ToUpperCaseAscii(std::string s) +{ + std::transform(s.begin(), s.end(), s.begin(), static_cast(&mpt::ToUpperCaseAscii)); + return s; +} + +int CompareNoCaseAscii(const char *a, const char *b, std::size_t n) +{ + while(n--) + { + unsigned char ac = static_cast(mpt::ToLowerCaseAscii(*a)); + unsigned char bc = static_cast(mpt::ToLowerCaseAscii(*b)); + if(ac != bc) + { + return ac < bc ? -1 : 1; + } else if(!ac && !bc) + { + return 0; + } + ++a; + ++b; + } + return 0; +} + +int CompareNoCaseAscii(std::string_view a, std::string_view b) +{ + for(std::size_t i = 0; i < std::min(a.length(), b.length()); ++i) + { + unsigned char ac = static_cast(mpt::ToLowerCaseAscii(a[i])); + unsigned char bc = static_cast(mpt::ToLowerCaseAscii(b[i])); + if(ac != bc) + { + return ac < bc ? -1 : 1; + } else if(!ac && !bc) + { + return 0; + } + } + if(a.length() == b.length()) + { + return 0; + } + return a.length() < b.length() ? -1 : 1; +} + +int CompareNoCaseAscii(const std::string &a, const std::string &b) +{ + return CompareNoCaseAscii(std::string_view(a), std::string_view(b)); +} + + +#if defined(MODPLUG_TRACKER) + +mpt::ustring ToLowerCase(const mpt::ustring &s) +{ + #if defined(MPT_WITH_MFC) + #if defined(UNICODE) + CString tmp = mpt::ToCString(s); + tmp.MakeLower(); + return mpt::ToUnicode(tmp); + #else // !UNICODE + CStringW tmp = mpt::ToWide(s).c_str(); + tmp.MakeLower(); + return mpt::ToUnicode(tmp.GetString()); + #endif // UNICODE + #else // !MPT_WITH_MFC + std::wstring ws = mpt::ToWide(s); + std::transform(ws.begin(), ws.end(), ws.begin(), &std::towlower); + return mpt::ToUnicode(ws); + #endif // MPT_WITH_MFC +} + +mpt::ustring ToUpperCase(const mpt::ustring &s) +{ + #if defined(MPT_WITH_MFC) + #if defined(UNICODE) + CString tmp = mpt::ToCString(s); + tmp.MakeUpper(); + return mpt::ToUnicode(tmp); + #else // !UNICODE + CStringW tmp = mpt::ToWide(s).c_str(); + tmp.MakeUpper(); + return mpt::ToUnicode(tmp.GetString()); + #endif // UNICODE + #else // !MPT_WITH_MFC + std::wstring ws = mpt::ToWide(s); + std::transform(ws.begin(), ws.end(), ws.begin(), &std::towlower); + return mpt::ToUnicode(ws); + #endif // MPT_WITH_MFC +} + +#endif // MODPLUG_TRACKER + + + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptString.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptString.h new file mode 100644 index 000000000..03f2da121 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptString.h @@ -0,0 +1,703 @@ +/* + * mptString.h + * ---------- + * Purpose: Small string-related utilities, number and message formatting. + * Notes : Currently none. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + +#include "mptAlloc.h" +#include "mptBaseTypes.h" +#include "mptSpan.h" + +#include +#include +#include +#include + +#include + + +OPENMPT_NAMESPACE_BEGIN + + +namespace mpt +{ + + + +template inline span as_span(std::basic_string & str) { return span(&(str[0]), str.length()); } + +template inline span as_span(const std::basic_string & str) { return span(&(str[0]), str.length()); } + + + +template inline std::vector::type> make_vector(const std::basic_string & str) { return std::vector::type>(str.begin(), str.end()); } + + + +// string_traits abstract the API of underlying string classes, in particular they allow adopting to CString without having to specialize for CString explicitly + +template +struct string_traits +{ + + using string_type = Tstring; + using size_type = typename string_type::size_type; + using char_type = typename string_type::value_type; + + static inline std::size_t length(const string_type &str) { return str.length(); } + + static inline void reserve(string_type &str, std::size_t size) { str.reserve(size); } + + static inline string_type& append(string_type &str, const string_type &a) { return str.append(a); } + static inline string_type& append(string_type &str, string_type &&a) { return str.append(std::move(a)); } + static inline string_type& append(string_type &str, std::size_t count, char_type c) { return str.append(count, c); } + + static inline string_type pad(string_type str, std::size_t left, std::size_t right) + { + str.insert(str.begin(), left, char_type(' ')); + str.insert(str.end(), right, char_type(' ')); + return str; + } + +}; + +#if defined(MPT_WITH_MFC) +template <> +struct string_traits +{ + + using string_type = CString; + using size_type = int; + using char_type = typename CString::XCHAR; + + static inline size_type length(const string_type &str) { return str.GetLength(); } + + static inline void reserve(string_type &str, size_type size) { str.Preallocate(size); } + + static inline string_type& append(string_type &str, const string_type &a) { str += a; return str; } + static inline string_type& append(string_type &str, size_type count, char_type c) { while(count--) str.AppendChar(c); return str; } + + static inline string_type pad(const string_type &str, size_type left, size_type right) + { + CString tmp; + while(left--) tmp.AppendChar(char_type(' ')); + tmp += str; + while(right--) tmp.AppendChar(char_type(' ')); + return tmp; + } + +}; +#endif // MPT_WITH_MFC + + + +namespace String +{ + + +template struct Traits { + static MPT_FORCEINLINE const char * GetDefaultWhitespace() noexcept { return " \n\r\t"; } + static MPT_FORCEINLINE bool IsLineEnding(char c) noexcept { return c == '\r' || c == '\n'; } +}; + +template <> struct Traits { + static MPT_FORCEINLINE const char * GetDefaultWhitespace() noexcept { return " \n\r\t"; } + static MPT_FORCEINLINE bool IsLineEnding(char c) noexcept { return c == '\r' || c == '\n'; } +}; + +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) +template <> struct Traits { + static MPT_FORCEINLINE const wchar_t * GetDefaultWhitespace() noexcept { return L" \n\r\t"; } + static MPT_FORCEINLINE bool IsLineEnding(wchar_t c) noexcept { return c == L'\r' || c == L'\n'; } +}; +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR + + +// Remove whitespace at start of string +template +inline Tstring LTrim(Tstring str, const Tstring &whitespace = Tstring(mpt::String::Traits::GetDefaultWhitespace())) +{ + typename Tstring::size_type pos = str.find_first_not_of(whitespace); + if(pos != Tstring::npos) + { + str.erase(str.begin(), str.begin() + pos); + } else if(pos == Tstring::npos && str.length() > 0 && str.find_last_of(whitespace) == str.length() - 1) + { + return Tstring(); + } + return str; +} + + +// Remove whitespace at end of string +template +inline Tstring RTrim(Tstring str, const Tstring &whitespace = Tstring(mpt::String::Traits::GetDefaultWhitespace())) +{ + typename Tstring::size_type pos = str.find_last_not_of(whitespace); + if(pos != Tstring::npos) + { + str.erase(str.begin() + pos + 1, str.end()); + } else if(pos == Tstring::npos && str.length() > 0 && str.find_first_of(whitespace) == 0) + { + return Tstring(); + } + return str; +} + + +// Remove whitespace at start and end of string +template +inline Tstring Trim(Tstring str, const Tstring &whitespace = Tstring(mpt::String::Traits::GetDefaultWhitespace())) +{ + return RTrim(LTrim(str, whitespace), whitespace); +} + + +template +inline Tstring Replace(Tstring str, const Tstring2 &oldStr_, const Tstring3 &newStr_) +{ + std::size_t pos = 0; + const Tstring oldStr = oldStr_; + const Tstring newStr = newStr_; + while((pos = str.find(oldStr, pos)) != Tstring::npos) + { + str.replace(pos, oldStr.length(), newStr); + pos += newStr.length(); + } + return str; +} + + +} // namespace String + + +inline std::string truncate(std::string str, std::size_t maxLen) +{ + if(str.length() > maxLen) + { + str.resize(maxLen); + } + return str; +} + + +enum class Charset { + + UTF8, + + ASCII, // strictly 7-bit ASCII + + ISO8859_1, + ISO8859_15, + + CP437, + CP437AMS, + CP437AMS2, + + Windows1252, + +#if defined(MPT_ENABLE_CHARSET_LOCALE) + Locale, // CP_ACP on windows, current C locale otherwise +#endif // MPT_ENABLE_CHARSET_LOCALE + +}; + + + +// source code / preprocessor (i.e. # token) +inline constexpr Charset CharsetSource = Charset::ASCII; + +// debug log files +inline constexpr Charset CharsetLogfile = Charset::UTF8; + +// std::clog / std::cout / std::cerr +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS && defined(MPT_ENABLE_CHARSET_LOCALE) +inline constexpr Charset CharsetStdIO = Charset::Locale; +#else +inline constexpr Charset CharsetStdIO = Charset::UTF8; +#endif + +// std::exception::what() +#if defined(MPT_ENABLE_CHARSET_LOCALE) +inline constexpr Charset CharsetException = Charset::Locale; +#else +inline constexpr Charset CharsetException = Charset::UTF8; +#endif + +// Locale in tracker builds, UTF8 in non-locale-aware libopenmpt builds. +#if defined(MPT_ENABLE_CHARSET_LOCALE) +inline constexpr Charset CharsetLocaleOrUTF8 = Charset::Locale; +#else +inline constexpr Charset CharsetLocaleOrUTF8 = Charset::UTF8; +#endif + + + +// Checks if the std::string represents an UTF8 string. +// This is currently implemented as converting to std::wstring and back assuming UTF8 both ways, +// and comparing the result to the original string. +// Caveats: +// - can give false negatives because of possible unicode normalization during conversion +// - can give false positives if the 8bit encoding contains high-ascii only in valid utf8 groups +// - slow because of double conversion +bool IsUTF8(const std::string &str); + + +#define MPT_CHAR_TYPE char +#define MPT_CHAR(x) x +#define MPT_LITERAL(x) x +#define MPT_STRING(x) std::string( x ) + +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) +#define MPT_WCHAR_TYPE wchar_t +#define MPT_WCHAR(x) L ## x +#define MPT_WLITERAL(x) L ## x +#define MPT_WSTRING(x) std::wstring( L ## x ) +#else // MPT_COMPILER_QUIRK_NO_WCHAR +#define MPT_WCHAR_TYPE char32_t +#define MPT_WCHAR(x) U ## x +#define MPT_WLITERAL(x) U ## x +#define MPT_WSTRING(x) std::u32string( U ## x ) +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR + + +template +struct charset_char_traits : std::char_traits { + static mpt::Charset charset() { return charset_tag; } +}; +#define MPT_ENCODED_STRING_TYPE(charset) std::basic_string< char, mpt::charset_char_traits< charset > > + + +#if defined(MPT_ENABLE_CHARSET_LOCALE) + +using lstring = MPT_ENCODED_STRING_TYPE(mpt::Charset::Locale); + +#endif // MPT_ENABLE_CHARSET_LOCALE + +#if MPT_OS_WINDOWS + +template struct windows_char_traits { }; +template <> struct windows_char_traits { using string_type = mpt::lstring; }; +template <> struct windows_char_traits { using string_type = std::wstring; }; + +#ifdef UNICODE +using tstring = windows_char_traits::string_type; +#else +using tstring = windows_char_traits::string_type; +#endif + +using winstring = mpt::tstring; + +#endif // MPT_OS_WINDOWS + + +#if MPT_ENABLE_U8STRING + +#if MPT_CXX_AT_LEAST(20) + +using u8string = std::u8string; + +#define MPT_U8CHAR_TYPE char8_t +#define MPT_U8CHAR(x) u8 ## x +#define MPT_U8LITERAL(x) u8 ## x +#define MPT_U8STRING(x) std::u8string( u8 ## x ) + +#else // !C++20 + +using u8string = MPT_ENCODED_STRING_TYPE(mpt::Charset::UTF8); + +#define MPT_U8CHAR_TYPE char +#define MPT_U8CHAR(x) x +#define MPT_U8LITERAL(x) x +#define MPT_U8STRING(x) mpt::u8string( x ) + +// mpt::u8string is a moderately type-safe string that is meant to contain +// UTF-8 encoded char bytes. +// +// mpt::u8string is not implicitely convertible to/from std::string, but +// it is convertible to/from C strings the same way as std::string is. +// +// The implementation of mpt::u8string is a compromise of compatibilty +// with implementation-defined STL details, efficiency, source code size, +// executable bloat, type-safety and simplicity. +// +// mpt::u8string is not meant to be used directly though. +// mpt::u8string is meant as an alternative implementaion to std::wstring +// for implementing the unicode string type mpt::ustring. + +#endif // C++20 + +#endif // MPT_ENABLE_U8STRING + + +#if MPT_WSTRING_CONVERT +// Convert to a wide character string. +// The wide encoding is UTF-16 or UTF-32, based on sizeof(wchar_t). +// If str does not contain any invalid characters, this conversion is lossless. +// Invalid source bytes will be replaced by some replacement character or string. +inline std::wstring ToWide(const std::wstring &str) { return str; } +inline std::wstring ToWide(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); } +std::wstring ToWide(Charset from, const std::string &str); +inline std::wstring ToWide(Charset from, const char * str) { return ToWide(from, str ? std::string(str) : std::string()); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +std::wstring ToWide(const mpt::lstring &str); +#endif // MPT_ENABLE_CHARSET_LOCALE +#endif + +// Convert to a string encoded in the 'to'-specified character set. +// If str does not contain any invalid characters, +// this conversion will be lossless iff, and only iff, +// 'to' is UTF8. +// Invalid source bytes or characters that are not representable in the +// destination charset will be replaced by some replacement character or string. +#if MPT_WSTRING_CONVERT +std::string ToCharset(Charset to, const std::wstring &str); +inline std::string ToCharset(Charset to, const wchar_t * str) { return ToCharset(to, str ? std::wstring(str) : std::wstring()); } +#endif +std::string ToCharset(Charset to, Charset from, const std::string &str); +inline std::string ToCharset(Charset to, Charset from, const char * str) { return ToCharset(to, from, str ? std::string(str) : std::string()); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +std::string ToCharset(Charset to, const mpt::lstring &str); +#endif // MPT_ENABLE_CHARSET_LOCALE + +#if defined(MPT_ENABLE_CHARSET_LOCALE) +#if MPT_WSTRING_CONVERT +mpt::lstring ToLocale(const std::wstring &str); +inline mpt::lstring ToLocale(const wchar_t * str) { return ToLocale(str ? std::wstring(str): std::wstring()); } +#endif +mpt::lstring ToLocale(Charset from, const std::string &str); +inline mpt::lstring ToLocale(Charset from, const char * str) { return ToLocale(from, str ? std::string(str): std::string()); } +inline mpt::lstring ToLocale(const mpt::lstring &str) { return str; } +#endif // MPT_ENABLE_CHARSET_LOCALE + +#if MPT_OS_WINDOWS +#if MPT_WSTRING_CONVERT +mpt::winstring ToWin(const std::wstring &str); +inline mpt::winstring ToWin(const wchar_t * str) { return ToWin(str ? std::wstring(str): std::wstring()); } +#endif +mpt::winstring ToWin(Charset from, const std::string &str); +inline mpt::winstring ToWin(Charset from, const char * str) { return ToWin(from, str ? std::string(str): std::string()); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::winstring ToWin(const mpt::lstring &str); +#endif // MPT_ENABLE_CHARSET_LOCALE +#endif // MPT_OS_WINDOWS + + +#if defined(MPT_WITH_MFC) +#if !(MPT_WSTRING_CONVERT) +#error "MFC depends on MPT_WSTRING_CONVERT)" +#endif + +// Convert to a MFC CString. The CString encoding depends on UNICODE. +// This should also be used when converting to TCHAR strings. +// If UNICODE is defined, this is a completely lossless operation. +inline CString ToCString(const CString &str) { return str; } +CString ToCString(const std::wstring &str); +inline CString ToCString(const wchar_t * str) { return ToCString(str ? std::wstring(str) : std::wstring()); } +CString ToCString(Charset from, const std::string &str); +inline CString ToCString(Charset from, const char * str) { return ToCString(from, str ? std::string(str) : std::string()); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +CString ToCString(const mpt::lstring &str); +mpt::lstring ToLocale(const CString &str); +#endif // MPT_ENABLE_CHARSET_LOCALE +#if MPT_OS_WINDOWS +mpt::winstring ToWin(const CString &str); +#endif // MPT_OS_WINDOWS + +// Convert from a MFC CString. The CString encoding depends on UNICODE. +// This should also be used when converting from TCHAR strings. +// If UNICODE is defined, this is a completely lossless operation. +std::wstring ToWide(const CString &str); +std::string ToCharset(Charset to, const CString &str); + +#endif // MPT_WITH_MFC + + +// mpt::ustring +// +// mpt::ustring is a string type that can hold unicode strings. +// It is implemented as a std::basic_string either based on wchar_t (i.e. the +// same as std::wstring) or a custom-defined char_traits class that is derived +// from std::char_traits. +// The selection of the underlying implementation is done at compile-time. +// MPT_UCHAR, MPT_ULITERAL and MPT_USTRING are macros that ease construction +// of ustring char literals, ustring char array literals and ustring objects +// from ustring char literals that work consistently in both modes. +// Note that these are not supported for non-ASCII characters appearing in +// the macro argument. +// Also note that, as both UTF8 and UTF16 (it is less of an issue for UTF32) +// are variable-length encodings and mpt::ustring is implemented as a +// std::basic_string, all member functions that require individual character +// access will not work consistently or even at all in a meaningful way. +// This in particular affects operator[], at(), find() and substr(). +// The code makes no effort in preventing these or generating warnings when +// these are used on mpt::ustring objects. However, compiling in the +// respectively other mpt::ustring mode will catch most of these anyway. + +#if MPT_USTRING_MODE_WIDE +#if MPT_USTRING_MODE_UTF8 +#error "MPT_USTRING_MODE_WIDE and MPT_USTRING_MODE_UTF8 are mutually exclusive." +#endif + +using ustring = std::wstring; +using uchar = wchar_t; +#define MPT_UCHAR(x) L ## x +#define MPT_ULITERAL(x) L ## x +#define MPT_USTRING(x) std::wstring( L ## x ) + +#endif // MPT_USTRING_MODE_WIDE + +#if MPT_USTRING_MODE_UTF8 +#if MPT_USTRING_MODE_WIDE +#error "MPT_USTRING_MODE_WIDE and MPT_USTRING_MODE_UTF8 are mutually exclusive." +#endif + +using ustring = mpt::u8string; +using uchar = MPT_U8CHAR_TYPE; +#define MPT_UCHAR(x) MPT_U8CHAR( x ) +#define MPT_ULITERAL(x) MPT_U8LITERAL( x ) +#define MPT_USTRING(x) MPT_U8STRING( x ) + +#endif // MPT_USTRING_MODE_UTF8 + +#define UC_(x) MPT_UCHAR(x) +#define UL_(x) MPT_ULITERAL(x) +#define U_(x) MPT_USTRING(x) + +#if MPT_USTRING_MODE_WIDE +#if !(MPT_WSTRING_CONVERT) +#error "MPT_USTRING_MODE_WIDE depends on MPT_WSTRING_CONVERT)" +#endif +inline mpt::ustring ToUnicode(const std::wstring &str) { return str; } +inline mpt::ustring ToUnicode(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); } +inline mpt::ustring ToUnicode(Charset from, const std::string &str) { return ToWide(from, str); } +inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +inline mpt::ustring ToUnicode(const mpt::lstring &str) { return ToWide(str); } +#endif // MPT_ENABLE_CHARSET_LOCALE +#if defined(MPT_WITH_MFC) +inline mpt::ustring ToUnicode(const CString &str) { return ToWide(str); } +#endif // MFC +#else // !MPT_USTRING_MODE_WIDE +inline mpt::ustring ToUnicode(const mpt::ustring &str) { return str; } +#if MPT_WSTRING_CONVERT +mpt::ustring ToUnicode(const std::wstring &str); +inline mpt::ustring ToUnicode(const wchar_t * str) { return ToUnicode(str ? std::wstring(str) : std::wstring()); } +#endif +mpt::ustring ToUnicode(Charset from, const std::string &str); +inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); } +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::ustring ToUnicode(const mpt::lstring &str); +#endif // MPT_ENABLE_CHARSET_LOCALE +#if defined(MPT_WITH_MFC) +mpt::ustring ToUnicode(const CString &str); +#endif // MPT_WITH_MFC +#endif // MPT_USTRING_MODE_WIDE + +#if MPT_USTRING_MODE_WIDE +#if !(MPT_WSTRING_CONVERT) +#error "MPT_USTRING_MODE_WIDE depends on MPT_WSTRING_CONVERT)" +#endif +// nothing, std::wstring overloads will catch all stuff +#else // !MPT_USTRING_MODE_WIDE +#if MPT_WSTRING_CONVERT +std::wstring ToWide(const mpt::ustring &str); +#endif +std::string ToCharset(Charset to, const mpt::ustring &str); +#if defined(MPT_ENABLE_CHARSET_LOCALE) +mpt::lstring ToLocale(const mpt::ustring &str); +#endif // MPT_ENABLE_CHARSET_LOCALE +#if MPT_OS_WINDOWS +mpt::winstring ToWin(const mpt::ustring &str); +#endif // MPT_OS_WINDOWS +#if defined(MPT_WITH_MFC) +CString ToCString(const mpt::ustring &str); +#endif // MPT_WITH_MFC +#endif // MPT_USTRING_MODE_WIDE + +// The MPT_UTF8 allows specifying UTF8 char arrays. +// The resulting type is mpt::ustring and the construction might require runtime translation, +// i.e. it is NOT generally available at compile time. +// Use explicit UTF8 encoding, +// i.e. U+00FC (LATIN SMALL LETTER U WITH DIAERESIS) would be written as "\xC3\xBC". +#define MPT_UTF8(x) mpt::ToUnicode(mpt::Charset::UTF8, x ) + + + + + +mpt::ustring ToUnicode(uint16 codepage, mpt::Charset fallback, const std::string &str); + + + + + +char ToLowerCaseAscii(char c); +char ToUpperCaseAscii(char c); +std::string ToLowerCaseAscii(std::string s); +std::string ToUpperCaseAscii(std::string s); + +int CompareNoCaseAscii(const char *a, const char *b, std::size_t n); +int CompareNoCaseAscii(std::string_view a, std::string_view b); +int CompareNoCaseAscii(const std::string &a, const std::string &b); + + +#if defined(MODPLUG_TRACKER) + +mpt::ustring ToLowerCase(const mpt::ustring &s); +mpt::ustring ToUpperCase(const mpt::ustring &s); + +#endif // MODPLUG_TRACKER + + + + + +} // namespace mpt + + + + + +// The AnyString types are meant to be used as function argument types only, +// and only during the transition phase to all-unicode strings in the whole codebase. +// Using an AnyString type as function argument avoids the need to overload a function for all the +// different string types that we currently have. +// Warning: These types will silently do charset conversions. Only use them when this can be tolerated. + +// BasicAnyString is convertable to mpt::ustring and constructable from any string at all. +template +class BasicAnyString : public mpt::ustring +{ + +private: + + static mpt::ustring From8bit(const std::string &str) + { + if constexpr(charset == mpt::Charset::UTF8) + { + return mpt::ToUnicode(mpt::Charset::UTF8, str); + } else + { + // auto utf8 detection + if constexpr(tryUTF8) + { + if(mpt::IsUTF8(str)) + { + return mpt::ToUnicode(mpt::Charset::UTF8, str); + } else + { + return mpt::ToUnicode(charset, str); + } + } else + { + return mpt::ToUnicode(charset, str); + } + } + } + +public: + + // 8 bit + BasicAnyString(const char *str) : mpt::ustring(From8bit(str ? str : std::string())) { } + BasicAnyString(const std::string str) : mpt::ustring(From8bit(str)) { } + + // locale +#if defined(MPT_ENABLE_CHARSET_LOCALE) + BasicAnyString(const mpt::lstring str) : mpt::ustring(mpt::ToUnicode(str)) { } +#endif // MPT_ENABLE_CHARSET_LOCALE + + // unicode + BasicAnyString(const mpt::ustring &str) : mpt::ustring(str) { } + BasicAnyString(mpt::ustring &&str) : mpt::ustring(std::move(str)) { } +#if MPT_USTRING_MODE_UTF8 && MPT_WSTRING_CONVERT + BasicAnyString(const std::wstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } +#endif +#if MPT_WSTRING_CONVERT + BasicAnyString(const wchar_t *str) : mpt::ustring(str ? mpt::ToUnicode(str) : mpt::ustring()) { } +#endif + + // mfc +#if defined(MPT_WITH_MFC) + BasicAnyString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { } +#endif // MPT_WITH_MFC + + // fallback for custom string types + template BasicAnyString(const Tstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } + template BasicAnyString(Tstring &&str) : mpt::ustring(mpt::ToUnicode(std::forward(str))) { } + +}; + +// AnyUnicodeString is convertable to mpt::ustring and constructable from any unicode string, +class AnyUnicodeString : public mpt::ustring +{ + +public: + + // locale +#if defined(MPT_ENABLE_CHARSET_LOCALE) + AnyUnicodeString(const mpt::lstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } +#endif // MPT_ENABLE_CHARSET_LOCALE + + // unicode + AnyUnicodeString(const mpt::ustring &str) : mpt::ustring(str) { } + AnyUnicodeString(mpt::ustring &&str) : mpt::ustring(std::move(str)) { } +#if MPT_USTRING_MODE_UTF8 && MPT_WSTRING_CONVERT + AnyUnicodeString(const std::wstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } +#endif +#if MPT_WSTRING_CONVERT + AnyUnicodeString(const wchar_t *str) : mpt::ustring(str ? mpt::ToUnicode(str) : mpt::ustring()) { } +#endif + + // mfc +#if defined(MPT_WITH_MFC) + AnyUnicodeString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { } +#endif // MPT_WITH_MFC + + // fallback for custom string types + template AnyUnicodeString(const Tstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } + template AnyUnicodeString(Tstring &&str) : mpt::ustring(mpt::ToUnicode(std::forward(str))) { } + +}; + +// AnyString +// Try to do the smartest auto-magic we can do. +#if defined(MPT_ENABLE_CHARSET_LOCALE) +using AnyString = BasicAnyString; +#elif MPT_OS_WINDOWS +using AnyString = BasicAnyString; +#else +using AnyString = BasicAnyString; +#endif + +// AnyStringLocale +// char-based strings are assumed to be in locale encoding. +#if defined(MPT_ENABLE_CHARSET_LOCALE) +using AnyStringLocale = BasicAnyString; +#else +using AnyStringLocale = BasicAnyString; +#endif + +// AnyStringUTF8orLocale +// char-based strings are tried in UTF8 first, if this fails, locale is used. +#if defined(MPT_ENABLE_CHARSET_LOCALE) +using AnyStringUTF8orLocale = BasicAnyString; +#else +using AnyStringUTF8orLocale = BasicAnyString; +#endif + +// AnyStringUTF8 +// char-based strings are assumed to be in UTF8. +using AnyStringUTF8 = BasicAnyString; + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptStringBuffer.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptStringBuffer.cpp new file mode 100644 index 000000000..7740e7d7e --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptStringBuffer.cpp @@ -0,0 +1,117 @@ +/* + * mptStringBuffer.cpp + * ------------------- + * Purpose: Various functions for "fixing" char array strings for writing to or + * reading from module files, or for securing char arrays in general. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" + +#include "mptStringBuffer.h" + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt +{ + +namespace String +{ + +namespace detail +{ + +std::string ReadStringBuffer(String::ReadWriteMode mode, const char *srcBuffer, std::size_t srcSize) +{ + + std::string dest; + const char *src = srcBuffer; + + if(mode == nullTerminated || mode == spacePaddedNull) + { + // We assume that the last character of the source buffer is null. + if(srcSize > 0) + { + srcSize -= 1; + } + } + + if(mode == nullTerminated || mode == maybeNullTerminated) + { + + // Copy null-terminated string, stopping at null. + dest.assign(src, std::find(src, src + srcSize, '\0')); + + } else if(mode == spacePadded || mode == spacePaddedNull) + { + + // Copy string over. + dest.assign(src, src + srcSize); + + // Convert null characters to spaces. + std::transform(dest.begin(), dest.end(), dest.begin(), [] (char c) -> char { return (c != '\0') ? c : ' '; }); + + // Trim trailing spaces. + dest = mpt::String::RTrim(dest, std::string(" ")); + + } + + return dest; + +} + +void WriteStringBuffer(String::ReadWriteMode mode, char *destBuffer, const std::size_t destSize, const char *srcBuffer, const std::size_t srcSize) +{ + + MPT_ASSERT(destSize > 0); + + const size_t maxSize = std::min(destSize, srcSize); + char *dst = destBuffer; + const char *src = srcBuffer; + + // First, copy over null-terminated string. + size_t pos = maxSize; + while(pos > 0) + { + if((*dst = *src) == '\0') + { + break; + } + pos--; + dst++; + src++; + } + + if(mode == nullTerminated || mode == maybeNullTerminated) + { + // Fill rest of string with nulls. + std::fill(dst, dst + destSize - maxSize + pos, '\0'); + } else if(mode == spacePadded || mode == spacePaddedNull) + { + // Fill the rest of the destination string with spaces. + std::fill(dst, dst + destSize - maxSize + pos, ' '); + } + + if(mode == nullTerminated || mode == spacePaddedNull) + { + // Make sure that destination is really null-terminated. + SetNullTerminator(destBuffer, destSize); + } + +} + +} // namespace detail + +} // namespace String + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptStringBuffer.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptStringBuffer.h new file mode 100644 index 000000000..4c8681ac9 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptStringBuffer.h @@ -0,0 +1,565 @@ +/* + * mptStringBuffer.h + * ----------------- + * Purpose: Various functions for "fixing" char array strings for writing to or + * reading from module files, or for securing char arrays in general. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + +#include "mptMemory.h" +#include "mptString.h" + +#include +#include +#include + + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt +{ + + +namespace String +{ + + + enum ReadWriteMode : uint8 + { + // Reading / Writing: Standard null-terminated string handling. + nullTerminated = 1, + // Reading: Source string is not guaranteed to be null-terminated (if it fills the whole char array). + // Writing: Destination string is not guaranteed to be null-terminated (if it fills the whole char array). + maybeNullTerminated = 2, + // Reading: String may contain null characters anywhere. They should be treated as spaces. + // Writing: A space-padded string is written. + spacePadded = 3, + // Reading: String may contain null characters anywhere. The last character is ignored (it is supposed to be 0). + // Writing: A space-padded string with a trailing null is written. + spacePaddedNull = 4, + }; + + namespace detail + { + + std::string ReadStringBuffer(String::ReadWriteMode mode, const char *srcBuffer, std::size_t srcSize); + + void WriteStringBuffer(String::ReadWriteMode mode, char *destBuffer, const std::size_t destSize, const char *srcBuffer, const std::size_t srcSize); + + } // namespace detail + + +} // namespace String + + + +template +class StringBufRefImpl +{ +private: + Tchar * buf; + std::size_t size; +public: + // cppcheck false-positive + // cppcheck-suppress uninitMemberVar + explicit StringBufRefImpl(Tchar * buf, std::size_t size) + : buf(buf) + , size(size) + { + static_assert(sizeof(Tchar) == sizeof(typename Tstring::value_type)); + MPT_ASSERT(size > 0); + } + StringBufRefImpl(const StringBufRefImpl &) = delete; + StringBufRefImpl(StringBufRefImpl &&) = default; + StringBufRefImpl & operator = (const StringBufRefImpl &) = delete; + StringBufRefImpl & operator = (StringBufRefImpl &&) = delete; + operator Tstring () const + { + std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 + return Tstring(buf, buf + len); + } + bool empty() const + { + return buf[0] == Tchar('\0'); + } + StringBufRefImpl & operator = (const Tstring & str) + { + std::fill(buf, buf + size, Tchar('\0')); + std::copy(str.data(), str.data() + std::min(str.length(), size - 1), buf); + std::fill(buf + std::min(str.length(), size - 1), buf + size, Tchar('\0')); + return *this; + } +}; + +template +class StringBufRefImpl +{ +private: + const Tchar * buf; + std::size_t size; +public: + // cppcheck false-positive + // cppcheck-suppress uninitMemberVar + explicit StringBufRefImpl(const Tchar * buf, std::size_t size) + : buf(buf) + , size(size) + { + static_assert(sizeof(Tchar) == sizeof(typename Tstring::value_type)); + MPT_ASSERT(size > 0); + } + StringBufRefImpl(const StringBufRefImpl &) = delete; + StringBufRefImpl(StringBufRefImpl &&) = default; + StringBufRefImpl & operator = (const StringBufRefImpl &) = delete; + StringBufRefImpl & operator = (StringBufRefImpl &&) = delete; + operator Tstring () const + { + std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 + return Tstring(buf, buf + len); + } + bool empty() const + { + return buf[0] == Tchar('\0'); + } +}; + +namespace String { +template +inline StringBufRefImpl::type> ReadTypedBuf(Tchar (&buf)[size]) +{ + return StringBufRefImpl::type>(buf, size); +} +template +inline StringBufRefImpl::type> ReadTypedBuf(Tchar * buf, std::size_t size) +{ + return StringBufRefImpl::type>(buf, size); +} +template +inline StringBufRefImpl WriteTypedBuf(Tchar (&buf)[size]) +{ + return StringBufRefImpl(buf, size); +} +template +inline StringBufRefImpl WriteTypedBuf(Tchar * buf, std::size_t size) +{ + return StringBufRefImpl(buf, size); +} +} // namespace String + +namespace String { +template +inline StringBufRefImpl::type>, typename std::add_const::type> ReadAutoBuf(Tchar (&buf)[size]) +{ + return StringBufRefImpl::type>, typename std::add_const::type>(buf, size); +} +template +inline StringBufRefImpl::type>, typename std::add_const::type> ReadAutoBuf(Tchar * buf, std::size_t size) +{ + return StringBufRefImpl::type>, typename std::add_const::type>(buf, size); +} +template +inline StringBufRefImpl::type>, Tchar> WriteAutoBuf(Tchar (&buf)[size]) +{ + return StringBufRefImpl::type>, Tchar>(buf, size); +} +template +inline StringBufRefImpl::type>, Tchar> WriteAutoBuf(Tchar * buf, std::size_t size) +{ + return StringBufRefImpl::type>, Tchar>(buf, size); +} +} // namespace String + +template (0)> struct charbuf; + +template +struct charbuf(0)> +{ +public: + typedef char Tchar; + using char_type = Tchar; + using string_type = std::basic_string; + constexpr std::size_t static_length() const { return len; } +public: + Tchar buf[len]; +public: + charbuf() + { + Clear(buf); + } + charbuf(const charbuf &) = default; + charbuf(charbuf &&) = default; + charbuf & operator = (const charbuf &) = default; + charbuf & operator = (charbuf &&) = default; + const Tchar & operator[](std::size_t i) const { return buf[i]; } + std::string str() const { return static_cast(*this); } + operator string_type () const + { + return mpt::String::ReadAutoBuf(buf); + } + bool empty() const + { + return mpt::String::ReadAutoBuf(buf).empty(); + } + charbuf & operator = (const string_type & str) + { + mpt::String::WriteAutoBuf(buf) = str; + return *this; + } +public: + friend bool operator!=(const std::string & a, const charbuf & b) { return a != b.str(); } + friend bool operator!=(const charbuf & a, const std::string & b) { return a.str() != b; } + friend bool operator==(const std::string & a, const charbuf & b) { return a == b.str(); } + friend bool operator==(const charbuf & a, const std::string & b) { return a.str() == b; } +}; + +template +class StringModeBufRefImpl +{ +private: + Tchar * buf; + std::size_t size; + String::ReadWriteMode mode; +public: + // cppcheck false-positive + // cppcheck-suppress uninitMemberVar + StringModeBufRefImpl(Tchar * buf, std::size_t size, String::ReadWriteMode mode) + : buf(buf) + , size(size) + , mode(mode) + { + static_assert(sizeof(Tchar) == 1); + } + StringModeBufRefImpl(const StringModeBufRefImpl &) = delete; + StringModeBufRefImpl(StringModeBufRefImpl &&) = default; + StringModeBufRefImpl & operator = (const StringModeBufRefImpl &) = delete; + StringModeBufRefImpl & operator = (StringModeBufRefImpl &&) = delete; + operator std::string () const + { + return String::detail::ReadStringBuffer(mode, buf, size); + } + bool empty() const + { + return String::detail::ReadStringBuffer(mode, buf, size).empty(); + } + StringModeBufRefImpl & operator = (const std::string & str) + { + String::detail::WriteStringBuffer(mode, buf, size, str.data(), str.size()); + return *this; + } +}; + +template +class StringModeBufRefImpl +{ +private: + const Tchar * buf; + std::size_t size; + String::ReadWriteMode mode; +public: + // cppcheck false-positive + // cppcheck-suppress uninitMemberVar + StringModeBufRefImpl(const Tchar * buf, std::size_t size, String::ReadWriteMode mode) + : buf(buf) + , size(size) + , mode(mode) + { + static_assert(sizeof(Tchar) == 1); + } + StringModeBufRefImpl(const StringModeBufRefImpl &) = delete; + StringModeBufRefImpl(StringModeBufRefImpl &&) = default; + StringModeBufRefImpl & operator = (const StringModeBufRefImpl &) = delete; + StringModeBufRefImpl & operator = (StringModeBufRefImpl &&) = delete; + operator std::string () const + { + return String::detail::ReadStringBuffer(mode, buf, size); + } + bool empty() const + { + return String::detail::ReadStringBuffer(mode, buf, size).empty(); + } +}; + +namespace String { +template +inline StringModeBufRefImpl::type> ReadBuf(String::ReadWriteMode mode, Tchar (&buf)[size]) +{ + return StringModeBufRefImpl::type>(buf, size, mode); +} +template +inline StringModeBufRefImpl::type> ReadBuf(String::ReadWriteMode mode, Tchar * buf, std::size_t size) +{ + return StringModeBufRefImpl::type>(buf, size, mode); +} +template +inline StringModeBufRefImpl WriteBuf(String::ReadWriteMode mode, Tchar (&buf)[size]) +{ + return StringModeBufRefImpl(buf, size, mode); +} +template +inline StringModeBufRefImpl WriteBuf(String::ReadWriteMode mode, Tchar * buf, std::size_t size) +{ + return StringModeBufRefImpl(buf, size, mode); +} +} // namespace String + +template +struct charbuf +{ +public: + typedef char Tchar; + using char_type = Tchar; + using string_type = std::basic_string; +public: + Tchar buf[len]; +public: + charbuf() = default; + charbuf(const charbuf &) = default; + charbuf(charbuf &&) = default; + charbuf & operator = (const charbuf &) = default; + charbuf & operator = (charbuf &&) = default; + operator string_type () const + { + return mpt::String::ReadBuf(mode, buf); + } + bool empty() const + { + return mpt::String::ReadBuf(mode, buf).empty(); + } + charbuf & operator = (const string_type & str) + { + mpt::String::WriteBuf(mode, buf) = str; + return *this; + } +}; + + +// see MPT_BINARY_STRUCT +template +struct is_binary_safe> : public std::true_type { }; +template +struct is_binary_safe(0)>> : public std::false_type { }; +static_assert(sizeof(mpt::charbuf<7>) == 7); +static_assert(alignof(mpt::charbuf<7>) == 1); +static_assert(std::is_standard_layout>::value); + + +#ifdef MODPLUG_TRACKER + +#if MPT_OS_WINDOWS + +namespace String { +template +inline StringBufRefImpl::type>::string_type, typename std::add_const::type> ReadWinBuf(Tchar (&buf)[size]) +{ + return StringBufRefImpl::type>::string_type, typename std::add_const::type>(buf, size); +} +template +inline StringBufRefImpl::type>::string_type, typename std::add_const::type> ReadWinBuf(Tchar * buf, std::size_t size) +{ + return StringBufRefImpl::type>::string_type, typename std::add_const::type>(buf, size); +} +template +inline StringBufRefImpl::type>::string_type, Tchar> WriteWinBuf(Tchar (&buf)[size]) +{ + return StringBufRefImpl::type>::string_type, Tchar>(buf, size); +} +template +inline StringBufRefImpl::type>::string_type, Tchar> WriteWinBuf(Tchar * buf, std::size_t size) +{ + return StringBufRefImpl::type>::string_type, Tchar>(buf, size); +} +} // namespace String + +#if defined(MPT_WITH_MFC) + +template +class CStringBufRefImpl +{ +private: + Tchar * buf; + std::size_t size; +public: + // cppcheck false-positive + // cppcheck-suppress uninitMemberVar + explicit CStringBufRefImpl(Tchar * buf, std::size_t size) + : buf(buf) + , size(size) + { + MPT_ASSERT(size > 0); + } + CStringBufRefImpl(const CStringBufRefImpl &) = delete; + CStringBufRefImpl(CStringBufRefImpl &&) = default; + CStringBufRefImpl & operator = (const CStringBufRefImpl &) = delete; + CStringBufRefImpl & operator = (CStringBufRefImpl &&) = delete; + operator CString () const + { + std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 + return CString(buf, mpt::saturate_cast(len)); + } + CStringBufRefImpl & operator = (const CString & str) + { + std::fill(buf, buf + size, Tchar('\0')); + std::copy(str.GetString(), str.GetString() + std::min(static_cast(str.GetLength()), size - 1), buf); + buf[size - 1] = Tchar('\0'); + return *this; + } +}; + +template +class CStringBufRefImpl +{ +private: + const Tchar * buf; + std::size_t size; +public: + // cppcheck false-positive + // cppcheck-suppress uninitMemberVar + explicit CStringBufRefImpl(const Tchar * buf, std::size_t size) + : buf(buf) + , size(size) + { + MPT_ASSERT(size > 0); + } + CStringBufRefImpl(const CStringBufRefImpl &) = delete; + CStringBufRefImpl(CStringBufRefImpl &&) = default; + CStringBufRefImpl & operator = (const CStringBufRefImpl &) = delete; + CStringBufRefImpl & operator = (CStringBufRefImpl &&) = delete; + operator CString () const + { + std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 + return CString(buf, mpt::saturate_cast(len)); + } +}; + +namespace String { +template +inline CStringBufRefImpl::type> ReadCStringBuf(Tchar (&buf)[size]) +{ + return CStringBufRefImpl::type>(buf, size); +} +template +inline CStringBufRefImpl::type> ReadCStringBuf(Tchar * buf, std::size_t size) +{ + return CStringBufRefImpl::type>(buf, size); +} +template +inline CStringBufRefImpl WriteCStringBuf(Tchar (&buf)[size]) +{ + return CStringBufRefImpl(buf, size); +} +template +inline CStringBufRefImpl WriteCStringBuf(Tchar * buf, std::size_t size) +{ + return CStringBufRefImpl(buf, size); +} +} // namespace String + +#endif // MPT_WITH_MFC + +#endif // MPT_OS_WINDOWS + +#endif // MODPLUG_TRACKER + + + + + +namespace String +{ + + +#if MPT_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant +#endif // MPT_COMPILER_MSVC + + + // Sets last character to null in given char array. + // Size of the array must be known at compile time. + template + void SetNullTerminator(char (&buffer)[size]) + { + static_assert(size > 0); + buffer[size - 1] = 0; + } + + inline void SetNullTerminator(char *buffer, size_t size) + { + MPT_ASSERT(size > 0); + buffer[size - 1] = 0; + } + +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) + + template + void SetNullTerminator(wchar_t (&buffer)[size]) + { + static_assert(size > 0); + buffer[size - 1] = 0; + } + + inline void SetNullTerminator(wchar_t *buffer, size_t size) + { + MPT_ASSERT(size > 0); + buffer[size - 1] = 0; + } + +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR + + + // Remove any chars after the first null char + template + void FixNullString(char (&buffer)[size]) + { + static_assert(size > 0); + SetNullTerminator(buffer); + size_t pos = 0; + // Find the first null char. + while(pos < size && buffer[pos] != '\0') + { + pos++; + } + // Remove everything after the null char. + while(pos < size) + { + buffer[pos++] = '\0'; + } + } + + inline void FixNullString(std::string & str) + { + for(std::size_t i = 0; i < str.length(); ++i) + { + if(str[i] == '\0') + { + // if we copied \0 in the middle of the buffer, terminate std::string here + str.resize(i); + break; + } + } + } + + + +#if MPT_COMPILER_MSVC +#pragma warning(pop) +#endif // MPT_COMPILER_MSVC + + +} // namespace String + + +} // namespace mpt + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptStringFormat.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptStringFormat.cpp new file mode 100644 index 000000000..76c6868ad --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptStringFormat.cpp @@ -0,0 +1,522 @@ +/* + * mptStringFormat.cpp + * ------------------- + * Purpose: Convert other types to strings. + * Notes : Currently none. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "mptStringFormat.h" + +#if MPT_COMPILER_MSVC +#define MPT_FORMAT_CXX17_INT 1 +#else +#define MPT_FORMAT_CXX17_INT 0 +#endif + +#if MPT_FORMAT_CXX17_INT +#if MPT_MSVC_AT_LEAST(2019,0) && MPT_MSVC_BEFORE(2019,2) +#if !(defined(UNICODE) || defined(_UNICODE)) +// work-around https://developercommunity.visualstudio.com/content/problem/629849/mfc-headers-conflict-with-c17-charconv-header-in-m.html +#pragma push_macro("_M2") +#undef _M2 +#endif +#endif +#include +#if MPT_MSVC_AT_LEAST(2019,0) && MPT_MSVC_BEFORE(2019,2) +#if !(defined(UNICODE) || defined(_UNICODE)) +// work-around https://developercommunity.visualstudio.com/content/problem/629849/mfc-headers-conflict-with-c17-charconv-header-in-m.html +#pragma pop_macro("_M2") +#endif +#endif +#endif // MPT_FORMAT_CXX17_INT +#include +#include +#include +#include +#if MPT_FORMAT_CXX17_INT +#include +#endif // MPT_FORMAT_CXX17_INT + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt +{ + + + +template inline void SaneInsert(Tstream & s, const T & x) { s << x; } +// do the right thing for signed/unsigned char and bool +template inline void SaneInsert(Tstream & s, const bool & x) { s << static_cast(x); } +template inline void SaneInsert(Tstream & s, const signed char & x) { s << static_cast(x); } +template inline void SaneInsert(Tstream & s, const unsigned char & x) { s << static_cast(x); } + +#if MPT_FORMAT_CXX17_INT + +#if MPT_WSTRING_FORMAT +static std::wstring ToWideSimple(const std::string &nstr) +{ + std::wstring wstr(nstr.size(), L'\0'); + for(std::size_t i = 0; i < nstr.size(); ++i) + { + wstr[i] = static_cast(nstr[i]); + } + return wstr; +} +#endif // MPT_WSTRING_FORMAT + +template +static inline std::string ToChars(const T & x, int base = 10) +{ + std::string str(1, '\0'); + bool done = false; + while(!done) + { + std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x, base); + if(result.ec != std::errc{}) + { + str.resize(Util::ExponentialGrow(str.size()), '\0'); + } else + { + str.resize(result.ptr - str.data()); + done = true; + } + } + return str; +} + +template +static inline std::string ToStringHelperInt(const T & x) +{ + return ToChars(x); +} + +#if MPT_WSTRING_FORMAT +template +static inline std::wstring ToWStringHelperInt(const T & x) +{ + return ToWideSimple(ToChars(x)); +} +#endif + +#else // !MPT_FORMAT_CXX17_INT + +template +static inline std::string ToStringHelperInt(const T & x) +{ + std::ostringstream o; + o.imbue(std::locale::classic()); + SaneInsert(o, x); + return o.str(); +} + +#if MPT_WSTRING_FORMAT +template +static inline std::wstring ToWStringHelperInt(const T & x) +{ + std::wostringstream o; + o.imbue(std::locale::classic()); + SaneInsert(o, x); + return o.str(); +} +#endif + +#endif // MPT_FORMAT_CXX17_INT + +template +static inline std::string ToStringHelperFloat(const T & x) +{ + std::ostringstream o; + o.imbue(std::locale::classic()); + SaneInsert(o, x); + return o.str(); +} + +#if MPT_WSTRING_FORMAT +template +static inline std::wstring ToWStringHelperFloat(const T & x) +{ + std::wostringstream o; + o.imbue(std::locale::classic()); + SaneInsert(o, x); + return o.str(); +} +#endif + +std::string ToString(const bool & x) { return ToStringHelperInt(static_cast(x)); } +std::string ToString(const signed char & x) { return ToStringHelperInt(x); } +std::string ToString(const unsigned char & x) { return ToStringHelperInt(x); } +std::string ToString(const signed short & x) { return ToStringHelperInt(x); } +std::string ToString(const unsigned short & x) { return ToStringHelperInt(x); } +std::string ToString(const signed int & x) { return ToStringHelperInt(x); } +std::string ToString(const unsigned int & x) { return ToStringHelperInt(x); } +std::string ToString(const signed long & x) { return ToStringHelperInt(x); } +std::string ToString(const unsigned long & x) { return ToStringHelperInt(x); } +std::string ToString(const signed long long & x) { return ToStringHelperInt(x); } +std::string ToString(const unsigned long long & x) { return ToStringHelperInt(x); } +std::string ToString(const float & x) { return ToStringHelperFloat(x); } +std::string ToString(const double & x) { return ToStringHelperFloat(x); } +std::string ToString(const long double & x) { return ToStringHelperFloat(x); } + +#if MPT_WSTRING_FORMAT +#if MPT_USTRING_MODE_UTF8 +mpt::ustring ToUString(const std::wstring & x) { return mpt::ToUnicode(x); } +#endif +mpt::ustring ToUString(const wchar_t * const & x) { return mpt::ToUnicode(x); } +#endif +#if defined(MPT_WITH_MFC) +mpt::ustring ToUString(const CString & x) { return mpt::ToUnicode(x); } +#endif // MPT_WITH_MFC +#if MPT_USTRING_MODE_WIDE +mpt::ustring ToUString(const bool & x) { return ToWStringHelperInt(static_cast(x)); } +mpt::ustring ToUString(const signed char & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const unsigned char & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const signed short & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const unsigned short & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const signed int & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const unsigned int & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const signed long & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const unsigned long & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const signed long long & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const unsigned long long & x) { return ToWStringHelperInt(x); } +mpt::ustring ToUString(const float & x) { return ToWStringHelperFloat(x); } +mpt::ustring ToUString(const double & x) { return ToWStringHelperFloat(x); } +mpt::ustring ToUString(const long double & x) { return ToWStringHelperFloat(x); } +#endif +#if MPT_USTRING_MODE_UTF8 +mpt::ustring ToUString(const bool & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(static_cast(x))); } +mpt::ustring ToUString(const signed char & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned char & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed short & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned short & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed int & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned int & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed long long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned long long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const float & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperFloat(x)); } +mpt::ustring ToUString(const double & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperFloat(x)); } +mpt::ustring ToUString(const long double & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperFloat(x)); } +#endif + +#if MPT_WSTRING_FORMAT +#if MPT_USTRING_MODE_UTF8 +std::wstring ToWString(const mpt::ustring & x) { return mpt::ToWide(x); } +#endif +#if defined(MPT_WITH_MFC) +std::wstring ToWString(const CString & x) { return mpt::ToWide(x); } +#endif // MPT_WITH_MFC +std::wstring ToWString(const bool & x) { return ToWStringHelperInt(static_cast(x)); } +std::wstring ToWString(const signed char & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const unsigned char & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const signed short & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const unsigned short & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const signed int & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const unsigned int & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const signed long & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const unsigned long & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const signed long long & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const unsigned long long & x) { return ToWStringHelperInt(x); } +std::wstring ToWString(const float & x) { return ToWStringHelperFloat(x); } +std::wstring ToWString(const double & x) { return ToWStringHelperFloat(x); } +std::wstring ToWString(const long double & x) { return ToWStringHelperFloat(x); } +#endif + + +template +struct NumPunct : std::numpunct +{ +private: + unsigned int group; + char sep; +public: + NumPunct(unsigned int g, char s) + : group(g) + , sep(s) + {} + std::string do_grouping() const override + { + return std::string(1, static_cast(group)); + } + Tchar do_thousands_sep() const override + { + return static_cast(sep); + } +}; + +template +static inline void ApplyFormat(Tostream & o, const FormatSpec & format, const T &) +{ + MPT_MAYBE_CONSTANT_IF(!std::numeric_limits::is_integer) + { + if(format.GetGroup() > 0) + { + o.imbue(std::locale(o.getloc(), new NumPunct(format.GetGroup(), format.GetGroupSep()))); + } + } + FormatFlags f = format.GetFlags(); + std::size_t width = format.GetWidth(); + int precision = format.GetPrecision(); + if(precision != -1 && width != 0 && !(f & fmt_base::NotaFix) && !(f & fmt_base::NotaSci)) + { + // fixup: + // precision behaves differently from .# + // avoid default format when precision and width are set + f &= ~fmt_base::NotaNrm; + f |= fmt_base::NotaFix; + } + if(f & fmt_base::BaseDec) { o << std::dec; } + else if(f & fmt_base::BaseHex) { o << std::hex; } + if(f & fmt_base::NotaNrm ) { /*nothing*/ } + else if(f & fmt_base::NotaFix ) { o << std::setiosflags(std::ios::fixed); } + else if(f & fmt_base::NotaSci ) { o << std::setiosflags(std::ios::scientific); } + if(f & fmt_base::CaseLow) { o << std::nouppercase; } + else if(f & fmt_base::CaseUpp) { o << std::uppercase; } + MPT_MAYBE_CONSTANT_IF(!std::numeric_limits::is_integer) + { + if(f & fmt_base::FillOff) { /* nothing */ } + else if(f & fmt_base::FillNul) { o << std::setw(width) << std::setfill(typename Tostream::char_type('0')); } + } + if(precision != -1) { o << std::setprecision(precision); } +} + +template +static inline Tstring PostProcessCase(Tstring str, const FormatSpec & format) +{ + FormatFlags f = format.GetFlags(); + if(f & fmt_base::CaseUpp) + { + for(auto & c : str) + { + if('a' <= c && c <= 'z') + { + c -= 'a' - 'A'; + } + } + } + return str; +} + +template +static inline Tstring PostProcessDigits(Tstring str, const FormatSpec & format) +{ + FormatFlags f = format.GetFlags(); + std::size_t width = format.GetWidth(); + if(f & fmt_base::FillNul) + { + auto pos = str.begin(); + if(str.length() > 0) + { + if(str[0] == typename Tstring::value_type('+')) + { + pos++; + width++; + } else if(str[0] == typename Tstring::value_type('-')) + { + pos++; + width++; + } + } + if(str.length() < width) + { + str.insert(pos, width - str.length(), '0'); + } + } + return str; +} + +template +static inline Tstring PostProcessGroup(Tstring str, const FormatSpec & format) +{ + if(format.GetGroup() > 0) + { + const unsigned int groupSize = format.GetGroup(); + const char groupSep = format.GetGroupSep(); + std::size_t len = str.length(); + for(std::size_t n = 0; n < len; ++n) + { + if(n > 0 && (n % groupSize) == 0) + { + if(!(n == (len - 1) && (str[0] == typename Tstring::value_type('+') || str[0] == typename Tstring::value_type('-')))) + { + str.insert(str.begin() + (len - n), 1, groupSep); + } + } + } + } + return str; +} + +#if MPT_FORMAT_CXX17_INT + +template +static inline std::string FormatValHelperInt(const T & x, const FormatSpec & f) +{ + int base = 10; + if(f.GetFlags() & fmt_base::BaseDec) { base = 10; } + if(f.GetFlags() & fmt_base::BaseHex) { base = 16; } + return PostProcessGroup(PostProcessDigits(PostProcessCase(ToChars(x, base), f), f), f); +} + +#if MPT_WSTRING_FORMAT +template +static inline std::wstring FormatValWHelperInt(const T & x, const FormatSpec & f) +{ + int base = 10; + if(f.GetFlags() & fmt_base::BaseDec) { base = 10; } + if(f.GetFlags() & fmt_base::BaseHex) { base = 16; } + return ToWideSimple(PostProcessGroup(PostProcessDigits(PostProcessCase(ToChars(x, base), f), f), f)); +} +#endif + +#else // !MPT_FORMAT_CXX17_INT + +template +static inline std::string FormatValHelperInt(const T & x, const FormatSpec & f) +{ + MPT_MAYBE_CONSTANT_IF((f.GetFlags() & fmt_base::BaseHex) && std::is_signed::value) + { + if(x == std::numeric_limits::min()) + { + return std::string(1, '-') + FormatValHelperInt(static_cast::type>(x), f); + } else MPT_MAYBE_CONSTANT_IF(x < 0) + { + return std::string(1, '-') + FormatValHelperInt(static_cast::type>(0-x), f); + } else + { + return FormatValHelperInt(static_cast::type>(x), f); + } + } + std::ostringstream o; + o.imbue(std::locale::classic()); + ApplyFormat(o, f, x); + SaneInsert(o, x); + return PostProcessGroup(PostProcessDigits(o.str(), f), f); +} + +#if MPT_WSTRING_FORMAT +template +static inline std::wstring FormatValWHelperInt(const T & x, const FormatSpec & f) +{ + MPT_MAYBE_CONSTANT_IF((f.GetFlags() & fmt_base::BaseHex) && std::is_signed::value) + { + if(x == std::numeric_limits::min()) + { + return std::wstring(1, L'-') + FormatValWHelperInt(static_cast::type>(x), f); + } else MPT_MAYBE_CONSTANT_IF(x < 0) + { + return std::wstring(1, L'-') + FormatValWHelperInt(static_cast::type>(0-x), f); + } else + { + return FormatValWHelperInt(static_cast::type>(x), f); + } + } + std::wostringstream o; + o.imbue(std::locale::classic()); + ApplyFormat(o, f, x); + SaneInsert(o, x); + return PostProcessGroup(PostProcessDigits(o.str(), f), f); +} +#endif + +#endif // MPT_FORMAT_CXX17_INT + +template +static inline std::string FormatValHelperFloat(const T & x, const FormatSpec & f) +{ + std::ostringstream o; + o.imbue(std::locale::classic()); + ApplyFormat(o, f, x); + SaneInsert(o, x); + return o.str(); +} + +#if MPT_WSTRING_FORMAT +template +static inline std::wstring FormatValWHelperFloat(const T & x, const FormatSpec & f) +{ + std::wostringstream o; + o.imbue(std::locale::classic()); + ApplyFormat(o, f, x); + SaneInsert(o, x); + return o.str(); +} +#endif + + +std::string FormatVal(const bool & x, const FormatSpec & f) { return FormatValHelperInt(static_cast(x), f); } +std::string FormatVal(const signed char & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const unsigned char & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const signed short & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const unsigned short & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const signed int & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const unsigned int & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const signed long & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const unsigned long & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const signed long long & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const unsigned long long & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } +std::string FormatVal(const float & x, const FormatSpec & f) { return FormatValHelperFloat(x, f); } +std::string FormatVal(const double & x, const FormatSpec & f) { return FormatValHelperFloat(x, f); } +std::string FormatVal(const long double & x, const FormatSpec & f) { return FormatValHelperFloat(x, f); } + +#if MPT_USTRING_MODE_WIDE +mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return FormatValWHelperInt(static_cast(x), f); } +mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const signed short & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const signed int & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const signed long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +mpt::ustring FormatValU(const float & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); } +mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); } +mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); } +#endif +#if MPT_USTRING_MODE_UTF8 +mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(static_cast(x), f)); } +mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed short & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed int & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const float & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperFloat(x, f)); } +mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperFloat(x, f)); } +mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperFloat(x, f)); } +#endif + +#if MPT_WSTRING_FORMAT +std::wstring FormatValW(const bool & x, const FormatSpec & f) { return FormatValWHelperInt(static_cast(x), f); } +std::wstring FormatValW(const signed char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const unsigned char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const signed short & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const unsigned short & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const signed int & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const unsigned int & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const signed long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const unsigned long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const signed long long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const unsigned long long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } +std::wstring FormatValW(const float & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); } +std::wstring FormatValW(const double & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); } +std::wstring FormatValW(const long double & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); } +#endif + + +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptStringFormat.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptStringFormat.h new file mode 100644 index 000000000..d8cf10006 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptStringFormat.h @@ -0,0 +1,737 @@ +/* + * mptStringFormat.h + * ----------------- + * Purpose: Convert other types to strings. + * Notes : Currently none. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + +#include "mptString.h" + + +OPENMPT_NAMESPACE_BEGIN + + + +// The following section demands a rationale. +// 1. mpt::fmt::val(), mpt::wfmt::val() and mpt::ufmt::val() mimic the semantics of c++11 std::to_string() and std::to_wstring(). +// There is an important difference though. The c++11 versions are specified in terms of sprintf formatting which in turn +// depends on the current C locale. This renders these functions unusable in a library context because the current +// C locale is set by the library-using application and could be anything. There is no way a library can get reliable semantics +// out of these functions. It is thus better to just avoid them. +// ToString() and ToWString() are based on iostream internally, but the the locale of the stream is forced to std::locale::classic(), +// which results in "C" ASCII locale behavior. +// 2. The full suite of printf-like or iostream like number formatting is generally not required. Instead, a sane subset functionality +// is provided here. +// For convenience, mpt::fmt::f(const char *, float) allows formatting a single floating point value with a +// standard printf-like format string. This itself relies on iostream with classic() locale internally and is thus current locale +// agnostic. +// When formatting integers, it is recommended to use mpt::fmt::dec or mpt::fmt::hex. Appending a template argument '' sets the width, +// the same way as '%nd' would do. Appending a '0' to the function name causes zero-filling as print-like '%0nd' would do. Spelling 'HEX' +// in upper-case generates upper-case hex digits. If these are not known at compile-time, a more verbose FormatVal(int, format) can be +// used. +// 3. mpt::format(format)(...) provides simplified and type-safe message and localization string formatting. +// The only specifier allowed is '%' followed by a single digit n. It references to n-th parameter after the format string (1-based). +// This mimics the behaviour of QString::arg() in QT4/5 or MFC AfxFormatString2(). C printf-like functions offer similar functionality +// with a '%n$TYPE' syntax. In .NET, the syntax is '{n}'. This is useful to support localization strings that can change the parameter +// ordering. +// 4. Every function is available for std::string, std::wstring and mpt::ustring. std::string makes no assumption about the encoding, which +// basically means, it should work for any 7-bit or 8-bit encoding, including for example ASCII, UTF8 or the current locale encoding. +// std::string std::wstring mpt::ustring mpt::tsrtring CString +// mpt::fmt mpt::wfmt mpt::ufmt mpt::tfmt mpt::cfmt +// mpt::format("%1") mpt::wformat(L"%1") mpt::uformat(MPT_ULITERAL(%1) mpt::tformat(_T("%1")) mpt::cformat(_T("%1")) +// mpt::format("%1") mpt::format(L"%1") mpt::format(MPT_USTRING(%1)) mpt::format(mpt::tstring(_T("%1")) mpt::format(CString(_T("%1")) +// 5. All functionality here delegates real work outside of the header file so that and do not need to be included when +// using this functionality. +// Advantages: +// - Avoids binary code bloat when too much of iostream operator << gets inlined at every usage site. +// - Faster compile times because and (2 very complex headers) are not included everywhere. +// Disadvantages: +// - Slightly more c++ code is required for delegating work. +// - As the header does not use iostreams, custom types need to overload mpt::UString instead of iostream operator << to allow for custom type +// formatting. +// - std::string, std::wstring and mpt::ustring are returned from somewhat deep cascades of helper functions. Where possible, code is +// written in such a way that return-value-optimization (RVO) or named-return-value-optimization (NRVO) should be able to eliminate +// almost all these copies. This should not be a problem for any decent modern compiler (and even less so for a c++11 compiler where +// move-semantics will kick in if RVO/NRVO fails). + +namespace mpt +{ + +// ToUString() converts various built-in types to a well-defined, locale-independent string representation. +// This is also used as a type-tunnel pattern for mpt::format. +// Custom types that need to be converted to strings are encouraged to overload ToUString(). + +// fallback to member function ToUString() +#if MPT_USTRING_MODE_UTF8 +template auto ToString(const T & x) -> decltype(mpt::ToCharset(mpt::Charset::UTF8, x.ToUString())) { return mpt::ToCharset(mpt::Charset::UTF8, x.ToUString()); } +#else +template auto ToString(const T & x) -> decltype(mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x.ToUString())) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x.ToUString()); } +#endif + +inline std::string ToString(const std::string & x) { return x; } +inline std::string ToString(const char * const & x) { return x; } +std::string ToString(const char &x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead +#if MPT_WSTRING_FORMAT +std::string ToString(const std::wstring & x) = delete; // Unknown encoding. +std::string ToString(const wchar_t * const & x) = delete; // Unknown encoding. +std::string ToString(const wchar_t &x ) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +#endif +#if MPT_USTRING_MODE_UTF8 +std::string ToString(const mpt::ustring & x) = delete; // Unknown encoding. +#endif +#if defined(MPT_WITH_MFC) +std::string ToString(const CString & x) = delete; // unknown encoding +#endif // MPT_WITH_MFC +std::string ToString(const bool & x); +std::string ToString(const signed char & x); +std::string ToString(const unsigned char & x); +std::string ToString(const signed short & x); +std::string ToString(const unsigned short & x); +std::string ToString(const signed int & x); +std::string ToString(const unsigned int & x); +std::string ToString(const signed long & x); +std::string ToString(const unsigned long & x); +std::string ToString(const signed long long & x); +std::string ToString(const unsigned long long & x); +std::string ToString(const float & x); +std::string ToString(const double & x); +std::string ToString(const long double & x); + +// fallback to member function ToUString() +template auto ToUString(const T & x) -> decltype(x.ToUString()) { return x.ToUString(); } + +inline mpt::ustring ToUString(const mpt::ustring & x) { return x; } +mpt::ustring ToUString(const std::string & x) = delete; // Unknown encoding. +mpt::ustring ToUString(const char * const & x) = delete; // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case. +mpt::ustring ToUString(const char & x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead +#if MPT_WSTRING_FORMAT +#if MPT_USTRING_MODE_UTF8 +mpt::ustring ToUString(const std::wstring & x); +#endif +mpt::ustring ToUString(const wchar_t * const & x); +mpt::ustring ToUString(const wchar_t & x) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +#endif +#if defined(MPT_WITH_MFC) +mpt::ustring ToUString(const CString & x); +#endif // MPT_WITH_MFC +mpt::ustring ToUString(const bool & x); +mpt::ustring ToUString(const signed char & x); +mpt::ustring ToUString(const unsigned char & x); +mpt::ustring ToUString(const signed short & x); +mpt::ustring ToUString(const unsigned short & x); +mpt::ustring ToUString(const signed int & x); +mpt::ustring ToUString(const unsigned int & x); +mpt::ustring ToUString(const signed long & x); +mpt::ustring ToUString(const unsigned long & x); +mpt::ustring ToUString(const signed long long & x); +mpt::ustring ToUString(const unsigned long long & x); +mpt::ustring ToUString(const float & x); +mpt::ustring ToUString(const double & x); +mpt::ustring ToUString(const long double & x); + +#if MPT_WSTRING_FORMAT +std::wstring ToWString(const std::string & x) = delete; // Unknown encoding. +std::wstring ToWString(const char * const & x) = delete; // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case. +std::wstring ToWString(const char & x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead +inline std::wstring ToWString(const std::wstring & x) { return x; } +inline std::wstring ToWString(const wchar_t * const & x) { return x; } +std::wstring ToWString(const wchar_t & x) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +#if MPT_USTRING_MODE_UTF8 +std::wstring ToWString(const mpt::ustring & x); +#endif +#if defined(MPT_WITH_MFC) +std::wstring ToWString(const CString & x); +#endif // MPT_WITH_MFC +std::wstring ToWString(const bool & x); +std::wstring ToWString(const signed char & x); +std::wstring ToWString(const unsigned char & x); +std::wstring ToWString(const signed short & x); +std::wstring ToWString(const unsigned short & x); +std::wstring ToWString(const signed int & x); +std::wstring ToWString(const unsigned int & x); +std::wstring ToWString(const signed long & x); +std::wstring ToWString(const unsigned long & x); +std::wstring ToWString(const signed long long & x); +std::wstring ToWString(const unsigned long long & x); +std::wstring ToWString(const float & x); +std::wstring ToWString(const double & x); +std::wstring ToWString(const long double & x); +// fallback to member function ToUString() +template auto ToWString(const T & x) -> decltype(mpt::ToWide(x.ToUString())) { return mpt::ToWide(x.ToUString()); } +#endif + +#if defined(MPT_ENABLE_CHARSET_LOCALE) +template struct ToLocaleHelper { mpt::lstring operator () (const T & v) { return mpt::ToLocale(ToUString(v)); } }; +template <> struct ToLocaleHelper { mpt::lstring operator () (const mpt::lstring & v) { return v; } }; +#endif // MPT_ENABLE_CHARSET_LOCALE + +#if defined(MPT_WITH_MFC) +template struct ToCStringHelper { CString operator () (const T & v) { return mpt::ToCString(ToUString(v)); } }; +template <> struct ToCStringHelper { CString operator () (const CString & v) { return v; } }; +#endif // MPT_WITH_MFC + +template struct ToStringTFunctor {}; +template <> struct ToStringTFunctor { template inline std::string operator() (const T & x) { return ToString(x); } }; +template <> struct ToStringTFunctor { template inline mpt::ustring operator() (const T & x) { return ToUString(x); } }; +#if MPT_WSTRING_FORMAT && MPT_USTRING_MODE_UTF8 +template <> struct ToStringTFunctor { template inline std::wstring operator() (const T & x) { return ToWString(x); } }; +#endif +#if defined(MPT_ENABLE_CHARSET_LOCALE) +template <> struct ToStringTFunctor { template inline mpt::lstring operator() (const T & x) { return mpt::ToLocaleHelper()(x); } }; +#endif // MPT_ENABLE_CHARSET_LOCALE +#if defined(MPT_WITH_MFC) +template <> struct ToStringTFunctor { template inline CString operator() (const T & x) { return mpt::ToCStringHelper()(x); } }; +#endif // MPT_WITH_MFC + +template inline Tstring ToStringT(const T & x) { return ToStringTFunctor()(x); } + + + +struct fmt_base +{ + +enum FormatFlagsEnum +{ + BaseDec = 0x0001, // base 10 (integers only) + BaseHex = 0x0002, // base 16 (integers only) + CaseLow = 0x0010, // lower case hex digits + CaseUpp = 0x0020, // upper case hex digits + FillOff = 0x0100, // do not fill up width + FillNul = 0x0400, // fill up width with zeros + NotaNrm = 0x1000, // float: normal/default notation + NotaFix = 0x2000, // float: fixed point notation + NotaSci = 0x4000, // float: scientific notation +}; + +}; // struct fmt_base + +typedef unsigned int FormatFlags; + +static_assert(sizeof(FormatFlags) >= sizeof(fmt_base::FormatFlagsEnum)); + +class FormatSpec; + +std::string FormatVal(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) +std::string FormatVal(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR +std::string FormatVal(const bool & x, const FormatSpec & f); +std::string FormatVal(const signed char & x, const FormatSpec & f); +std::string FormatVal(const unsigned char & x, const FormatSpec & f); +std::string FormatVal(const signed short & x, const FormatSpec & f); +std::string FormatVal(const unsigned short & x, const FormatSpec & f); +std::string FormatVal(const signed int & x, const FormatSpec & f); +std::string FormatVal(const unsigned int & x, const FormatSpec & f); +std::string FormatVal(const signed long & x, const FormatSpec & f); +std::string FormatVal(const unsigned long & x, const FormatSpec & f); +std::string FormatVal(const signed long long & x, const FormatSpec & f); +std::string FormatVal(const unsigned long long & x, const FormatSpec & f); +std::string FormatVal(const float & x, const FormatSpec & f); +std::string FormatVal(const double & x, const FormatSpec & f); +std::string FormatVal(const long double & x, const FormatSpec & f); + +mpt::ustring FormatValU(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) +mpt::ustring FormatValU(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR +mpt::ustring FormatValU(const bool & x, const FormatSpec & f); +mpt::ustring FormatValU(const signed char & x, const FormatSpec & f); +mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f); +mpt::ustring FormatValU(const signed short & x, const FormatSpec & f); +mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f); +mpt::ustring FormatValU(const signed int & x, const FormatSpec & f); +mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f); +mpt::ustring FormatValU(const signed long & x, const FormatSpec & f); +mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f); +mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f); +mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f); +mpt::ustring FormatValU(const float & x, const FormatSpec & f); +mpt::ustring FormatValU(const double & x, const FormatSpec & f); +mpt::ustring FormatValU(const long double & x, const FormatSpec & f); + +#if MPT_WSTRING_FORMAT +std::wstring FormatValW(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) +std::wstring FormatValW(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR +std::wstring FormatValW(const bool & x, const FormatSpec & f); +std::wstring FormatValW(const signed char & x, const FormatSpec & f); +std::wstring FormatValW(const unsigned char & x, const FormatSpec & f); +std::wstring FormatValW(const signed short & x, const FormatSpec & f); +std::wstring FormatValW(const unsigned short & x, const FormatSpec & f); +std::wstring FormatValW(const signed int & x, const FormatSpec & f); +std::wstring FormatValW(const unsigned int & x, const FormatSpec & f); +std::wstring FormatValW(const signed long & x, const FormatSpec & f); +std::wstring FormatValW(const unsigned long & x, const FormatSpec & f); +std::wstring FormatValW(const signed long long & x, const FormatSpec & f); +std::wstring FormatValW(const unsigned long long & x, const FormatSpec & f); +std::wstring FormatValW(const float & x, const FormatSpec & f); +std::wstring FormatValW(const double & x, const FormatSpec & f); +std::wstring FormatValW(const long double & x, const FormatSpec & f); +#endif + +template struct FormatValTFunctor {}; +template <> struct FormatValTFunctor { template inline std::string operator() (const T & x, const FormatSpec & f) { return FormatVal(x, f); } }; +template <> struct FormatValTFunctor { template inline mpt::ustring operator() (const T & x, const FormatSpec & f) { return FormatValU(x, f); } }; +#if MPT_USTRING_MODE_UTF8 && MPT_WSTRING_FORMAT +template <> struct FormatValTFunctor { template inline std::wstring operator() (const T & x, const FormatSpec & f) { return FormatValW(x, f); } }; +#endif +#if defined(MPT_ENABLE_CHARSET_LOCALE) +template <> struct FormatValTFunctor { template inline mpt::lstring operator() (const T & x, const FormatSpec & f) { return mpt::ToLocale(mpt::Charset::Locale, FormatVal(x, f)); } }; +#endif // MPT_ENABLE_CHARSET_LOCALE +#if defined(MPT_WITH_MFC ) +#ifdef UNICODE +template <> struct FormatValTFunctor { template inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(FormatValW(x, f)); } }; +#else // !UNICODE +template <> struct FormatValTFunctor { template inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(mpt::Charset::Locale, FormatVal(x, f)); } }; +#endif // UNICODE +#endif // MPT_WITH_MFC + + +class FormatSpec +{ +private: + FormatFlags flags; + std::size_t width; + int precision; + unsigned int group; + char group_sep; +public: + MPT_CONSTEXPR11_FUN FormatSpec() noexcept : flags(0), width(0), precision(-1), group(0), group_sep(',') {} + MPT_CONSTEXPR11_FUN FormatFlags GetFlags() const noexcept { return flags; } + MPT_CONSTEXPR11_FUN std::size_t GetWidth() const noexcept { return width; } + MPT_CONSTEXPR11_FUN int GetPrecision() const noexcept { return precision; } + MPT_CONSTEXPR11_FUN unsigned int GetGroup() const noexcept { return group; } + MPT_CONSTEXPR11_FUN char GetGroupSep() const noexcept { return group_sep; } + MPT_CONSTEXPR14_FUN FormatSpec & SetFlags(FormatFlags f) noexcept { flags = f; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & SetWidth(std::size_t w) noexcept { width = w; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & SetPrecision(int p) noexcept { precision = p; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & SetGroup(unsigned int g) noexcept { group = g; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & SetGroupSep(char s) noexcept { group_sep = s; return *this; } +public: + MPT_CONSTEXPR14_FUN FormatSpec & BaseDec() noexcept { flags &= ~(fmt_base::BaseDec|fmt_base::BaseHex); flags |= fmt_base::BaseDec; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & BaseHex() noexcept { flags &= ~(fmt_base::BaseDec|fmt_base::BaseHex); flags |= fmt_base::BaseHex; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & CaseLow() noexcept { flags &= ~(fmt_base::CaseLow|fmt_base::CaseUpp); flags |= fmt_base::CaseLow; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & CaseUpp() noexcept { flags &= ~(fmt_base::CaseLow|fmt_base::CaseUpp); flags |= fmt_base::CaseUpp; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & FillOff() noexcept { flags &= ~(fmt_base::FillOff|fmt_base::FillNul); flags |= fmt_base::FillOff; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & FillNul() noexcept { flags &= ~(fmt_base::FillOff|fmt_base::FillNul); flags |= fmt_base::FillNul; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & NotaNrm() noexcept { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaNrm; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & NotaFix() noexcept { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaFix; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & NotaSci() noexcept { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaSci; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & Width(std::size_t w) noexcept { width = w; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & Prec(int p) noexcept { precision = p; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & Group(unsigned int g) noexcept { group = g; return *this; } + MPT_CONSTEXPR14_FUN FormatSpec & GroupSep(char s) noexcept { group_sep = s; return *this; } +public: + MPT_CONSTEXPR14_FUN FormatSpec & Dec() noexcept { return BaseDec(); } + MPT_CONSTEXPR14_FUN FormatSpec & Hex() noexcept { return BaseHex(); } + MPT_CONSTEXPR14_FUN FormatSpec & Low() noexcept { return CaseLow(); } + MPT_CONSTEXPR14_FUN FormatSpec & Upp() noexcept { return CaseUpp(); } + MPT_CONSTEXPR14_FUN FormatSpec & Off() noexcept { return FillOff(); } + MPT_CONSTEXPR14_FUN FormatSpec & Nul() noexcept { return FillNul(); } + MPT_CONSTEXPR14_FUN FormatSpec & Nrm() noexcept { return NotaNrm(); } + MPT_CONSTEXPR14_FUN FormatSpec & Fix() noexcept { return NotaFix(); } + MPT_CONSTEXPR14_FUN FormatSpec & Sci() noexcept { return NotaSci(); } +public: + MPT_CONSTEXPR14_FUN FormatSpec & Decimal() noexcept { return BaseDec(); } + MPT_CONSTEXPR14_FUN FormatSpec & Hexadecimal() noexcept { return BaseHex(); } + MPT_CONSTEXPR14_FUN FormatSpec & Lower() noexcept { return CaseLow(); } + MPT_CONSTEXPR14_FUN FormatSpec & Upper() noexcept { return CaseUpp(); } + MPT_CONSTEXPR14_FUN FormatSpec & FillNone() noexcept { return FillOff(); } + MPT_CONSTEXPR14_FUN FormatSpec & FillZero() noexcept { return FillNul(); } + MPT_CONSTEXPR14_FUN FormatSpec & FloatNormal() noexcept { return NotaNrm(); } + MPT_CONSTEXPR14_FUN FormatSpec & FloatFixed() noexcept { return NotaFix(); } + MPT_CONSTEXPR14_FUN FormatSpec & FloatScientific() noexcept { return NotaSci(); } + MPT_CONSTEXPR14_FUN FormatSpec & Precision(int p) noexcept { return Prec(p); } +}; + +template +struct pointer_cast_helper +{ + Tdst operator()(const Tsrc & src) const { return src; } +}; +template +struct pointer_cast_helper +{ + Tdst operator()(const Tptr * const & src) const { return reinterpret_cast(src); } +}; +template +struct pointer_cast_helper +{ + Tdst operator()(const Tptr * const & src) const { return reinterpret_cast(src); } +}; + + +template +Tdst pointer_cast(const Tsrc & src) +{ + return pointer_cast_helper()(src); +} + +template +struct fmtT : fmt_base +{ + +template +static inline Tstring val(const T& x) +{ + return ToStringTFunctor()(x); +} + +template +static inline Tstring fmt(const T& x, const FormatSpec& f) +{ + return FormatValTFunctor()(x, f); +} + +template +static inline Tstring dec(const T& x) +{ + static_assert(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseDec().FillOff()); +} +template +static inline Tstring dec0(const T& x) +{ + static_assert(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseDec().FillNul().Width(width)); +} + +template +static inline Tstring dec(unsigned int g, char s, const T& x) +{ + static_assert(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseDec().FillOff().Group(g).GroupSep(s)); +} +template +static inline Tstring dec0(unsigned int g, char s, const T& x) +{ + static_assert(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseDec().FillNul().Width(width).Group(g).GroupSep(s)); +} + +template +static inline Tstring hex(const T& x) +{ + static_assert(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillOff()); +} +template +static inline Tstring HEX(const T& x) +{ + static_assert(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillOff()); +} +template +static inline Tstring hex0(const T& x) +{ + static_assert(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillNul().Width(width)); +} +template +static inline Tstring HEX0(const T& x) +{ + static_assert(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width)); +} + +template +static inline Tstring hex(unsigned int g, char s, const T& x) +{ + static_assert(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillOff().Group(g).GroupSep(s)); +} +template +static inline Tstring HEX(unsigned int g, char s, const T& x) +{ + static_assert(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillOff().Group(g).GroupSep(s)); +} +template +static inline Tstring hex0(unsigned int g, char s, const T& x) +{ + static_assert(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillNul().Width(width).Group(g).GroupSep(s)); +} +template +static inline Tstring HEX0(unsigned int g, char s, const T& x) +{ + static_assert(std::numeric_limits::is_integer); + return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width).Group(g).GroupSep(s)); +} + +template +static inline Tstring flt(const T& x, int precision = -1) +{ + static_assert(std::is_floating_point::value); + return FormatValTFunctor()(x, FormatSpec().NotaNrm().FillOff().Precision(precision)); +} +template +static inline Tstring fix(const T& x, int precision = -1) +{ + static_assert(std::is_floating_point::value); + return FormatValTFunctor()(x, FormatSpec().NotaFix().FillOff().Precision(precision)); +} +template +static inline Tstring sci(const T& x, int precision = -1) +{ + static_assert(std::is_floating_point::value); + return FormatValTFunctor()(x, FormatSpec().NotaSci().FillOff().Precision(precision)); +} + +template +static inline Tstring ptr(const T& x) +{ + static_assert(std::is_pointer::value || std::is_same::value || std::is_same::value, ""); + return hex0(pointer_cast(x)); +} +template +static inline Tstring PTR(const T& x) +{ + static_assert(std::is_pointer::value || std::is_same::value || std::is_same::value, ""); + return HEX0(pointer_cast(x)); +} + +static inline Tstring pad_left(std::size_t width_, const Tstring &str) +{ + typedef mpt::string_traits traits; + typename traits::size_type width = static_cast(width_); + return traits::pad(str, width, 0); +} +static inline Tstring pad_right(std::size_t width_, const Tstring &str) +{ + typedef mpt::string_traits traits; + typename traits::size_type width = static_cast(width_); + return traits::pad(str, 0, width); +} +static inline Tstring left(std::size_t width_, const Tstring &str) +{ + typedef mpt::string_traits traits; + typename traits::size_type width = static_cast(width_); + return (traits::length(str) < width) ? traits::pad(str, 0, width - traits::length(str)) : str; +} +static inline Tstring right(std::size_t width_, const Tstring &str) +{ + typedef mpt::string_traits traits; + typename traits::size_type width = static_cast(width_); + return (traits::length(str) < width) ? traits::pad(str, width - traits::length(str), 0) : str; +} +static inline Tstring center(std::size_t width_, const Tstring &str) +{ + typedef mpt::string_traits traits; + typename traits::size_type width = static_cast(width_); + return (traits::length(str) < width) ? traits::pad(str, (width - traits::length(str)) / 2, (width - traits::length(str) + 1) / 2) : str; +} + +}; // struct fmtT + + +typedef fmtT fmt; +#if MPT_WSTRING_FORMAT +typedef fmtT wfmt; +#endif +#if MPT_USTRING_MODE_WIDE +typedef fmtT ufmt; +#else +typedef fmtT ufmt; +#endif +#if defined(MPT_ENABLE_CHARSET_LOCALE) +typedef fmtT lfmt; +#endif // MPT_ENABLE_CHARSET_LOCALE +#if MPT_OS_WINDOWS +typedef fmtT tfmt; +#endif +#if defined(MPT_WITH_MFC) +typedef fmtT cfmt; +#endif // MPT_WITH_MFC + +} // namespace mpt + +namespace mpt { + +namespace String { + +namespace detail +{ + +template struct to_string_type { }; +template <> struct to_string_type { typedef std::string type; }; +template <> struct to_string_type { typedef std::string type; }; +template <> struct to_string_type { typedef std::string type; }; +template <> struct to_string_type { typedef std::string type; }; +template <> struct to_string_type { typedef std::string type; }; +#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) +template <> struct to_string_type { typedef std::wstring type; }; +template <> struct to_string_type { typedef std::wstring type; }; +template <> struct to_string_type { typedef std::wstring type; }; +template <> struct to_string_type { typedef std::wstring type; }; +template <> struct to_string_type { typedef std::wstring type; }; +#endif // !MPT_COMPILER_QUIRK_NO_WCHAR +#if MPT_USTRING_MODE_UTF8 +template <> struct to_string_type { typedef mpt::ustring type; }; +#endif +#if defined(MPT_ENABLE_CHARSET_LOCALE) +template <> struct to_string_type { typedef mpt::lstring type; }; +#endif // MPT_ENABLE_CHARSET_LOCALE +#if defined(MPT_WITH_MFC) +template <> struct to_string_type { typedef CString type; }; +#endif // MPT_WITH_MFC +template struct to_string_type { typedef typename to_string_type::type type; }; + +} // namespace detail + +} // namespace String + +template +class message_formatter +{ + +public: + + typedef typename mpt::String::detail::to_string_type::type Tstring; + +private: + + Tstring format; + +private: + + MPT_NOINLINE Tstring do_format(mpt::span vals) const + { + typedef typename mpt::string_traits traits; + Tstring result; + const typename traits::size_type len = traits::length(format); + traits::reserve(result, len); + for(typename traits::size_type pos = 0; pos != len; ++pos) + { + typename traits::char_type c = format[pos]; + if(pos + 1 != len && c == typename traits::char_type('%')) + { + pos++; + c = format[pos]; + if(typename traits::char_type('1') <= c && c <= typename traits::char_type('9')) + { + const std::size_t n = c - typename traits::char_type('0') - 1; + if(n < std::size(vals)) + { + traits::append(result, vals[n]); + } + continue; + } else if(c != typename traits::char_type('%')) + { + traits::append(result, 1, typename traits::char_type('%')); + } + } + traits::append(result, 1, c); + } + return result; + } + +public: + + message_formatter(Tstring format_) + : format(std::move(format_)) + { + } + +public: + + template + Tstring operator() (const Ts&... xs) const + { + const std::array vals{{ToStringTFunctor()(xs)...}}; + return do_format(mpt::as_span(vals)); + } + +}; // struct message_formatter + +template +message_formatter::type> format(Tformat format) +{ + typedef typename mpt::String::detail::to_string_type::type Tstring; + return message_formatter(Tstring(std::move(format))); +} + +#if MPT_WSTRING_FORMAT +static inline message_formatter wformat(std::wstring format) +{ + return message_formatter(std::move(format)); +} +#endif + +static inline message_formatter uformat(mpt::ustring format) +{ + return message_formatter(std::move(format)); +} + +#if defined(MPT_ENABLE_CHARSET_LOCALE) +static inline message_formatter lformat(mpt::lstring format) +{ + return message_formatter(std::move(format)); +} +#endif // MPT_ENABLE_CHARSET_LOCALE + +#if MPT_OS_WINDOWS +static inline message_formatter tformat(mpt::tstring format) +{ + return message_formatter(std::move(format)); +} +#endif + +#if defined(MPT_WITH_MFC) +static inline message_formatter cformat(CString format) +{ + return message_formatter(std::move(format)); +} +#endif // MPT_WITH_MFC + +} // namespace mpt + + + +namespace mpt { namespace String { + +// Combine a vector of values into a string, separated with the given separator. +// No escaping is performed. +template +mpt::ustring Combine(const std::vector &vals, const mpt::ustring &sep=U_(",")) +{ + mpt::ustring str; + for(std::size_t i = 0; i < vals.size(); ++i) + { + if(i > 0) + { + str += sep; + } + str += mpt::ufmt::val(vals[i]); + } + return str; +} +template +std::string Combine(const std::vector &vals, const std::string &sep=std::string(",")) +{ + std::string str; + for(std::size_t i = 0; i < vals.size(); ++i) + { + if(i > 0) + { + str += sep; + } + str += mpt::fmt::val(vals[i]); + } + return str; +} + +} } // namespace mpt::String + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptStringParse.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptStringParse.cpp new file mode 100644 index 000000000..89c948b9d --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptStringParse.cpp @@ -0,0 +1,120 @@ +/* + * mptStringParse.cpp + * ------------------ + * Purpose: Convert strings to other types. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "mptStringParse.h" + +#include +#include + + +OPENMPT_NAMESPACE_BEGIN + + +template +inline T ConvertStrToHelper(const std::string &str) +{ + std::istringstream i(str); + i.imbue(std::locale::classic()); + T x; + if(!(i >> x)) + { + return T(); + } + return x; +} +template<> inline bool ConvertStrToHelper(const std::string &str) { return ConvertStrToHelper(str)?true:false; } +template<> inline signed char ConvertStrToHelper(const std::string &str) { return static_cast(ConvertStrToHelper(str)); } +template<> inline unsigned char ConvertStrToHelper(const std::string &str) { return static_cast(ConvertStrToHelper(str)); } + +#if MPT_WSTRING_FORMAT +template +inline T ConvertStrToHelper(const std::wstring &str) +{ + std::wistringstream i(str); + i.imbue(std::locale::classic()); + T x; + if(!(i >> x)) + { + return T(); + } + return x; +} +template<> inline bool ConvertStrToHelper(const std::wstring &str) { return ConvertStrToHelper(str)?true:false; } +template<> inline signed char ConvertStrToHelper(const std::wstring &str) { return static_cast(ConvertStrToHelper(str)); } +template<> inline unsigned char ConvertStrToHelper(const std::wstring &str) { return static_cast(ConvertStrToHelper(str)); } +#endif + +bool ConvertStrToBool(const std::string &str) { return ConvertStrToHelper(str); } +signed char ConvertStrToSignedChar(const std::string &str) { return ConvertStrToHelper(str); } +unsigned char ConvertStrToUnsignedChar(const std::string &str) { return ConvertStrToHelper(str); } +signed short ConvertStrToSignedShort(const std::string &str) { return ConvertStrToHelper(str); } +unsigned short ConvertStrToUnsignedShort(const std::string &str) { return ConvertStrToHelper(str); } +signed int ConvertStrToSignedInt(const std::string &str) { return ConvertStrToHelper(str); } +unsigned int ConvertStrToUnsignedInt(const std::string &str) { return ConvertStrToHelper(str); } +signed long ConvertStrToSignedLong(const std::string &str) { return ConvertStrToHelper(str); } +unsigned long ConvertStrToUnsignedLong(const std::string &str) { return ConvertStrToHelper(str); } +signed long long ConvertStrToSignedLongLong(const std::string &str) { return ConvertStrToHelper(str); } +unsigned long long ConvertStrToUnsignedLongLong(const std::string &str) { return ConvertStrToHelper(str); } +float ConvertStrToFloat(const std::string &str) { return ConvertStrToHelper(str); } +double ConvertStrToDouble(const std::string &str) { return ConvertStrToHelper(str); } +long double ConvertStrToLongDouble(const std::string &str) { return ConvertStrToHelper(str); } + +#if MPT_WSTRING_FORMAT +bool ConvertStrToBool(const std::wstring &str) { return ConvertStrToHelper(str); } +signed char ConvertStrToSignedChar(const std::wstring &str) { return ConvertStrToHelper(str); } +unsigned char ConvertStrToUnsignedChar(const std::wstring &str) { return ConvertStrToHelper(str); } +signed short ConvertStrToSignedShort(const std::wstring &str) { return ConvertStrToHelper(str); } +unsigned short ConvertStrToUnsignedShort(const std::wstring &str) { return ConvertStrToHelper(str); } +signed int ConvertStrToSignedInt(const std::wstring &str) { return ConvertStrToHelper(str); } +unsigned int ConvertStrToUnsignedInt(const std::wstring &str) { return ConvertStrToHelper(str); } +signed long ConvertStrToSignedLong(const std::wstring &str) { return ConvertStrToHelper(str); } +unsigned long ConvertStrToUnsignedLong(const std::wstring &str) { return ConvertStrToHelper(str); } +signed long long ConvertStrToSignedLongLong(const std::wstring &str) { return ConvertStrToHelper(str); } +unsigned long long ConvertStrToUnsignedLongLong(const std::wstring &str) { return ConvertStrToHelper(str); } +float ConvertStrToFloat(const std::wstring &str) { return ConvertStrToHelper(str); } +double ConvertStrToDouble(const std::wstring &str) { return ConvertStrToHelper(str); } +long double ConvertStrToLongDouble(const std::wstring &str) { return ConvertStrToHelper(str); } +#endif + + +namespace mpt +{ +namespace String +{ +namespace Parse +{ + +template +T HexToHelper(const std::string &str) +{ + std::istringstream i(str); + i.imbue(std::locale::classic()); + T x; + if(!(i >> std::hex >> x)) + { + return T(); + } + return x; +} +template<> unsigned char HexToHelper(const std::string &str) { return static_cast(HexToHelper(str)); } + +unsigned char HexToUnsignedChar(const std::string &str) { return HexToHelper(str); } +unsigned short HexToUnsignedShort(const std::string &str) { return HexToHelper(str); } +unsigned int HexToUnsignedInt(const std::string &str) { return HexToHelper(str); } +unsigned long HexToUnsignedLong(const std::string &str) { return HexToHelper(str); } +unsigned long long HexToUnsignedLongLong(const std::string &str) { return HexToHelper(str); } + +} // namespace Parse +} // namespace String +} // namespace mpt + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptStringParse.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptStringParse.h new file mode 100644 index 000000000..488df8939 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptStringParse.h @@ -0,0 +1,250 @@ +/* + * mptStringParse.h + * ---------------- + * Purpose: Convert strings to other types. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + +OPENMPT_NAMESPACE_BEGIN + + +bool ConvertStrToBool(const std::string &str); +signed char ConvertStrToSignedChar(const std::string &str); +unsigned char ConvertStrToUnsignedChar(const std::string &str); +signed short ConvertStrToSignedShort(const std::string &str); +unsigned short ConvertStrToUnsignedShort(const std::string &str); +signed int ConvertStrToSignedInt(const std::string &str); +unsigned int ConvertStrToUnsignedInt(const std::string &str); +signed long ConvertStrToSignedLong(const std::string &str); +unsigned long ConvertStrToUnsignedLong(const std::string &str); +signed long long ConvertStrToSignedLongLong(const std::string &str); +unsigned long long ConvertStrToUnsignedLongLong(const std::string &str); +float ConvertStrToFloat(const std::string &str); +double ConvertStrToDouble(const std::string &str); +long double ConvertStrToLongDouble(const std::string &str); +template inline T ConvertStrTo(const std::string &str); // not defined, generates compiler error for non-specialized types +template<> inline std::string ConvertStrTo(const std::string &str) { return str; } +template<> inline bool ConvertStrTo(const std::string &str) { return ConvertStrToBool(str); } +template<> inline signed char ConvertStrTo(const std::string &str) { return ConvertStrToSignedChar(str); } +template<> inline unsigned char ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedChar(str); } +template<> inline signed short ConvertStrTo(const std::string &str) { return ConvertStrToSignedShort(str); } +template<> inline unsigned short ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedShort(str); } +template<> inline signed int ConvertStrTo(const std::string &str) { return ConvertStrToSignedInt(str); } +template<> inline unsigned int ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedInt(str); } +template<> inline signed long ConvertStrTo(const std::string &str) { return ConvertStrToSignedLong(str); } +template<> inline unsigned long ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedLong(str); } +template<> inline signed long long ConvertStrTo(const std::string &str) { return ConvertStrToSignedLongLong(str); } +template<> inline unsigned long long ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedLongLong(str); } +template<> inline float ConvertStrTo(const std::string &str) { return ConvertStrToFloat(str); } +template<> inline double ConvertStrTo(const std::string &str) { return ConvertStrToDouble(str); } +template<> inline long double ConvertStrTo(const std::string &str) { return ConvertStrToLongDouble(str); } + +#if MPT_WSTRING_FORMAT +bool ConvertStrToBool(const std::wstring &str); +signed char ConvertStrToSignedChar(const std::wstring &str); +unsigned char ConvertStrToUnsignedChar(const std::wstring &str); +signed short ConvertStrToSignedShort(const std::wstring &str); +unsigned short ConvertStrToUnsignedShort(const std::wstring &str); +signed int ConvertStrToSignedInt(const std::wstring &str); +unsigned int ConvertStrToUnsignedInt(const std::wstring &str); +signed long ConvertStrToSignedLong(const std::wstring &str); +unsigned long ConvertStrToUnsignedLong(const std::wstring &str); +signed long long ConvertStrToSignedLongLong(const std::wstring &str); +unsigned long long ConvertStrToUnsignedLongLong(const std::wstring &str); +float ConvertStrToFloat(const std::wstring &str); +double ConvertStrToDouble(const std::wstring &str); +long double ConvertStrToLongDouble(const std::wstring &str); +template inline T ConvertStrTo(const std::wstring &str); // not defined, generates compiler error for non-specialized types +template<> inline std::wstring ConvertStrTo(const std::wstring &str) { return str; } +template<> inline bool ConvertStrTo(const std::wstring &str) { return ConvertStrToBool(str); } +template<> inline signed char ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedChar(str); } +template<> inline unsigned char ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedChar(str); } +template<> inline signed short ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedShort(str); } +template<> inline unsigned short ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedShort(str); } +template<> inline signed int ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedInt(str); } +template<> inline unsigned int ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedInt(str); } +template<> inline signed long ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedLong(str); } +template<> inline unsigned long ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedLong(str); } +template<> inline signed long long ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedLongLong(str); } +template<> inline unsigned long long ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedLongLong(str); } +template<> inline float ConvertStrTo(const std::wstring &str) { return ConvertStrToFloat(str); } +template<> inline double ConvertStrTo(const std::wstring &str) { return ConvertStrToDouble(str); } +template<> inline long double ConvertStrTo(const std::wstring &str) { return ConvertStrToLongDouble(str); } +#endif + +#if defined(MPT_WITH_MFC) +template +inline T ConvertStrTo(const CString &str) +{ + #if defined(UNICODE) && MPT_WSTRING_FORMAT + return ConvertStrTo(mpt::ToWide(str)); + #elif defined(UNICODE) + return ConvertStrTo(mpt::ToCharset(mpt::Charset::UTF8, str)); + #else // !UNICODE + return ConvertStrTo(mpt::ToCharset(mpt::Charset::Locale, str)); + #endif // UNICODE +} +#endif // MPT_WITH_MFC + +template +inline T ConvertStrTo(const char *str) +{ + if(!str) + { + return T(); + } + return ConvertStrTo(std::string(str)); +} + +#if MPT_WSTRING_FORMAT +#if MPT_USTRING_MODE_UTF8 +template<> inline mpt::ustring ConvertStrTo(const std::wstring &str) { return mpt::ToUnicode(str); } +#endif +template +inline T ConvertStrTo(const wchar_t *str) +{ + if(!str) + { + return T(); + } + return ConvertStrTo(std::wstring(str)); +} +#endif + +#if MPT_USTRING_MODE_UTF8 +template +inline T ConvertStrTo(const mpt::ustring &str) +{ + return ConvertStrTo(mpt::ToCharset(mpt::Charset::UTF8, str)); +} +template<> inline mpt::ustring ConvertStrTo(const mpt::ustring &str) { return str; } +#if MPT_WSTRING_CONVERT +template<> inline std::wstring ConvertStrTo(const mpt::ustring &str) { return mpt::ToWide(str); } +#endif +#endif + +#if defined(MPT_ENABLE_CHARSET_LOCALE) +template +inline T ConvertStrTo(const mpt::lstring &str) +{ + return ConvertStrTo(mpt::ToCharset(mpt::Charset::Locale, str)); +} +template<> inline mpt::lstring ConvertStrTo(const mpt::lstring &str) { return str; } +#endif + + +namespace mpt +{ +namespace String +{ +namespace Parse +{ + +unsigned char HexToUnsignedChar(const std::string &str); +unsigned short HexToUnsignedShort(const std::string &str); +unsigned int HexToUnsignedInt(const std::string &str); +unsigned long HexToUnsignedLong(const std::string &str); +unsigned long long HexToUnsignedLongLong(const std::string &str); + +template inline T Hex(const std::string &str); // not defined, generates compiler error for non-specialized types +template<> inline unsigned char Hex(const std::string &str) { return HexToUnsignedChar(str); } +template<> inline unsigned short Hex(const std::string &str) { return HexToUnsignedShort(str); } +template<> inline unsigned int Hex(const std::string &str) { return HexToUnsignedInt(str); } +template<> inline unsigned long Hex(const std::string &str) { return HexToUnsignedLong(str); } +template<> inline unsigned long long Hex(const std::string &str) { return HexToUnsignedLongLong(str); } + +template +inline T Hex(const char *str) +{ + if(!str) + { + return T(); + } + return Hex(std::string(str)); +} + +#if MPT_WSTRING_FORMAT + +template +inline T Hex(const std::wstring &str) +{ + return Hex(mpt::ToCharset(mpt::Charset::UTF8, str)); +} + +template +inline T Hex(const wchar_t *str) +{ + if(!str) + { + return T(); + } + return Hex(std::wstring(str)); +} + +#endif + +#if MPT_USTRING_MODE_UTF8 +template +inline T Hex(const mpt::ustring &str) +{ + return Hex(mpt::ToCharset(mpt::Charset::UTF8, str)); +} +#endif + +} // namespace Parse +} // namespace String +} // namespace mpt + + + +namespace mpt { namespace String { + +// Split the given string at separator positions into individual values returned as a vector. +// An empty string results in an empty vector. +// Leading or trailing separators result in a default-constructed element being inserted before or after the other elements. +template +std::vector Split(const mpt::ustring &str, const mpt::ustring &sep=U_(",")) +{ + std::vector vals; + std::size_t pos = 0; + while(str.find(sep, pos) != std::string::npos) + { + vals.push_back(ConvertStrTo(str.substr(pos, str.find(sep, pos) - pos))); + pos = str.find(sep, pos) + sep.length(); + } + if(!vals.empty() || (str.substr(pos).length() > 0)) + { + vals.push_back(ConvertStrTo(str.substr(pos))); + } + return vals; +} +template +std::vector Split(const std::string &str, const std::string &sep=std::string(",")) +{ + std::vector vals; + std::size_t pos = 0; + while(str.find(sep, pos) != std::string::npos) + { + vals.push_back(ConvertStrTo(str.substr(pos, str.find(sep, pos) - pos))); + pos = str.find(sep, pos) + sep.length(); + } + if(!vals.empty() || (str.substr(pos).length() > 0)) + { + vals.push_back(ConvertStrTo(str.substr(pos))); + } + return vals; +} + +} } // namespace mpt::String + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptThread.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptThread.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptThread.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptThread.h diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptTime.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptTime.cpp new file mode 100644 index 000000000..3b1440117 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptTime.cpp @@ -0,0 +1,309 @@ +/* + * mptTime.cpp + * ----------- + * Purpose: Various time utility functions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "mptTime.h" + +#include "mptStringBuffer.h" + +#include + +#if MPT_OS_WINDOWS +#include +#if defined(MODPLUG_TRACKER) +#include +#endif +#endif + + +OPENMPT_NAMESPACE_BEGIN + + +namespace mpt +{ +namespace Date +{ + +#if defined(MODPLUG_TRACKER) + +#if MPT_OS_WINDOWS + +namespace ANSI +{ + +uint64 Now() +{ + FILETIME filetime; + GetSystemTimeAsFileTime(&filetime); + return ((uint64)filetime.dwHighDateTime << 32 | filetime.dwLowDateTime); +} + +mpt::ustring ToUString(uint64 time100ns) +{ + constexpr std::size_t bufsize = 256; + + mpt::ustring result; + + FILETIME filetime; + SYSTEMTIME systime; + filetime.dwHighDateTime = (DWORD)(((uint64)time100ns) >> 32); + filetime.dwLowDateTime = (DWORD)((uint64)time100ns); + FileTimeToSystemTime(&filetime, &systime); + + TCHAR buf[bufsize]; + + GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, &systime, TEXT("yyyy-MM-dd"), buf, bufsize); + result.append(mpt::ToUnicode(mpt::String::ReadWinBuf(buf))); + + result.append(U_(" ")); + + GetTimeFormat(LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, &systime, TEXT("HH:mm:ss"), buf, bufsize); + result.append(mpt::ToUnicode(mpt::String::ReadWinBuf(buf))); + + result.append(U_(".")); + + result.append(mpt::ufmt::dec0<3>((unsigned)systime.wMilliseconds)); + + return result; + +} + +} // namespace ANSI + +#endif // MPT_OS_WINDOWS + +#endif // MODPLUG_TRACKER + +Unix::Unix() + : Value(0) +{ + return; +} + +Unix::Unix(int64 unixtime) + : Value(unixtime) +{ + return; +} + +Unix::operator int64 () const +{ + return Value; +} + +static int32 ToDaynum(int32 year, int32 month, int32 day) +{ + month = (month + 9) % 12; + year = year - (month / 10); + int32 daynum = year*365 + year/4 - year/100 + year/400 + (month*306 + 5)/10 + (day - 1); + return daynum; +} + +static void FromDaynum(int32 d, int32 & year, int32 & month, int32 & day) +{ + int64 g = d; + int64 y,ddd,mi,mm,dd; + + y = (10000*g + 14780)/3652425; + ddd = g - (365*y + y/4 - y/100 + y/400); + if(ddd < 0) + { + y = y - 1; + ddd = g - (365*y + y/4 - y/100 + y/400); + } + mi = (100*ddd + 52)/3060; + mm = (mi + 2)%12 + 1; + y = y + (mi + 2)/12; + dd = ddd - (mi*306 + 5)/10 + 1; + + year = static_cast(y); + month = static_cast(mm); + day = static_cast(dd); +} + +mpt::Date::Unix Unix::FromUTC(tm timeUtc) +{ + int32 daynum = ToDaynum(timeUtc.tm_year+1900, timeUtc.tm_mon+1, timeUtc.tm_mday); + int64 seconds = static_cast(daynum - ToDaynum(1970,1,1))*24*60*60 + timeUtc.tm_hour*60*60 + timeUtc.tm_min*60 + timeUtc.tm_sec; + return mpt::Date::Unix(seconds); +} + +tm Unix::AsUTC() const +{ + int64 tmp = Value; + int64 seconds = tmp % 60; tmp /= 60; + int64 minutes = tmp % 60; tmp /= 60; + int64 hours = tmp % 24; tmp /= 24; + int32 year = 0, month = 0, day = 0; + FromDaynum(static_cast(tmp) + ToDaynum(1970,1,1), year, month, day); + tm result; + MemsetZero(result); + result.tm_year = year - 1900; + result.tm_mon = month - 1; + result.tm_mday = day; + result.tm_hour = static_cast(hours); + result.tm_min = static_cast(minutes); + result.tm_sec = static_cast(seconds); + return result; +} + +mpt::ustring ToShortenedISO8601(tm date) +{ + // We assume date in UTC here. + // There are too many differences in supported format specifiers in strftime() + // and strftime does not support reduced precision ISO8601 at all. + // Just do the formatting ourselves. + mpt::ustring result; + mpt::ustring tz = U_("Z"); + if(date.tm_year == 0) + { + return result; + } + result += mpt::ufmt::dec0<4>(date.tm_year + 1900); + if(date.tm_mon < 0 || date.tm_mon > 11) + { + return result; + } + result += U_("-") + mpt::ufmt::dec0<2>(date.tm_mon + 1); + if(date.tm_mday < 1 || date.tm_mday > 31) + { + return result; + } + result += U_("-") + mpt::ufmt::dec0<2>(date.tm_mday); + if(date.tm_hour == 0 && date.tm_min == 0 && date.tm_sec == 0) + { + return result; + } + if(date.tm_hour < 0 || date.tm_hour > 23) + { + return result; + } + if(date.tm_min < 0 || date.tm_min > 59) + { + return result; + } + result += U_("T"); + if(date.tm_isdst > 0) + { + tz = U_("+01:00"); + } + result += mpt::ufmt::dec0<2>(date.tm_hour) + U_(":") + mpt::ufmt::dec0<2>(date.tm_min); + if(date.tm_sec < 0 || date.tm_sec > 61) + { + return result + tz; + } + result += U_(":") + mpt::ufmt::dec0<2>(date.tm_sec); + result += tz; + return result; +} + +} // namespace Date +} // namespace mpt + + + +#ifdef MODPLUG_TRACKER + +namespace Util +{ + +#if MPT_OS_WINDOWS + +void MultimediaClock::Init() +{ + m_CurrentPeriod = 0; +} + +void MultimediaClock::SetPeriod(uint32 ms) +{ + TIMECAPS caps; + MemsetZero(caps); + if(timeGetDevCaps(&caps, sizeof(caps)) != MMSYSERR_NOERROR) + { + return; + } + if((caps.wPeriodMax == 0) || (caps.wPeriodMin > caps.wPeriodMax)) + { + return; + } + ms = std::clamp(mpt::saturate_cast(ms), caps.wPeriodMin, caps.wPeriodMax); + if(timeBeginPeriod(ms) != MMSYSERR_NOERROR) + { + return; + } + m_CurrentPeriod = ms; +} + +void MultimediaClock::Cleanup() +{ + if(m_CurrentPeriod > 0) + { + if(timeEndPeriod(m_CurrentPeriod) != MMSYSERR_NOERROR) + { + // should not happen + MPT_ASSERT_NOTREACHED(); + } + m_CurrentPeriod = 0; + } +} + +MultimediaClock::MultimediaClock() +{ + Init(); +} + +MultimediaClock::MultimediaClock(uint32 ms) +{ + Init(); + SetResolution(ms); +} + +MultimediaClock::~MultimediaClock() +{ + Cleanup(); +} + +uint32 MultimediaClock::SetResolution(uint32 ms) +{ + if(m_CurrentPeriod == ms) + { + return m_CurrentPeriod; + } + Cleanup(); + if(ms != 0) + { + SetPeriod(ms); + } + return GetResolution(); +} + +uint32 MultimediaClock::GetResolution() const +{ + return m_CurrentPeriod; +} + +uint32 MultimediaClock::Now() const +{ + return timeGetTime(); +} + +uint64 MultimediaClock::NowNanoseconds() const +{ + return (uint64)timeGetTime() * (uint64)1000000; +} + +#endif // MPT_OS_WINDOWS + +} // namespace Util + +#endif // MODPLUG_TRACKER + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/mptTime.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptTime.h new file mode 100644 index 000000000..1332710a8 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/mptTime.h @@ -0,0 +1,116 @@ +/* + * mptTime.h + * --------- + * Purpose: Various time utility functions. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + +#include + +#include + + +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt +{ +namespace Date +{ + +#if defined(MODPLUG_TRACKER) + +#if MPT_OS_WINDOWS + +namespace ANSI +{ +// uint64 counts 100ns since 1601-01-01T00:00Z + +uint64 Now(); + +mpt::ustring ToUString(uint64 time100ns); // i.e. 2015-01-15 18:32:01.718 + +} // namespacee ANSI + +#endif // MPT_OS_WINDOWS + +#endif // MODPLUG_TRACKER + +class Unix +{ +// int64 counts 1s since 1970-01-01T00:00Z +private: + int64 Value; +public: + Unix(); + explicit Unix(int64 unixtime); + operator int64 () const; +public: + static mpt::Date::Unix FromUTC(tm timeUtc); + tm AsUTC() const; +}; + +mpt::ustring ToShortenedISO8601(tm date); // i.e. 2015-01-15T18:32:01Z + +} // namespace Date +} // namespace mpt + + + +#ifdef MODPLUG_TRACKER + +namespace Util +{ + +#if MPT_OS_WINDOWS + +// RAII wrapper around timeBeginPeriod/timeEndPeriod/timeGetTime (on Windows). +// This clock is monotonic, even across changing its resolution. +// This is needed to synchronize time in Steinberg APIs (ASIO and VST). +class MultimediaClock +{ +private: + uint32 m_CurrentPeriod; +private: + void Init(); + void SetPeriod(uint32 ms); + void Cleanup(); +public: + MultimediaClock(); + MultimediaClock(uint32 ms); + ~MultimediaClock(); +public: + // Sets the desired resolution in milliseconds, returns the obtained resolution in milliseconds. + // A parameter of 0 causes the resolution to be reset to system defaults. + // A return value of 0 means the resolution is unknown, but timestamps will still be valid. + uint32 SetResolution(uint32 ms); + // Returns obtained resolution in milliseconds. + // A return value of 0 means the resolution is unknown, but timestamps will still be valid. + uint32 GetResolution() const; + // Returns current instantaneous timestamp in milliseconds. + // The epoch (offset) of the timestamps is undefined but constant until the next system reboot. + // The resolution is the value returned from GetResolution(). + uint32 Now() const; + // Returns current instantaneous timestamp in nanoseconds. + // The epoch (offset) of the timestamps is undefined but constant until the next system reboot. + // The resolution is the value returned from GetResolution() in milliseconds. + uint64 NowNanoseconds() const; +}; + +#endif // MPT_OS_WINDOWS + +} // namespace Util + +#endif // MODPLUG_TRACKER + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptUUID.cpp similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp rename to Frameworks/OpenMPT.old/OpenMPT/common/mptUUID.cpp diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptUUID.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptUUID.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptUUID.h diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/mptWine.cpp similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp rename to Frameworks/OpenMPT.old/OpenMPT/common/mptWine.cpp diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptWine.h b/Frameworks/OpenMPT.old/OpenMPT/common/mptWine.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/common/mptWine.h rename to Frameworks/OpenMPT.old/OpenMPT/common/mptWine.h diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/serialization_utils.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/serialization_utils.cpp new file mode 100644 index 000000000..0c82bf4e1 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/serialization_utils.cpp @@ -0,0 +1,737 @@ +/* + * serialization_utils.cpp + * ----------------------- + * Purpose: Serializing data to and from MPTM files. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" + +#include "serialization_utils.h" + +#include +#include +#include + +#include "misc_util.h" + + +OPENMPT_NAMESPACE_BEGIN + + +namespace srlztn +{ + + +#ifdef MPT_ALL_LOGGING +#define SSB_LOGGING +#endif + + +#ifdef SSB_LOGGING +#define SSB_LOG(x) MPT_LOG(LogDebug, "serialization", x) +#else +#define SSB_LOG(x) MPT_DO { } MPT_WHILE_0 +#endif + + +static const uint8 HeaderId_FlagByte = 0; + +// Indexing starts from 0. +static inline bool Testbit(uint8 val, uint8 bitindex) {return ((val & (1 << bitindex)) != 0);} + +static inline void Setbit(uint8& val, uint8 bitindex, bool newval) +{ + if(newval) val |= (1 << bitindex); + else val &= ~(1 << bitindex); +} + + +bool ID::IsPrintable() const +{ + for(std::size_t i = 0; i < m_ID.length(); ++i) + { + if(m_ID[i] <= 0 || isprint(static_cast(m_ID[i])) == 0) + { + return false; + } + } + return true; +} + + +//Format: First bit tells whether the size indicator is 1 or 2 bytes. +static void WriteAdaptive12String(std::ostream& oStrm, const std::string& str) +{ + uint16 s = static_cast(str.size()); + LimitMax(s, uint16(uint16_max / 2)); + mpt::IO::WriteAdaptiveInt16LE(oStrm, s); + oStrm.write(str.c_str(), s); +} + + +void WriteItemString(std::ostream& oStrm, const std::string &str) +{ + uint32 id = static_cast(std::min(str.size(), static_cast((uint32_max >> 4)))) << 4; + id |= 12; // 12 == 1100b + Binarywrite(oStrm, id); + id >>= 4; + if(id > 0) + oStrm.write(str.data(), id); +} + + +void ReadItemString(std::istream& iStrm, std::string& str, const DataSize) +{ + // bits 0,1: Bytes per char type: 1,2,3,4. + // bits 2,3: Bytes in size indicator, 1,2,3,4 + uint32 id = 0; + Binaryread(iStrm, id, 1); + const uint8 nSizeBytes = (id & 12) >> 2; // 12 == 1100b + if (nSizeBytes > 0) + { + uint8 bytes = std::min(uint8(3), nSizeBytes); + uint8 v2 = 0; + uint8 v3 = 0; + uint8 v4 = 0; + if(bytes >= 1) Binaryread(iStrm, v2); + if(bytes >= 2) Binaryread(iStrm, v3); + if(bytes >= 3) Binaryread(iStrm, v4); + id &= 0xff; + id |= (v2 << 8) | (v3 << 16) | (v4 << 24); + } + // Limit to 1 MB. + str.resize(std::min(id >> 4, uint32(1000000))); + for(size_t i = 0; i < str.size(); i++) + iStrm.read(&str[i], 1); + + id = (id >> 4) - static_cast(str.size()); + if(id > 0) + iStrm.ignore(id); +} + + +mpt::ustring ID::AsString() const +{ + if(IsPrintable()) + { + return mpt::ToUnicode(mpt::Charset::ISO8859_1, m_ID); + } + if(m_ID.length() > 8) + { + return mpt::ustring(); + } + uint64le val; + val.set(0); + std::memcpy(&val, m_ID.data(), m_ID.length()); + return mpt::ufmt::val(val); +} + + +const char Ssb::s_EntryID[3] = {'2','2','8'}; + + +#ifdef SSB_LOGGING +static const mpt::uchar tstrWriteHeader[] = UL_("Write header with ID = %1\n"); +static const mpt::uchar tstrWriteProgress[] = UL_("Wrote entry: {num, id, rpos, size} = {%1, %2, %3, %4}\n"); +static const mpt::uchar tstrWritingMap[] = UL_("Writing map to rpos: %1\n"); +static const mpt::uchar tstrMapEntryWrite[] = UL_("Writing map entry: id=%1, rpos=%2, size=%3\n"); +static const mpt::uchar strWriteNote[] = UL_("Write note: "); +static const mpt::uchar tstrEndOfStream[] = UL_("End of stream(rpos): %1\n"); + +static const mpt::uchar tstrReadingHeader[] = UL_("Read header with expected ID = %1\n"); +static const mpt::uchar strNoMapInFile[] = UL_("No map in the file.\n"); +static const mpt::uchar strIdMismatch[] = UL_("ID mismatch, terminating read.\n"); +static const mpt::uchar strIdMatch[] = UL_("ID match, continuing reading.\n"); +static const mpt::uchar tstrReadingMap[] = UL_("Reading map from rpos: %1\n"); +static const mpt::uchar tstrEndOfMap[] = UL_("End of map(rpos): %1\n"); +static const mpt::uchar tstrReadProgress[] = UL_("Read entry: {num, id, rpos, size, desc} = {%1, %2, %3, %4, %5}\n"); +static const mpt::uchar tstrNoEntryFound[] = UL_("No entry with id %1 found.\n"); +static const mpt::uchar strReadNote[] = UL_("Read note: "); +#endif + + +Ssb::Ssb() + : m_Status(SNT_NONE) + , m_nFixedEntrySize(0) + , m_posStart(0) + , m_nIdbytes(IdSizeVariable) + , m_nCounter(0) + , m_Flags((1 << RwfWMapStartPosEntry) + (1 << RwfWMapSizeEntry) + (1 << RwfWVersionNum)) +{ + return; +} + + +SsbWrite::SsbWrite(std::ostream& os) + : oStrm(os) + , m_posEntrycount(0) + , m_posMapPosField(0) +{ + return; +} + + +SsbRead::SsbRead(std::istream& is) + : iStrm(is) + , m_nReadVersion(0) + , m_rposMapBegin(0) + , m_posMapEnd(0) + , m_posDataBegin(0) + , m_rposEndofHdrData(0) + , m_nReadEntrycount(0) + , m_nNextReadHint(0) +{ + return; +} + + +void SsbWrite::AddWriteNote(const SsbStatus s) +{ + m_Status |= s; + SSB_LOG(mpt::format(U_("%1: 0x%2\n"))(strWriteNote, mpt::ufmt::hex(s))); +} + +void SsbRead::AddReadNote(const SsbStatus s) +{ + m_Status |= s; + SSB_LOG(mpt::format(U_("%1: 0x%2\n"))(strReadNote, mpt::ufmt::hex(s))); +} + +void SsbRead::AddReadNote(const ReadEntry* const pRe, const NumType nNum) +{ + m_Status |= SNT_PROGRESS; + SSB_LOG(mpt::format(mpt::ustring(tstrReadProgress))( + nNum, + (pRe && pRe->nIdLength < 30 && m_Idarray.size() > 0) ? ID(&m_Idarray[pRe->nIdpos], pRe->nIdLength).AsString() : U_(""), + (pRe) ? pRe->rposStart : 0, + (pRe && pRe->nSize != invalidDatasize) ? mpt::ufmt::val(pRe->nSize) : U_(""), + U_(""))); +#ifndef SSB_LOGGING + MPT_UNREFERENCED_PARAMETER(pRe); + MPT_UNREFERENCED_PARAMETER(nNum); +#endif +} + +// Called after writing an entry. +void SsbWrite::AddWriteNote(const ID &id, const NumType nEntryNum, const DataSize nBytecount, const RposType rposStart) +{ + m_Status |= SNT_PROGRESS; + SSB_LOG(mpt::format(mpt::ustring(tstrWriteProgress))(nEntryNum, id.AsString(), rposStart, nBytecount)); +#ifndef SSB_LOGGING + MPT_UNREFERENCED_PARAMETER(id); + MPT_UNREFERENCED_PARAMETER(nEntryNum); + MPT_UNREFERENCED_PARAMETER(nBytecount); + MPT_UNREFERENCED_PARAMETER(rposStart); +#endif +} + + +void SsbRead::ResetReadstatus() +{ + m_Status = SNT_NONE; + m_Idarray.reserve(32); + m_Idarray.push_back(0); +} + + +void SsbWrite::WriteMapItem(const ID &id, + const RposType& rposDataStart, + const DataSize& nDatasize, + const char* pszDesc) +{ + SSB_LOG(mpt::format(mpt::ustring(tstrMapEntryWrite))( + (id.GetSize() > 0) ? id.AsString() : U_(""), + rposDataStart, + nDatasize)); + + std::ostringstream mapStream; + + if(m_nIdbytes > 0) + { + if (m_nIdbytes != IdSizeVariable && id.GetSize() != m_nIdbytes) + { AddWriteNote(SNW_CHANGING_IDSIZE_WITH_FIXED_IDSIZESETTING); return; } + + if (m_nIdbytes == IdSizeVariable) //Variablesize ID? + mpt::IO::WriteAdaptiveInt16LE(mapStream, static_cast(id.GetSize())); + + if(id.GetSize() > 0) + mapStream.write(id.GetBytes(), id.GetSize()); + } + + if (GetFlag(RwfWMapStartPosEntry)) //Startpos + mpt::IO::WriteAdaptiveInt64LE(mapStream, rposDataStart); + if (GetFlag(RwfWMapSizeEntry)) //Entrysize + mpt::IO::WriteAdaptiveInt64LE(mapStream, nDatasize); + if (GetFlag(RwfWMapDescEntry)) //Entry descriptions + WriteAdaptive12String(mapStream, std::string(pszDesc)); + + m_MapStreamString.append(mapStream.str()); + +} + + +void SsbWrite::IncrementWriteCounter() +{ + m_nCounter++; + if (m_nCounter >= (uint16_max >> 2)) + { + FinishWrite(); + AddWriteNote(SNW_MAX_WRITE_COUNT_REACHED); + } +} + + +void SsbWrite::BeginWrite(const ID &id, const uint64& nVersion) +{ + SSB_LOG(mpt::format(mpt::ustring(tstrWriteHeader))(id.AsString())); + + ResetWritestatus(); + + if(!oStrm.good()) + { AddWriteNote(SNRW_BADGIVEN_STREAM); return; } + + // Start bytes. + oStrm.write(s_EntryID, sizeof(s_EntryID)); + + m_posStart = oStrm.tellp() - Offtype(sizeof(s_EntryID)); + + // Object ID. + { + uint8 idsize = static_cast(id.GetSize()); + Binarywrite(oStrm, idsize); + if(idsize > 0) oStrm.write(id.GetBytes(), id.GetSize()); + } + + // Form header. + uint8 header = 0; + + SetFlag(RwfWMapStartPosEntry, GetFlag(RwfWMapStartPosEntry) && m_nFixedEntrySize == 0); + SetFlag(RwfWMapSizeEntry, GetFlag(RwfWMapSizeEntry) && m_nFixedEntrySize == 0); + + header = (m_nIdbytes != 4) ? (m_nIdbytes & 3) : 3; //0,1 : Bytes per IDtype, 0,1,2,4 + Setbit(header, 2, GetFlag(RwfWMapStartPosEntry)); //2 : Startpos in map? + Setbit(header, 3, GetFlag(RwfWMapSizeEntry)); //3 : Datasize in map? + Setbit(header, 4, GetFlag(RwfWVersionNum)); //4 : Version numeric field? + Setbit(header, 7, GetFlag(RwfWMapDescEntry)); //7 : Entrydescriptions in map? + + // Write header + Binarywrite(oStrm, header); + + // Additional options. + uint8 tempU8 = 0; + Setbit(tempU8, 0, (m_nIdbytes == IdSizeVariable) || (m_nIdbytes == 3) || (m_nIdbytes > 4)); + Setbit(tempU8, 1, m_nFixedEntrySize != 0); + + const uint8 flags = tempU8; + if(flags != s_DefaultFlagbyte) + { + mpt::IO::WriteAdaptiveInt32LE(oStrm, 2); //Headersize - now it is 2. + Binarywrite(oStrm, HeaderId_FlagByte); + Binarywrite(oStrm, flags); + } + else + mpt::IO::WriteAdaptiveInt32LE(oStrm, 0); + + if(Testbit(header, 4)) // Version(numeric)? + mpt::IO::WriteAdaptiveInt64LE(oStrm, nVersion); + + if(Testbit(flags, 0)) // Custom IDbytecount? + { + uint8 n = (m_nIdbytes == IdSizeVariable) ? 1 : static_cast((m_nIdbytes << 1)); + Binarywrite(oStrm, n); + } + + if(Testbit(flags, 1)) // Fixedsize entries? + mpt::IO::WriteAdaptiveInt32LE(oStrm, m_nFixedEntrySize); + + //Entrycount. Reserve two bytes(max uint16_max / 4 entries), actual value is written after writing data. + m_posEntrycount = oStrm.tellp(); + Binarywrite(oStrm, 0); + + SetFlag(RwfRwHasMap, (m_nIdbytes != 0 || GetFlag(RwfWMapStartPosEntry) || GetFlag(RwfWMapSizeEntry) || GetFlag(RwfWMapDescEntry))); + + m_posMapPosField = oStrm.tellp(); + if (GetFlag(RwfRwHasMap)) //Mapping begin pos(reserve space - actual value is written after writing data) + Binarywrite(oStrm, 0); +} + + +SsbRead::ReadRv SsbRead::OnReadEntry(const ReadEntry* pE, const ID &id, const Postype& posReadBegin) +{ + if (pE != nullptr) + AddReadNote(pE, m_nCounter); + else if (GetFlag(RwfRMapHasId) == false) // Not ID's in map. + { + ReadEntry e; + e.rposStart = static_cast(posReadBegin - m_posStart); + e.nSize = static_cast(iStrm.tellg() - posReadBegin); + AddReadNote(&e, m_nCounter); + } + else // Entry not found. + { + SSB_LOG(mpt::format(mpt::ustring(tstrNoEntryFound))(id.AsString())); +#ifndef SSB_LOGGING + MPT_UNREFERENCED_PARAMETER(id); +#endif + return EntryNotFound; + } + m_nCounter++; + return EntryRead; +} + + +void SsbWrite::OnWroteItem(const ID &id, const Postype& posBeforeWrite) +{ + const Offtype nRawEntrySize = oStrm.tellp() - posBeforeWrite; + + MPT_MAYBE_CONSTANT_IF(nRawEntrySize < 0 || static_cast(nRawEntrySize) > std::numeric_limits::max()) + { + AddWriteNote(SNW_INSUFFICIENT_DATASIZETYPE); + return; + } + + if(GetFlag(RwfRMapHasSize) && (nRawEntrySize < 0 || static_cast(nRawEntrySize) > (std::numeric_limits::max() >> 2))) + { AddWriteNote(SNW_DATASIZETYPE_OVERFLOW); return; } + + DataSize nEntrySize = static_cast(nRawEntrySize); + + // Handle fixed size entries: + if (m_nFixedEntrySize > 0) + { + if(nEntrySize <= m_nFixedEntrySize) + { + for(uint32 i = 0; i(posBeforeWrite - m_posStart), nEntrySize, ""); + + AddWriteNote(id, m_nCounter, nEntrySize, static_cast(posBeforeWrite - m_posStart)); + IncrementWriteCounter(); +} + + +void SsbRead::BeginRead(const ID &id, const uint64& nVersion) +{ + SSB_LOG(mpt::format(mpt::ustring(tstrReadingHeader))(id.AsString())); + + ResetReadstatus(); + + if (!iStrm.good()) + { AddReadNote(SNRW_BADGIVEN_STREAM); return; } + + m_posStart = iStrm.tellg(); + + // Start bytes. + { + char temp[sizeof(s_EntryID)]; + ArrayReader(sizeof(s_EntryID))(iStrm, temp, sizeof(s_EntryID)); + if(std::memcmp(temp, s_EntryID, sizeof(s_EntryID))) + { + AddReadNote(SNR_STARTBYTE_MISMATCH); + return; + } + } + + // Compare IDs. + uint8 storedIdLen = 0; + Binaryread(iStrm, storedIdLen); + char storedIdBuf[256]; + Clear(storedIdBuf); + if(storedIdLen > 0) + { + iStrm.read(storedIdBuf, storedIdLen); + } + if(!(id == ID(storedIdBuf, storedIdLen))) + { + AddReadNote(SNR_OBJECTCLASS_IDMISMATCH); + } + if ((m_Status & SNT_FAILURE) != 0) + { + SSB_LOG(mpt::ustring(strIdMismatch)); + return; + } + + SSB_LOG(mpt::ustring(strIdMatch)); + + // Header + uint8 tempU8; + Binaryread(iStrm, tempU8); + const uint8 header = tempU8; + m_nIdbytes = ((header & 3) == 3) ? 4 : (header & 3); + if (Testbit(header, 6)) + SetFlag(RwfRTwoBytesDescChar, true); + + // Read headerdata size + uint32 tempU32 = 0; + mpt::IO::ReadAdaptiveInt32LE(iStrm, tempU32); + const uint32 headerdatasize = tempU32; + + // If headerdatasize != 0, read known headerdata and ignore rest. + uint8 flagbyte = s_DefaultFlagbyte; + if(headerdatasize >= 2) + { + Binaryread(iStrm, tempU8); + if(tempU8 == HeaderId_FlagByte) + Binaryread(iStrm, flagbyte); + + iStrm.ignore( (tempU8 == HeaderId_FlagByte) ? headerdatasize - 2 : headerdatasize - 1); + } + + uint64 tempU64 = 0; + + // Read version numeric if available. + if (Testbit(header, 4)) + { + mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); + m_nReadVersion = tempU64; + if(tempU64 > nVersion) + AddReadNote(SNR_LOADING_OBJECT_WITH_LARGER_VERSION); + } + + if (Testbit(header, 5)) + { + Binaryread(iStrm, tempU8); + iStrm.ignore(tempU8); + } + + if(Testbit(flagbyte, 0)) // Custom ID? + { + Binaryread(iStrm, tempU8); + if ((tempU8 & 1) != 0) + m_nIdbytes = IdSizeVariable; + else + m_nIdbytes = (tempU8 >> 1); + if(m_nIdbytes == 0) + AddReadNote(SNR_NO_ENTRYIDS_WITH_CUSTOMID_DEFINED); + } + + m_nFixedEntrySize = 0; + if(Testbit(flagbyte, 1)) // Fixedsize entries? + mpt::IO::ReadAdaptiveInt32LE(iStrm, m_nFixedEntrySize); + + SetFlag(RwfRMapHasStartpos, Testbit(header, 2)); + SetFlag(RwfRMapHasSize, Testbit(header, 3)); + SetFlag(RwfRMapHasId, (m_nIdbytes > 0)); + SetFlag(RwfRMapHasDesc, Testbit(header, 7)); + SetFlag(RwfRwHasMap, GetFlag(RwfRMapHasId) || GetFlag(RwfRMapHasStartpos) || GetFlag(RwfRMapHasSize) || GetFlag(RwfRMapHasDesc)); + + if (GetFlag(RwfRwHasMap) == false) + { + SSB_LOG(mpt::ustring(strNoMapInFile)); + } + + if (Testbit(flagbyte, 2)) // Object description? + { + uint16 size = 0; + mpt::IO::ReadAdaptiveInt16LE(iStrm, size); + iStrm.ignore(size * (GetFlag(RwfRTwoBytesDescChar) ? 2 : 1)); + } + + if(Testbit(flagbyte, 3)) + iStrm.ignore(5); + + // Read entrycount + mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); + if(tempU64 > 16000) + // The current code can only write 16383 entries because it uses a Adaptive64LE with a fixed size=2 + // Additionally, 16000 is an arbitrary limit to avoid an out-of-memory DoS when caching the map. + { AddReadNote(SNR_TOO_MANY_ENTRIES_TO_READ); return; } + + m_nReadEntrycount = static_cast(tempU64); + if(m_nReadEntrycount == 0) + AddReadNote(SNR_ZEROENTRYCOUNT); + + // Read map rpos if map exists. + if (GetFlag(RwfRwHasMap)) + { + mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); + if(tempU64 > static_cast(std::numeric_limits::max())) + { AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; } + } + + const Offtype rawEndOfHdrData = iStrm.tellg() - m_posStart; + + MPT_MAYBE_CONSTANT_IF(rawEndOfHdrData < 0 || static_cast(rawEndOfHdrData) > std::numeric_limits::max()) + { + AddReadNote(SNR_INSUFFICIENT_RPOSTYPE); + return; + } + + m_rposEndofHdrData = static_cast(rawEndOfHdrData); + m_rposMapBegin = (GetFlag(RwfRwHasMap)) ? static_cast(tempU64) : m_rposEndofHdrData; + + if (GetFlag(RwfRwHasMap) == false) + m_posMapEnd = m_posStart + m_rposEndofHdrData; + + SetFlag(RwfRHeaderIsRead, true); +} + + +void SsbRead::CacheMap() +{ + if(GetFlag(RwfRwHasMap) || m_nFixedEntrySize > 0) + { + iStrm.seekg(m_posStart + m_rposMapBegin); + + if(iStrm.fail()) + { AddReadNote(SNR_BADSTREAM_AFTER_MAPHEADERSEEK); return; } + + SSB_LOG(mpt::format(mpt::ustring(tstrReadingMap))(m_rposMapBegin)); + + mapData.resize(m_nReadEntrycount); + m_Idarray.reserve(m_nReadEntrycount * 4); + + //Read map + for(NumType i = 0; i 0 && (Util::MaxValueOfType(nOldEnd) - nOldEnd >= nIdsize)) + { + m_Idarray.resize(nOldEnd + nIdsize); + iStrm.read(&m_Idarray[nOldEnd], nIdsize); + } + mapData[i].nIdLength = nIdsize; + mapData[i].nIdpos = nOldEnd; + + // Read position. + if(GetFlag(RwfRMapHasStartpos)) + { + uint64 tempU64; + mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); + if(tempU64 > static_cast(std::numeric_limits::max())) + { AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; } + mapData[i].rposStart = static_cast(tempU64); + } + + // Read entry size. + if (m_nFixedEntrySize > 0) + mapData[i].nSize = m_nFixedEntrySize; + else if(GetFlag(RwfRMapHasSize)) // Map has datasize field. + { + uint64 tempU64; + mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64); + if(tempU64 > static_cast(std::numeric_limits::max())) + { AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; } + mapData[i].nSize = static_cast(tempU64); + } + + // If there's no entry startpos in map, count start pos from datasizes. + // Here readentry.rposStart is set to relative position from databegin. + if (mapData[i].nSize != invalidDatasize && GetFlag(RwfRMapHasStartpos) == false) + mapData[i].rposStart = (i > 0) ? mapData[i-1].rposStart + mapData[i-1].nSize : 0; + + if(GetFlag(RwfRMapHasDesc)) //Map has entrydescriptions? + { + uint16 size = 0; + mpt::IO::ReadAdaptiveInt16LE(iStrm, size); + if(GetFlag(RwfRTwoBytesDescChar)) + iStrm.ignore(size * 2); + else + iStrm.ignore(size); + } + } + m_posMapEnd = iStrm.tellg(); + SSB_LOG(mpt::format(mpt::ustring(tstrEndOfMap))(m_posMapEnd - m_posStart)); + } + + SetFlag(RwfRMapCached, true); + m_posDataBegin = (m_rposMapBegin == m_rposEndofHdrData) ? m_posMapEnd : m_posStart + Postype(m_rposEndofHdrData); + iStrm.seekg(m_posDataBegin); + + // If there are no positions in the map but there are entry sizes, rposStart will + // be relative to data start. Now that posDataBegin is known, make them relative to + // startpos. + if (GetFlag(RwfRMapHasStartpos) == false && (GetFlag(RwfRMapHasSize) || m_nFixedEntrySize > 0)) + { + const RposType offset = static_cast(m_posDataBegin - m_posStart); + for(size_t i = 0; i < m_nReadEntrycount; i++) + mapData[i].rposStart += offset; + } +} + + +const ReadEntry* SsbRead::Find(const ID &id) +{ + iStrm.clear(); + if (GetFlag(RwfRMapCached) == false) + CacheMap(); + + if (m_nFixedEntrySize > 0 && GetFlag(RwfRMapHasStartpos) == false && GetFlag(RwfRMapHasSize) == false) + iStrm.seekg(m_posDataBegin + Postype(m_nFixedEntrySize * m_nCounter)); + + if (GetFlag(RwfRMapHasId) == true) + { + const size_t nEntries = mapData.size(); + for(size_t i0 = 0; i0 < nEntries; i0++) + { + const size_t i = (i0 + m_nNextReadHint) % nEntries; + if(mapData[i].nIdpos < m_Idarray.size() && id == ID(&m_Idarray[mapData[i].nIdpos], mapData[i].nIdLength)) + { + m_nNextReadHint = (i + 1) % nEntries; + if (mapData[i].rposStart != 0) + iStrm.seekg(m_posStart + Postype(mapData[i].rposStart)); + return &mapData[i]; + } + } + } + return nullptr; +} + + +void SsbWrite::FinishWrite() +{ + const Postype posDataEnd = oStrm.tellp(); + + Postype posMapStart = oStrm.tellp(); + + SSB_LOG(mpt::format(mpt::ustring(tstrWritingMap))(posMapStart - m_posStart)); + + if (GetFlag(RwfRwHasMap)) //Write map + { + oStrm.write(m_MapStreamString.c_str(), m_MapStreamString.length()); + } + + const Postype posMapEnd = oStrm.tellp(); + + // Write entry count. + oStrm.seekp(m_posEntrycount); + + // Write a fixed size=2 Adaptive64LE because space for this value has already been reserved berforehand. + mpt::IO::WriteAdaptiveInt64LE(oStrm, m_nCounter, 2); + + if (GetFlag(RwfRwHasMap)) + { // Write map start position. + oStrm.seekp(m_posMapPosField); + const uint64 rposMap = posMapStart - m_posStart; + + // Write a fixed size=8 Adaptive64LE because space for this value has already been reserved berforehand. + mpt::IO::WriteAdaptiveInt64LE(oStrm, rposMap, 8); + + } + + // Seek to end. + oStrm.seekp(std::max(posMapEnd, posDataEnd)); + + SSB_LOG(mpt::format(mpt::ustring(tstrEndOfStream))(oStrm.tellp() - m_posStart)); +} + +} // namespace srlztn + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/serialization_utils.h b/Frameworks/OpenMPT.old/OpenMPT/common/serialization_utils.h new file mode 100644 index 000000000..ee8f58b51 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/serialization_utils.h @@ -0,0 +1,553 @@ +/* + * serialization_utils.h + * --------------------- + * Purpose: Serializing data to and from MPTM files. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + +#include "../common/mptBaseTypes.h" +#include "../common/mptIO.h" +#include "../common/Endianness.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +OPENMPT_NAMESPACE_BEGIN + +namespace srlztn //SeRiaLiZaTioN +{ + +typedef std::ios::off_type Offtype; +typedef Offtype Postype; + +typedef uintptr_t DataSize; // Data size type. +typedef uintptr_t RposType; // Relative position type. +typedef uintptr_t NumType; // Entry count type. + +const DataSize invalidDatasize = DataSize(-1); + +enum +{ + SNT_PROGRESS = 0x08000000, // = 1 << 27 + SNT_FAILURE = 0x40000000, // = 1 << 30 + SNT_NOTE = 0x20000000, // = 1 << 29 + SNT_WARNING = 0x10000000, // = 1 << 28 + SNT_NONE = 0, + + SNRW_BADGIVEN_STREAM = 1 | SNT_FAILURE, + + // Read failures. + SNR_BADSTREAM_AFTER_MAPHEADERSEEK = 2 | SNT_FAILURE, + SNR_STARTBYTE_MISMATCH = 3 | SNT_FAILURE, + SNR_BADSTREAM_AT_MAP_READ = 4 | SNT_FAILURE, + SNR_INSUFFICIENT_STREAM_OFFTYPE = 5 | SNT_FAILURE, + SNR_OBJECTCLASS_IDMISMATCH = 6 | SNT_FAILURE, + SNR_TOO_MANY_ENTRIES_TO_READ = 7 | SNT_FAILURE, + SNR_INSUFFICIENT_RPOSTYPE = 8 | SNT_FAILURE, + + // Read notes and warnings. + SNR_ZEROENTRYCOUNT = 0x80 | SNT_NOTE, // 0x80 == 1 << 7 + SNR_NO_ENTRYIDS_WITH_CUSTOMID_DEFINED = 0x100 | SNT_NOTE, + SNR_LOADING_OBJECT_WITH_LARGER_VERSION = 0x200 | SNT_NOTE, + + // Write failures. + SNW_INSUFFICIENT_FIXEDSIZE = (0x10) | SNT_FAILURE, + SNW_CHANGING_IDSIZE_WITH_FIXED_IDSIZESETTING = (0x11) | SNT_FAILURE, + SNW_DATASIZETYPE_OVERFLOW = (0x13) | SNT_FAILURE, + SNW_MAX_WRITE_COUNT_REACHED = (0x14) | SNT_FAILURE, + SNW_INSUFFICIENT_DATASIZETYPE = (0x16) | SNT_FAILURE +}; + + +enum +{ + IdSizeVariable = uint16_max, + IdSizeMaxFixedSize = (uint8_max >> 1) +}; + +typedef int32 SsbStatus; + + +struct ReadEntry +{ + ReadEntry() : nIdpos(0), rposStart(0), nSize(invalidDatasize), nIdLength(0) {} + + uintptr_t nIdpos; // Index of id start in ID array. + RposType rposStart; // Entry start position. + DataSize nSize; // Entry size. + uint16 nIdLength; // Length of id. +}; + + +enum Rwf +{ + RwfWMapStartPosEntry, // Write. True to include data start pos entry to map. + RwfWMapSizeEntry, // Write. True to include data size entry to map. + RwfWMapDescEntry, // Write. True to include description entry to map. + RwfWVersionNum, // Write. True to include version numeric. + RwfRMapCached, // Read. True if map has been cached. + RwfRMapHasId, // Read. True if map has IDs + RwfRMapHasStartpos, // Read. True if map data start pos. + RwfRMapHasSize, // Read. True if map has entry size. + RwfRMapHasDesc, // Read. True if map has entry description. + RwfRTwoBytesDescChar, // Read. True if map description characters are two bytes. + RwfRHeaderIsRead, // Read. True when header is read. + RwfRwHasMap, // Read/write. True if map exists. + RwfNumFlags +}; + + +template +inline void Binarywrite(std::ostream& oStrm, const T& data) +{ + mpt::IO::WriteIntLE(oStrm, data); +} + +template<> +inline void Binarywrite(std::ostream& oStrm, const float& data) +{ + IEEE754binary32LE tmp = IEEE754binary32LE(data); + mpt::IO::Write(oStrm, tmp); +} + +template<> +inline void Binarywrite(std::ostream& oStrm, const double& data) +{ + IEEE754binary64LE tmp = IEEE754binary64LE(data); + mpt::IO::Write(oStrm, tmp); +} + +template +inline void WriteItem(std::ostream& oStrm, const T& data) +{ + static_assert(std::is_trivial::value == true, ""); + Binarywrite(oStrm, data); +} + +void WriteItemString(std::ostream& oStrm, const std::string &str); + +template <> +inline void WriteItem(std::ostream& oStrm, const std::string& str) {WriteItemString(oStrm, str);} + + +template +inline void Binaryread(std::istream& iStrm, T& data) +{ + mpt::IO::ReadIntLE(iStrm, data); +} + +template<> +inline void Binaryread(std::istream& iStrm, float& data) +{ + IEEE754binary32LE tmp = IEEE754binary32LE(0.0f); + mpt::IO::Read(iStrm, tmp); + data = tmp; +} + +template<> +inline void Binaryread(std::istream& iStrm, double& data) +{ + IEEE754binary64LE tmp = IEEE754binary64LE(0.0); + mpt::IO::Read(iStrm, tmp); + data = tmp; +} + +//Read only given number of bytes to the beginning of data; data bytes are memset to 0 before reading. +template +inline void Binaryread(std::istream& iStrm, T& data, const Offtype bytecount) +{ + mpt::IO::ReadBinaryTruncatedLE(iStrm, data, static_cast(bytecount)); +} + +template <> +inline void Binaryread(std::istream& iStrm, float& data, const Offtype bytecount) +{ + typedef IEEE754binary32LE T; + std::byte bytes[sizeof(T)]; + std::memset(bytes, 0, sizeof(T)); + mpt::IO::ReadRaw(iStrm, bytes, std::min(static_cast(bytecount), sizeof(T))); + // There is not much we can sanely do for truncated floats, + // thus we ignore what we just read and return 0. + data = 0.0f; +} + +template <> +inline void Binaryread(std::istream& iStrm, double& data, const Offtype bytecount) +{ + typedef IEEE754binary64LE T; + std::byte bytes[sizeof(T)]; + std::memset(bytes, 0, sizeof(T)); + mpt::IO::ReadRaw(iStrm, bytes, std::min(static_cast(bytecount), sizeof(T))); + // There is not much we can sanely do for truncated floats, + // thus we ignore what we just read and return 0. + data = 0.0; +} + + +template +inline void ReadItem(std::istream& iStrm, T& data, const DataSize nSize) +{ + static_assert(std::is_trivial::value == true, ""); + if (nSize == sizeof(T) || nSize == invalidDatasize) + Binaryread(iStrm, data); + else + Binaryread(iStrm, data, nSize); +} + +void ReadItemString(std::istream& iStrm, std::string& str, const DataSize); + +template <> +inline void ReadItem(std::istream& iStrm, std::string& str, const DataSize nSize) +{ + ReadItemString(iStrm, str, nSize); +} + + + +class ID +{ +private: + std::string m_ID; // NOTE: can contain null characters ('\0') +public: + ID() { } + ID(const std::string &id) : m_ID(id) { } + ID(const char *beg, const char *end) : m_ID(beg, end) { } + ID(const char *id) : m_ID(id?id:"") { } + ID(const char * str, std::size_t len) : m_ID(str, str + len) { } + template + static ID FromInt(const T &val) + { + static_assert(std::numeric_limits::is_integer); + typename mpt::make_le::type valle; + valle = val; + return ID(std::string(mpt::byte_cast(mpt::as_raw_memory(valle).data()), mpt::byte_cast(mpt::as_raw_memory(valle).data() + sizeof(valle)))); + } + bool IsPrintable() const; + mpt::ustring AsString() const; + const char *GetBytes() const { return m_ID.c_str(); } + std::size_t GetSize() const { return m_ID.length(); } + bool operator == (const ID &other) const { return m_ID == other.m_ID; } + bool operator != (const ID &other) const { return m_ID != other.m_ID; } +}; + + + +class Ssb +{ + +protected: + + Ssb(); + +public: + + SsbStatus GetStatus() const + { + return m_Status; + } + +protected: + + // When writing, returns the number of entries written. + // When reading, returns the number of entries read not including unrecognized entries. + NumType GetCounter() const {return m_nCounter;} + + void SetFlag(Rwf flag, bool val) {m_Flags.set(flag, val);} + bool GetFlag(Rwf flag) const {return m_Flags[flag];} + +protected: + + SsbStatus m_Status; + + uint32 m_nFixedEntrySize; // Read/write: If > 0, data entries have given fixed size. + + Postype m_posStart; // Read/write: Stream position at the beginning of object. + + uint16 m_nIdbytes; // Read/Write: Tells map ID entry size in bytes. If size is variable, value is IdSizeVariable. + NumType m_nCounter; // Read/write: Keeps count of entries written/read. + + std::bitset m_Flags; // Read/write: Various flags. + +protected: + + enum : uint8 { s_DefaultFlagbyte = 0 }; + static const char s_EntryID[3]; + +}; + + + +class SsbRead + : public Ssb +{ + +public: + + enum ReadRv // Read return value. + { + EntryRead, + EntryNotFound + }; + enum IdMatchStatus + { + IdMatch, IdMismatch + }; + typedef std::vector::const_iterator ReadIterator; + + SsbRead(std::istream& iStrm); + + // Call this to begin reading: must be called before other read functions. + void BeginRead(const ID &id, const uint64& nVersion); + + // After calling BeginRead(), this returns number of entries in the file. + NumType GetNumEntries() const {return m_nReadEntrycount;} + + // Returns read iterator to the beginning of entries. + // The behaviour of read iterators is undefined if map doesn't + // contain entry ids or data begin positions. + ReadIterator GetReadBegin(); + + // Returns read iterator to the end(one past last) of entries. + ReadIterator GetReadEnd(); + + // Compares given id with read entry id + IdMatchStatus CompareId(const ReadIterator& iter, const ID &id); + + uint64 GetReadVersion() {return m_nReadVersion;} + + // Read item using default read implementation. + template + ReadRv ReadItem(T& obj, const ID &id) {return ReadItem(obj, id, srlztn::ReadItem);} + + // Read item using given function. + template + ReadRv ReadItem(T& obj, const ID &id, FuncObj); + + // Read item using read iterator. + template + ReadRv ReadIterItem(const ReadIterator& iter, T& obj) {return ReadIterItem(iter, obj, srlztn::ReadItem);} + template + ReadRv ReadIterItem(const ReadIterator& iter, T& obj, FuncObj func); + +private: + + // Reads map to cache. + void CacheMap(); + + // Searches for entry with given ID. If found, returns pointer to corresponding entry, else + // returns nullptr. + const ReadEntry* Find(const ID &id); + + // Called after reading an object. + ReadRv OnReadEntry(const ReadEntry* pE, const ID &id, const Postype& posReadBegin); + + void AddReadNote(const SsbStatus s); + + // Called after reading entry. pRe is a pointer to associated map entry if exists. + void AddReadNote(const ReadEntry* const pRe, const NumType nNum); + + void ResetReadstatus(); + +private: + + // mapData is a cache that facilitates faster access to the stored data + // without having to reparse on every access. + // Iterator invalidation in CacheMap() is not a problem because every code + // path that ever returns an iterator into mapData does CacheMap exactly once + // beforehand. Following calls use this already cached map. As the data is + // immutable when reading, there is no need to ever invalidate the cache and + // redo CacheMap(). + + std::istream& iStrm; + + std::vector m_Idarray; // Read: Holds entry ids. + + std::vector mapData; // Read: Contains map information. + uint64 m_nReadVersion; // Read: Version is placed here when reading. + RposType m_rposMapBegin; // Read: If map exists, rpos of map begin, else m_rposEndofHdrData. + Postype m_posMapEnd; // Read: If map exists, map end position, else pos of end of hdrData. + Postype m_posDataBegin; // Read: Data begin position. + RposType m_rposEndofHdrData; // Read: rpos of end of header data. + NumType m_nReadEntrycount; // Read: Number of entries. + + NumType m_nNextReadHint; // Read: Hint where to start looking for the next read entry. + +}; + + + +class SsbWrite + : public Ssb +{ + +public: + + SsbWrite(std::ostream& oStrm); + + // Write header + void BeginWrite(const ID &id, const uint64& nVersion); + + // Write item using default write implementation. + template + void WriteItem(const T& obj, const ID &id) {WriteItem(obj, id, &srlztn::WriteItem);} + + // Write item using given function. + template + void WriteItem(const T& obj, const ID &id, FuncObj); + + // Writes mapping. + void FinishWrite(); + +private: + + // Called after writing an item. + void OnWroteItem(const ID &id, const Postype& posBeforeWrite); + + void AddWriteNote(const SsbStatus s); + void AddWriteNote(const ID &id, + const NumType nEntryNum, + const DataSize nBytecount, + const RposType rposStart); + + // Writes mapping item to mapstream. + void WriteMapItem(const ID &id, + const RposType& rposDataStart, + const DataSize& nDatasize, + const char* pszDesc); + + void ResetWritestatus() {m_Status = SNT_NONE;} + + void IncrementWriteCounter(); + +private: + + std::ostream& oStrm; + + Postype m_posEntrycount; // Write: Pos of entrycount field. + Postype m_posMapPosField; // Write: Pos of map position field. + std::string m_MapStreamString; // Write: Map stream string. + +}; + + +template +void SsbWrite::WriteItem(const T& obj, const ID &id, FuncObj Func) +{ + const Postype pos = oStrm.tellp(); + Func(oStrm, obj); + OnWroteItem(id, pos); +} + +template +SsbRead::ReadRv SsbRead::ReadItem(T& obj, const ID &id, FuncObj Func) +{ + const ReadEntry* pE = Find(id); + const Postype pos = iStrm.tellg(); + if (pE != nullptr || GetFlag(RwfRMapHasId) == false) + Func(iStrm, obj, (pE) ? (pE->nSize) : invalidDatasize); + return OnReadEntry(pE, id, pos); +} + + +template +SsbRead::ReadRv SsbRead::ReadIterItem(const ReadIterator& iter, T& obj, FuncObj func) +{ + iStrm.clear(); + if (iter->rposStart != 0) + iStrm.seekg(m_posStart + Postype(iter->rposStart)); + const Postype pos = iStrm.tellg(); + func(iStrm, obj, iter->nSize); + return OnReadEntry(&(*iter), ID(&m_Idarray[iter->nIdpos], iter->nIdLength), pos); +} + + +inline SsbRead::IdMatchStatus SsbRead::CompareId(const ReadIterator& iter, const ID &id) +{ + if(iter->nIdpos >= m_Idarray.size()) return IdMismatch; + return (id == ID(&m_Idarray[iter->nIdpos], iter->nIdLength)) ? IdMatch : IdMismatch; +} + + +inline SsbRead::ReadIterator SsbRead::GetReadBegin() +{ + MPT_ASSERT(GetFlag(RwfRMapHasId) && (GetFlag(RwfRMapHasStartpos) || GetFlag(RwfRMapHasSize) || m_nFixedEntrySize > 0)); + if (GetFlag(RwfRMapCached) == false) + CacheMap(); + return mapData.begin(); +} + + +inline SsbRead::ReadIterator SsbRead::GetReadEnd() +{ + if (GetFlag(RwfRMapCached) == false) + CacheMap(); + return mapData.end(); +} + + +template +struct VectorWriter +{ + VectorWriter(size_t nCount) : m_nCount(nCount) {} + void operator()(std::ostream &oStrm, const std::vector &vec) + { + for(size_t i = 0; i < m_nCount; i++) + { + Binarywrite(oStrm, vec[i]); + } + } + size_t m_nCount; +}; + +template +struct VectorReader +{ + VectorReader(size_t nCount) : m_nCount(nCount) {} + void operator()(std::istream& iStrm, std::vector &vec, const size_t) + { + vec.resize(m_nCount); + for(std::size_t i = 0; i < m_nCount; ++i) + { + Binaryread(iStrm, vec[i]); + } + } + size_t m_nCount; +}; + +template +struct ArrayReader +{ + ArrayReader(size_t nCount) : m_nCount(nCount) {} + void operator()(std::istream& iStrm, T* pData, const size_t) + { + for(std::size_t i=0; i // MFC core +// cppcheck-suppress missingInclude +#include // MFC standard components +// cppcheck-suppress missingInclude +#include // MFC extensions +// cppcheck-suppress missingInclude +#include // MFC support for Windows Common Controls +// cppcheck-suppress missingInclude +#include +// cppcheck-suppress missingInclude +#include +#ifdef MPT_MFC_FULL +// cppcheck-suppress missingInclude +#include +#endif // MPT_MFC_FULL +// cppcheck-suppress missingInclude +#include + +#endif // MPT_WITH_MFC + +#if MPT_OS_WINDOWS + +#include +#include +#include +#include + +#endif // MPT_OS_WINDOWS + +#endif // MODPLUG_TRACKER + + +#if MPT_COMPILER_MSVC +#include +#endif + + +// this will be available everywhere + +#include "../common/mptBaseMacros.h" +// +// +// +// +// + +#include "../common/mptBaseTypes.h" +// "mptBaseMacros.h" +// +// +// +// + +#include "../common/mptAssert.h" +// "mptBaseMacros.h" + +#include "../common/mptBaseUtils.h" +// +// +// +// +// + +#include "../common/mptException.h" +// +// +// + +#include "../common/mptSpan.h" +// "mptBaseTypes.h" +// +// + +#include "../common/mptMemory.h" +// "mptAssert.h" +// "mptBaseTypes.h" +// "mptSpan.h" +// +// +// + +#include "../common/mptAlloc.h" +// "mptBaseMacros.h" +// "mptMemory.h" +// "mptSpan.h" +// +// +// +// + +#include "../common/mptString.h" +// +// +// +// +// +// + +#include "../common/mptStringBuffer.h" + +#include "../common/mptOSError.h" +// "mptException.h" +// "mptString.h" +// +// + +#include "../common/mptExceptionText.h" +// "mptException.h" +// "mptString.h" +// + +#include "../common/mptStringFormat.h" + +#include "../common/mptPathString.h" + +#include "../common/Logging.h" +// + +#include "../common/misc_util.h" +// +// + +// for std::abs +#include +#include +#include +#include + + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/version.cpp b/Frameworks/OpenMPT.old/OpenMPT/common/version.cpp new file mode 100644 index 000000000..8684b7044 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/version.cpp @@ -0,0 +1,795 @@ +/* + * version.cpp + * ----------- + * Purpose: OpenMPT version handling. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "version.h" + +#include "mptString.h" +#include "mptStringFormat.h" +#include "mptStringParse.h" + +#include "versionNumber.h" +#include "svn_version.h" + + + +OPENMPT_NAMESPACE_BEGIN + + + +#define MPT_MAKE_VERSION_NUMERIC_HELPER(prefix,v0,v1,v2,v3) Version( prefix ## v0 , prefix ## v1 , prefix ## v2 , prefix ## v3 ) +#define MPT_MAKE_VERSION_NUMERIC(v0,v1,v2,v3) MPT_MAKE_VERSION_NUMERIC_HELPER(0x, v0, v1, v2, v3) + +#define MPT_VERSION_CURRENT MPT_MAKE_VERSION_NUMERIC(VER_MAJORMAJOR,VER_MAJOR,VER_MINOR,VER_MINORMINOR) + + + +static_assert((MPT_VERSION_CURRENT.GetRawVersion() & 0xffffu) != 0x0000u, "Version numbers ending in .00.00 shall never exist again, as they make interpreting the version number ambiguous for file formats which can only store the two major parts of the version number (e.g. IT and S3M)."); + + + +Version Version::Current() noexcept +{ + return MPT_VERSION_CURRENT; +} + +mpt::ustring Version::GetOpenMPTVersionString() const +{ + return U_("OpenMPT ") + ToUString(); +} + +Version Version::Parse(const mpt::ustring &s) +{ + uint32 result = 0; + std::vector numbers = mpt::String::Split(s, U_(".")); + for (std::size_t i = 0; i < numbers.size() && i < 4; ++i) + { + result |= (mpt::String::Parse::Hex(numbers[i]) & 0xff) << ((3 - i) * 8); + } + return Version(result); +} + +mpt::ustring Version::ToUString() const +{ + uint32 v = m_Version; + if(v == 0) + { + // Unknown version + return U_("Unknown"); + } else if((v & 0xFFFF) == 0) + { + // Only parts of the version number are known (e.g. when reading the version from the IT or S3M file header) + return mpt::format(U_("%1.%2"))(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF)); + } else + { + // Full version info available + return mpt::format(U_("%1.%2.%3.%4"))(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF), mpt::ufmt::HEX0<2>((v >> 8) & 0xFF), mpt::ufmt::HEX0<2>((v) & 0xFF)); + } +} + +Version Version::WithoutTestNumber() const noexcept +{ + return Version(m_Version & 0xFFFFFF00u); +} + +Version Version::WithoutPatchOrTestNumbers() const noexcept +{ + return Version(m_Version & 0xFFFF0000u); +} + +bool Version::IsTestVersion() const noexcept +{ + return ( + // Legacy + (*this > MPT_V("1.17.02.54") && *this < MPT_V("1.18.02.00") && *this != MPT_V("1.18.00.00")) + || + // Test builds have non-zero VER_MINORMINOR + (*this > MPT_V("1.18.02.00") && ((m_Version & 0xFFFFFF00u) != m_Version)) + ); +} + + + +namespace Source { + +static mpt::ustring GetUrl() +{ + #ifdef OPENMPT_VERSION_URL + return mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_VERSION_URL); + #else + return mpt::ustring(); + #endif +} + +static int GetRevision() +{ + #if defined(OPENMPT_VERSION_REVISION) + return OPENMPT_VERSION_REVISION; + #elif defined(OPENMPT_VERSION_SVNVERSION) + std::string svnversion = OPENMPT_VERSION_SVNVERSION; + if(svnversion.length() == 0) + { + return 0; + } + if(svnversion.find(":") != std::string::npos) + { + svnversion = svnversion.substr(svnversion.find(":") + 1); + } + if(svnversion.find("-") != std::string::npos) + { + svnversion = svnversion.substr(svnversion.find("-") + 1); + } + if(svnversion.find("M") != std::string::npos) + { + svnversion = svnversion.substr(0, svnversion.find("M")); + } + if(svnversion.find("S") != std::string::npos) + { + svnversion = svnversion.substr(0, svnversion.find("S")); + } + if(svnversion.find("P") != std::string::npos) + { + svnversion = svnversion.substr(0, svnversion.find("P")); + } + return ConvertStrTo(svnversion); + #else + MPT_WARNING_STATEMENT("SVN revision unknown. Please check your build system."); + return 0; + #endif +} + +static bool IsDirty() +{ + #if defined(OPENMPT_VERSION_DIRTY) + return OPENMPT_VERSION_DIRTY != 0; + #elif defined(OPENMPT_VERSION_SVNVERSION) + std::string svnversion = OPENMPT_VERSION_SVNVERSION; + if(svnversion.length() == 0) + { + return false; + } + if(svnversion.find("M") != std::string::npos) + { + return true; + } + return false; + #else + return false; + #endif +} + +static bool HasMixedRevisions() +{ + #if defined(OPENMPT_VERSION_MIXEDREVISIONS) + return OPENMPT_VERSION_MIXEDREVISIONS != 0; + #elif defined(OPENMPT_VERSION_SVNVERSION) + std::string svnversion = OPENMPT_VERSION_SVNVERSION; + if(svnversion.length() == 0) + { + return false; + } + if(svnversion.find(":") != std::string::npos) + { + return true; + } + if(svnversion.find("-") != std::string::npos) + { + return true; + } + if(svnversion.find("S") != std::string::npos) + { + return true; + } + if(svnversion.find("P") != std::string::npos) + { + return true; + } + return false; + #else + return false; + #endif +} + +static bool IsPackage() +{ + #if defined(OPENMPT_VERSION_IS_PACKAGE) + return OPENMPT_VERSION_IS_PACKAGE != 0; + #else + return false; + #endif +} + +static mpt::ustring GetSourceDate() +{ + #if defined(OPENMPT_VERSION_DATE) + return mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_VERSION_DATE); + #else + return mpt::ustring(); + #endif +} + +} // namespace Source + +SourceInfo::SourceInfo() + : m_Url(Source::GetUrl()) + , m_Revision(Source::GetRevision()) + , m_IsDirty(Source::IsDirty()) + , m_HasMixedRevisions(Source::HasMixedRevisions()) + , m_IsPackage(Source::IsPackage()) + , m_Date(Source::GetSourceDate()) +{ +} + +mpt::ustring SourceInfo::GetUrlWithRevision() const +{ + if(m_Url.empty() || (m_Revision == 0)) + { + return mpt::ustring(); + } + return m_Url + UL_("@") + mpt::ufmt::val(m_Revision); +} + +mpt::ustring SourceInfo::GetStateString() const +{ + mpt::ustring retval; + if(m_IsDirty) + { + retval += UL_("+dirty"); + } + if(m_HasMixedRevisions) + { + retval += UL_("+mixed"); + } + if(retval.empty()) + { + retval += UL_("clean"); + } + if(m_IsPackage) + { + retval += UL_("-pkg"); + } + return retval; +} + +SourceInfo SourceInfo::Current() +{ + return SourceInfo(); +} + + + +namespace Build { + +bool IsReleasedBuild() +{ + return !(Version::Current().IsTestVersion() || IsDebugBuild() || Source::IsDirty() || Source::HasMixedRevisions()); +} + +bool IsDebugBuild() +{ + #if defined(MPT_BUILD_DEBUG) || defined(DEBUG) || defined(_DEBUG) + return true; + #else + return false; + #endif +} + +mpt::ustring GetBuildDateString() +{ + mpt::ustring result; + #ifdef MODPLUG_TRACKER + #if defined(OPENMPT_BUILD_DATE) + result = mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_BUILD_DATE ); + #else + result = mpt::ToUnicode(mpt::Charset::ASCII, __DATE__ " " __TIME__ ); + #endif + #else // !MODPLUG_TRACKER + result = SourceInfo::Current().Date(); + #endif // MODPLUG_TRACKER + return result; +} + +static mpt::ustring GetBuildFlagsString() +{ + mpt::ustring retval; + #ifdef MODPLUG_TRACKER + if(Version::Current().IsTestVersion()) + { + retval += UL_(" TEST"); + } + #endif // MODPLUG_TRACKER + if(IsDebugBuild()) + { + retval += UL_(" DEBUG"); + } + return retval; +} + +mpt::ustring GetBuildFeaturesString() +{ + mpt::ustring retval; + #ifdef LIBOPENMPT_BUILD + retval = UL_("") + #if defined(MPT_WITH_ZLIB) + UL_(" +ZLIB") + #endif + #if defined(MPT_WITH_MINIZ) + UL_(" +MINIZ") + #endif + #if !defined(MPT_WITH_ZLIB) && !defined(MPT_WITH_MINIZ) + UL_(" -INFLATE") + #endif + #if defined(MPT_WITH_MPG123) + UL_(" +MPG123") + #endif + #if defined(MPT_WITH_MINIMP3) + UL_(" +MINIMP3") + #endif + #if defined(MPT_WITH_MEDIAFOUNDATION) + UL_(" +MF") + #endif + #if !defined(MPT_WITH_MPG123) && !defined(MPT_WITH_MINIMP3) && !defined(MPT_WITH_MEDIAFOUNDATION) + UL_(" -MP3") + #endif + #if defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) + UL_(" +VORBIS") + #endif + #if defined(MPT_WITH_STBVORBIS) + UL_(" +STBVORBIS") + #endif + #if !(defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE)) && !defined(MPT_WITH_STBVORBIS) + UL_(" -VORBIS") + #endif + #if !defined(NO_PLUGINS) + UL_(" +PLUGINS") + #else + UL_(" -PLUGINS") + #endif + #if defined(MPT_WITH_DMO) + UL_(" +DMO") + #endif + ; + #endif + #ifdef MODPLUG_TRACKER + retval += UL_("") + #if defined(UNICODE) + UL_(" UNICODE") + #else + UL_(" ANSI") + #endif + #ifdef NO_VST + UL_(" NO_VST") + #endif + #ifndef MPT_WITH_DMO + UL_(" NO_DMO") + #endif + #ifdef NO_PLUGINS + UL_(" NO_PLUGINS") + #endif + #ifndef MPT_WITH_ASIO + UL_(" NO_ASIO") + #endif + ; + #endif + return retval; +} + +mpt::ustring GetBuildCompilerString() +{ + mpt::ustring retval; + #if MPT_COMPILER_GENERIC + retval += U_("Generic C++11 Compiler"); + #elif MPT_COMPILER_MSVC + #if defined(_MSC_FULL_VER) && defined(_MSC_BUILD) && (_MSC_BUILD > 0) + retval += mpt::format(U_("Microsoft Compiler %1.%2.%3.%4")) + ( _MSC_FULL_VER / 10000000 + , mpt::ufmt::dec0<2>((_MSC_FULL_VER / 100000) % 100) + , mpt::ufmt::dec0<5>(_MSC_FULL_VER % 100000) + , mpt::ufmt::dec0<2>(_MSC_BUILD) + ); + #elif defined(_MSC_FULL_VER) + retval += mpt::format(U_("Microsoft Compiler %1.%2.%3")) + ( _MSC_FULL_VER / 10000000 + , mpt::ufmt::dec0<2>((_MSC_FULL_VER / 100000) % 100) + , mpt::ufmt::dec0<5>(_MSC_FULL_VER % 100000) + ); + #else + retval += mpt::format(U_("Microsoft Compiler %1.%2"))(MPT_COMPILER_MSVC_VERSION / 100, MPT_COMPILER_MSVC_VERSION % 100); + #endif + #elif MPT_COMPILER_GCC + retval += mpt::format(U_("GNU Compiler Collection %1.%2.%3"))(MPT_COMPILER_GCC_VERSION / 10000, (MPT_COMPILER_GCC_VERSION / 100) % 100, MPT_COMPILER_GCC_VERSION % 100); + #elif MPT_COMPILER_CLANG + retval += mpt::format(U_("Clang %1.%2.%3"))(MPT_COMPILER_CLANG_VERSION / 10000, (MPT_COMPILER_CLANG_VERSION / 100) % 100, MPT_COMPILER_CLANG_VERSION % 100); + #else + retval += U_("unknown"); + #endif + return retval; +} + +static mpt::ustring GetRevisionString() +{ + mpt::ustring result; + if(Source::GetRevision() == 0) + { + return result; + } + result = U_("-r") + mpt::ufmt::val(Source::GetRevision()); + if(Source::HasMixedRevisions()) + { + result += UL_("!"); + } + if(Source::IsDirty()) + { + result += UL_("+"); + } + if(Source::IsPackage()) + { + result += UL_("p"); + } + return result; +} + +mpt::ustring GetVersionString(FlagSet strings) +{ + std::vector result; + if(strings[StringVersion]) + { + result.push_back(mpt::ufmt::val(Version::Current())); + } + if(strings[StringRevision]) + { + if(!IsReleasedBuild()) + { + result.push_back(GetRevisionString()); + } + } + if(strings[StringBitness]) + { + result.push_back(mpt::format(U_(" %1 bit"))(mpt::arch_bits)); + } + if(strings[StringSourceInfo]) + { + const SourceInfo sourceInfo = SourceInfo::Current(); + if(!sourceInfo.GetUrlWithRevision().empty()) + { + result.push_back(mpt::format(U_(" %1"))(sourceInfo.GetUrlWithRevision())); + } + if(!sourceInfo.Date().empty()) + { + result.push_back(mpt::format(U_(" (%1)"))(sourceInfo.Date())); + } + if(!sourceInfo.GetStateString().empty()) + { + result.push_back(mpt::format(U_(" %1"))(sourceInfo.GetStateString())); + } + } + if(strings[StringBuildFlags]) + { + if(!IsReleasedBuild()) + { + result.push_back(GetBuildFlagsString()); + } + } + if(strings[StringBuildFeatures]) + { + result.push_back(GetBuildFeaturesString()); + } + return mpt::String::Trim(mpt::String::Combine(result, U_(""))); +} + +mpt::ustring GetVersionStringPure() +{ + FlagSet strings; + strings |= Build::StringVersion; + strings |= Build::StringRevision; + #ifdef MODPLUG_TRACKER + strings |= Build::StringBitness; + #endif + return GetVersionString(strings); +} + +mpt::ustring GetVersionStringSimple() +{ + FlagSet strings; + strings |= Build::StringVersion; + strings |= Build::StringRevision; + strings |= Build::StringBuildFlags; + return GetVersionString(strings); +} + +mpt::ustring GetVersionStringExtended() +{ + FlagSet strings; + strings |= Build::StringVersion; + strings |= Build::StringRevision; + #ifdef MODPLUG_TRACKER + strings |= Build::StringBitness; + #endif + #ifndef MODPLUG_TRACKER + strings |= Build::StringSourceInfo; + #endif + strings |= Build::StringBuildFlags; + #ifdef MODPLUG_TRACKER + strings |= Build::StringBuildFeatures; + #endif + return GetVersionString(strings); +} + +mpt::ustring GetURL(Build::Url key) +{ + mpt::ustring result; + switch(key) + { + case Url::Website: + #ifdef LIBOPENMPT_BUILD + result = U_("https://lib.openmpt.org/"); + #else + result = U_("https://openmpt.org/"); + #endif + break; + case Url::Download: + #ifdef MODPLUG_TRACKER + result = IsReleasedBuild() ? U_("https://openmpt.org/download") : U_("https://builds.openmpt.org/builds/"); + #else + result = U_("https://lib.openmpt.org/libopenmpt/download/"); + #endif + break; + case Url::Forum: + result = U_("https://forum.openmpt.org/"); + break; + case Url::Bugtracker: + result = U_("https://bugs.openmpt.org/"); + break; + case Url::Updates: + result = U_("https://openmpt.org/download"); + break; + case Url::TopPicks: + result = U_("https://openmpt.org/top_picks"); + break; + } + return result; +} + +mpt::ustring GetFullCreditsString() +{ + return mpt::ToUnicode(mpt::Charset::UTF8, +#ifdef MODPLUG_TRACKER + "OpenMPT / ModPlug Tracker\n" +#else + "libopenmpt (based on OpenMPT / ModPlug Tracker)\n" +#endif + "\n" + "Copyright \xC2\xA9 2004-2021 Contributors\n" + "Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n" + "\n" + "Contributors:\n" + "Johannes Schultz (2008-2021)\n" + "J\xC3\xB6rn Heusipp (2012-2021)\n" + "Ahti Lepp\xC3\xA4nen (2005-2011)\n" + "Robin Fernandes (2004-2007)\n" + "Sergiy Pylypenko (2007)\n" + "Eric Chavanon (2004-2005)\n" + "Trevor Nunes (2004)\n" + "Olivier Lapicque (1997-2003)\n" + "\n" + "Additional patch submitters:\n" + "coda (https://coda.s3m.us/)\n" + "Jo\xC3\xA3o Baptista de Paula e Silva (https://joaobapt.com/)\n" + "kode54 (https://kode54.net/)\n" + "Revenant (https://revenant1.net/)\n" + "xaimus (http://xaimus.com/)\n" + "\n" + "Thanks to:\n" + "\n" + "Konstanty for the XMMS-ModPlug resampling implementation\n" + "http://modplug-xmms.sourceforge.net/\n" + "\n" +#ifdef MODPLUG_TRACKER + "Stephan M. Bernsee for pitch shifting source code\n" + "http://www.dspdimension.com/\n" + "\n" + "Aleksey Vaneev of Voxengo for r8brain sample rate converter\n" + "https://github.com/avaneev/r8brain-free-src\n" + "\n" + "Olli Parviainen for SoundTouch Library (time stretching)\n" + "https://www.surina.net/soundtouch/\n" + "\n" +#endif +#ifndef NO_VST + "Hermann Seib for his example VST Host implementation\n" + "http://www.hermannseib.com/english/vsthost.htm\n" + "\n" + "Benjamin \"BeRo\" Rosseaux for his independent VST header\n" + "https://blog.rosseaux.net/\n" + "\n" +#endif + "Storlek for all the IT compatibility hints and testcases\n" + "as well as the IMF, MDL, OKT and ULT loaders\n" + "http://schismtracker.org/\n" + "\n" + "Sergei \"x0r\" Kolzun for various hints on Scream Tracker 2 compatibility\n" + "https://github.com/viiri/st2play\n" + "\n" + "Laurent Cl\xc3\xA9vy for unofficial MO3 documentation and decompression code\n" + "https://github.com/lclevy/unmo3\n" + "\n" + "Ben \"GreaseMonkey\" Russell for IT sample compression code\n" + "https://github.com/iamgreaser/it2everything/\n" + "\n" + "Antti S. Lankila for Amiga resampler implementation\n" + "https://bel.fi/alankila/modguide/interpolate.txt\n" + "\n" + "Shayde / Reality Productions for Opal OPL3 emulator\n" + "https://www.3eality.com/\n" + "\n" + "Ryuhei Mori for TinyFFT\n" + "https://github.com/ryuhei-mori/tinyfft\n" + "\n" +#ifdef MPT_WITH_ZLIB + "Jean-loup Gailly and Mark Adler for zlib\n" + "https://zlib.net/\n" + "\n" +#endif +#ifdef MPT_WITH_MINIZ + "Rich Geldreich et al. for miniz\n" + "https://github.com/richgel999/miniz\n" + "\n" +#endif +#ifdef MPT_WITH_LHASA + "Simon Howard for lhasa\n" + "https://fragglet.github.io/lhasa/\n" + "\n" +#endif +#ifdef MPT_WITH_UNRAR + "Alexander L. Roshal for UnRAR\n" + "https://rarlab.com/\n" + "\n" +#endif +#ifdef MPT_WITH_PORTAUDIO + "PortAudio contributors\n" + "http://www.portaudio.com/\n" + "\n" +#endif +#ifdef MPT_WITH_RTAUDIO + "Gary P. Scavone, McGill University\n" + "https://www.music.mcgill.ca/~gary/rtaudio/\n" + "\n" +#endif +#ifdef MPT_WITH_FLAC + "Josh Coalson / Xiph.Org Foundation for libFLAC\n" + "https://xiph.org/flac/\n" + "\n" +#endif +#if defined(MPT_WITH_MPG123) + "The mpg123 project for libmpg123\n" + "https://mpg123.de/\n" + "\n" +#endif +#ifdef MPT_WITH_MINIMP3 + "Lion (github.com/lieff) for minimp3\n" + "https://github.com/lieff/minimp3/\n" + "\n" +#endif +#ifdef MPT_WITH_STBVORBIS + "Sean Barrett for stb_vorbis\n" + "https://github.com/nothings/stb/\n" + "\n" +#endif +#ifdef MPT_WITH_OGG + "Xiph.Org Foundation for libogg\n" + "https://xiph.org/ogg/\n" + "\n" +#endif +#if defined(MPT_WITH_VORBIS) || defined(MPT_WITH_LIBVORBISFILE) + "Xiph.Org Foundation for libvorbis\n" + "https://xiph.org/vorbis/\n" + "\n" +#endif +#if defined(MPT_WITH_OPUS) + "Xiph.Org, Skype Limited, Octasic, Jean-Marc Valin, Timothy B. Terriberry,\n" + "CSIRO, Gregory Maxwell, Mark Borgerding, Erik de Castro Lopo,\n" + "Xiph.Org Foundation, Microsoft Corporation, Broadcom Corporation for libopus\n" + "https://opus-codec.org/\n" + "\n" +#endif +#if defined(MPT_WITH_OPUSFILE) + "Xiph.Org Foundation and contributors for libopusfile\n" + "https://opus-codec.org/\n" + "\n" +#endif +#if defined(MPT_WITH_OPUSENC) + "Xiph.Org Foundation, Jean-Marc Valin and contributors for libopusenc\n" + "https://git.xiph.org/?p=libopusenc.git;a=summary\n" + "\n" +#endif +#if defined(MPT_WITH_LAME) + "The LAME project for LAME\n" + "https://lame.sourceforge.io/\n" + "\n" +#endif +#if defined(MPT_WITH_NLOHMANNJSON) + "Niels Lohmann et al. for nlohmann-json\n" + "https://github.com/nlohmann/json\n" + "\n" +#endif +#ifdef MODPLUG_TRACKER + "Lennart Poettering and David Henningsson for RealtimeKit\n" + "http://git.0pointer.net/rtkit.git/\n" + "\n" + "Gary P. Scavone for RtMidi\n" + "https://www.music.mcgill.ca/~gary/rtmidi/\n" + "\n" + "Alexander Uckun for decimal input field\n" + "https://www.codeproject.com/Articles/21257/_\n" + "\n" + "\xc3\x9alfur Kolka for application icon, splash and about screen\n" + "https://www.behance.net/ulfurkolka\n" + "\n" + "Nobuyuki for file icon\n" + "https://twitter.com/nobuyukinyuu\n" + "\n" +#endif + "Daniel Collin (emoon/TBL) for providing test infrastructure\n" + "https://twitter.com/daniel_collin\n" + "\n" + "The people at ModPlug forums for crucial contribution\n" + "in the form of ideas, testing and support;\n" + "thanks particularly to:\n" + "33, 8bitbubsy, Anboi, BooT-SectoR-ViruZ, Bvanoudtshoorn\n" + "christofori, cubaxd, Diamond, Ganja, Georg, Goor00,\n" + "Harbinger, jmkz, KrazyKatz, LPChip, Nofold, Rakib, Sam Zen\n" + "Skaven, Skilletaudio, Snu, Squirrel Havoc, Teimoso, Waxhead\n" + "\n" +#ifndef NO_VST + "VST PlugIn Technology by Steinberg Media Technologies GmbH\n" + "\n" +#endif +#ifdef MPT_WITH_ASIO + "ASIO Technology by Steinberg Media Technologies GmbH\n" + "\n" +#endif + ); +} + +mpt::ustring GetLicenseString() +{ + return MPT_UTF8( + "Copyright (c) 2004-2021, OpenMPT contributors" "\n" + "Copyright (c) 1997-2003, Olivier Lapicque" "\n" + "All rights reserved." "\n" + "" "\n" + "Redistribution and use in source and binary forms, with or without" "\n" + "modification, are permitted provided that the following conditions are met:" "\n" + " * Redistributions of source code must retain the above copyright" "\n" + " notice, this list of conditions and the following disclaimer." "\n" + " * Redistributions in binary form must reproduce the above copyright" "\n" + " notice, this list of conditions and the following disclaimer in the" "\n" + " documentation and/or other materials provided with the distribution." "\n" + " * Neither the name of the OpenMPT project nor the" "\n" + " names of its contributors may be used to endorse or promote products" "\n" + " derived from this software without specific prior written permission." "\n" + "" "\n" + "THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY" "\n" + "EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED" "\n" + "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE" "\n" + "DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY" "\n" + "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES" "\n" + "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;" "\n" + "LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND" "\n" + "ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" "\n" + "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS" "\n" + "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." "\n" + ); +} + +} // namespace Build + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/version.h b/Frameworks/OpenMPT.old/OpenMPT/common/version.h new file mode 100644 index 000000000..95e8c5b8a --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/version.h @@ -0,0 +1,311 @@ +/* + * version.h + * --------- + * Purpose: OpenMPT version handling. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + +#include "mptString.h" +#include "FlagSet.h" + +#include + + +OPENMPT_NAMESPACE_BEGIN + + +class Version +{ + +private: + + uint32 m_Version; // e.g. 0x01170208 + +public: + + enum class Field + { + Major, + Minor, + Patch, + Test, + }; + +public: + + static Version Current() noexcept; + +public: + + MPT_CONSTEXPR11_FUN Version() noexcept + : m_Version(0) + {} + + explicit MPT_CONSTEXPR11_FUN Version(uint32 version) noexcept + : m_Version(version) + {} + + explicit MPT_CONSTEXPR11_FUN Version(uint8 v1, uint8 v2, uint8 v3, uint8 v4) noexcept + : m_Version((static_cast(v1) << 24) | (static_cast(v2) << 16) | (static_cast(v3) << 8) | (static_cast(v4) << 0)) + {} + +public: + + mpt::ustring ToUString() const; // e.g "1.17.02.08" + + // Returns numerical version value from given version string. + static Version Parse(const mpt::ustring &s); + +public: + + explicit MPT_CONSTEXPR11_FUN operator bool () const noexcept + { + return m_Version != 0; + } + MPT_CONSTEXPR11_FUN bool operator ! () const noexcept + { + return m_Version == 0; + } + + MPT_CONSTEXPR11_FUN uint32 GetRawVersion() const noexcept + { + return m_Version; + } + + MPT_FORCEINLINE Version Masked(uint32 mask) const noexcept + { + return Version(m_Version & mask); + } + + MPT_CONSTEXPR11_FUN uint8 GetField(Field field) const noexcept + { + return + (field == Field::Major) ? static_cast((m_Version >> 24) & 0xffu) : + (field == Field::Minor) ? static_cast((m_Version >> 16) & 0xffu) : + (field == Field::Patch) ? static_cast((m_Version >> 8) & 0xffu) : + (field == Field::Test ) ? static_cast((m_Version >> 0) & 0xffu) : + 0u; + } + + // Return a version without build number (the last number in the version). + // The current versioning scheme uses this number only for test builds, and it should be 00 for official builds, + // So sometimes it might be wanted to do comparisons without the build number. + Version WithoutTestNumber() const noexcept; + + Version WithoutPatchOrTestNumbers() const noexcept; + +public: + + // Return a OpenMPT version string suitable for file format tags + mpt::ustring GetOpenMPTVersionString() const; // e.g. "OpenMPT 1.17.02.08" + + // Returns true if a given version number is from a test build, false if it's a release build. + bool IsTestVersion() const noexcept; + +public: + + struct LiteralParser + { + + public: + + // Work-around for GCC 5 which complains about instanciating non-literal type inside a constexpr function when using mpt::constexpr_throw(std::runtime_error("")). + struct ParseException {}; + + private: + + static MPT_CONSTEXPR11_FUN uint8 NibbleFromChar(char x) + { + return + ('0' <= x && x <= '9') ? static_cast(x - '0' + 0) : + ('a' <= x && x <= 'z') ? static_cast(x - 'a' + 10) : + ('A' <= x && x <= 'Z') ? static_cast(x - 'A' + 10) : + mpt::constexpr_throw(std::domain_error("")); + } + + public: + + static MPT_CONSTEXPR14_FUN Version Parse(const char * str, std::size_t len) + { + // 0123456789 + // 1.23.45.67 + uint8 v[4] = {0, 0, 0, 0}; + std::size_t field = 0; + std::size_t fieldlen = 0; + for(std::size_t i = 0; i < len; ++i) + { + char c = str[i]; + if(c == '.') + { + if(field >= 3) + { + mpt::constexpr_throw(ParseException()); + } + if(fieldlen == 0) + { + mpt::constexpr_throw(ParseException()); + } + field++; + fieldlen = 0; + } else if(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) + { + fieldlen++; + if(fieldlen > 2) + { + mpt::constexpr_throw(ParseException()); + } + v[field] <<= 4; + v[field] |= NibbleFromChar(c); + } else + { + mpt::constexpr_throw(ParseException()); + } + } + if(fieldlen == 0) + { + mpt::constexpr_throw(ParseException()); + } + return Version(v[0], v[1], v[2], v[3]); + } + + }; + +}; + +MPT_CONSTEXPR11_FUN bool operator == (const Version &a, const Version &b) noexcept +{ + return a.GetRawVersion() == b.GetRawVersion(); +} +MPT_CONSTEXPR11_FUN bool operator != (const Version &a, const Version &b) noexcept +{ + return a.GetRawVersion() != b.GetRawVersion(); +} +MPT_CONSTEXPR11_FUN bool operator <= (const Version &a, const Version &b) noexcept +{ + return a.GetRawVersion() <= b.GetRawVersion(); +} +MPT_CONSTEXPR11_FUN bool operator >= (const Version &a, const Version &b) noexcept +{ + return a.GetRawVersion() >= b.GetRawVersion(); +} +MPT_CONSTEXPR11_FUN bool operator < (const Version &a, const Version &b) noexcept +{ + return a.GetRawVersion() < b.GetRawVersion(); +} +MPT_CONSTEXPR11_FUN bool operator > (const Version &a, const Version &b) noexcept +{ + return a.GetRawVersion() > b.GetRawVersion(); +} + + +MPT_CONSTEXPR14_FUN Version operator "" _LiteralVersionImpl (const char * str, std::size_t len) +{ + return Version::LiteralParser::Parse(str, len); +} + +// Create Version object from version string and check syntax, all at compile time. +// cppcheck false-positive +// cppcheck-suppress preprocessorErrorDirective +#define MPT_V(strver) Version{MPT_FORCE_CONSTEXPR(( strver ## _LiteralVersionImpl ).GetRawVersion())} + + + +class SourceInfo +{ +private: + mpt::ustring m_Url; // svn repository url (or empty string) + int m_Revision; // svn revision (or 0) + bool m_IsDirty; // svn working copy is dirty (or false) + bool m_HasMixedRevisions; // svn working copy has mixed revisions (or false) + bool m_IsPackage; // source code originates from a packaged version of the source code + mpt::ustring m_Date; // svn date (or empty string) +private: + SourceInfo(); +public: + static SourceInfo Current(); +public: + const mpt::ustring & Url() const { return m_Url; } + int Revision() const { return m_Revision; } + bool IsDirty() const { return m_IsDirty; } + bool HasMixedRevisions() const { return m_HasMixedRevisions; } + bool IsPackage() const { return m_IsPackage; } + const mpt::ustring & Date() const { return m_Date; } +public: + mpt::ustring GetUrlWithRevision() const; // i.e. "https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@1234" or empty string + mpt::ustring GetStateString() const; // i.e. "+dirty" or "clean" +}; + + + +namespace Build +{ + + // Returns true if all conditions for an official release build are met + bool IsReleasedBuild(); + + // Return true if this is a debug build with no optimizations + bool IsDebugBuild(); + + // Return a string decribing the time of the build process (if built from a svn working copy and tsvn was available during build, otherwise it returns the time version.cpp was last rebuild which could be unreliable as it does not get rebuild every time without tsvn) + mpt::ustring GetBuildDateString(); + + // Return a string decribing some of the build features + mpt::ustring GetBuildFeaturesString(); // e.g. " NO_VST NO_DSOUND" + + // Return a string describing the compiler version used for building. + mpt::ustring GetBuildCompilerString(); // e.g. "Microsoft Compiler 15.00.20706.01" + + enum Strings + { + StringsNone = 0, + StringVersion = 1<<0, // "1.23.35.45" + StringRevision = 1<<2, // "-r1234+" + StringBitness = 1<<3, // "32 bit" + StringSourceInfo = 1<<4, // "https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@1234 (2016-01-02) +dirty" + StringBuildFlags = 1<<5, // "TEST DEBUG" + StringBuildFeatures = 1<<6, // "NO_VST NO_DSOUND" + }; + MPT_DECLARE_ENUM(Strings) + + // Returns a versions string with the fields selected via @strings. + mpt::ustring GetVersionString(FlagSet strings); + + // Returns a pure version string + mpt::ustring GetVersionStringPure(); // e.g. "1.17.02.08-r1234+ 32 bit" + + // Returns a simple version string + mpt::ustring GetVersionStringSimple(); // e.g. "1.17.02.08-r1234+ TEST" + + // Returns Version::CurrentAsString() if the build is a clean release build straight from the repository or an extended string otherwise (if built from a svn working copy and tsvn was available during build) + mpt::ustring GetVersionStringExtended(); // e.g. "1.17.02.08-r1234+ 32 bit DEBUG" + + enum class Url + { + Website, + Download, + Forum, + Bugtracker, + Updates, + TopPicks, + }; + // Returns a URL for the respective key. + mpt::ustring GetURL(Build::Url key); + + // Returns a multi-line string containing the full credits for the code base + mpt::ustring GetFullCreditsString(); + + // Returns the OpenMPT license text + mpt::ustring GetLicenseString(); + +} //namespace Build + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/common/versionNumber.h b/Frameworks/OpenMPT.old/OpenMPT/common/versionNumber.h new file mode 100644 index 000000000..fbd29f0e0 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/common/versionNumber.h @@ -0,0 +1,23 @@ +/* + * versionNumber.h + * --------------- + * Purpose: OpenMPT version number. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + +OPENMPT_NAMESPACE_BEGIN + +// Version definitions. The only thing that needs to be changed when changing version number. +#define VER_MAJORMAJOR 1 +#define VER_MAJOR 29 +#define VER_MINOR 15 +#define VER_MINORMINOR 00 + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/all_formats.dict b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/all_formats.dict new file mode 100644 index 000000000..9bb862355 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/all_formats.dict @@ -0,0 +1,304 @@ +669="if" +669="JN" + +amf="ASYLUM Music Format V1.0\x00" +amf="AMF\x0A" + +ams="Extreme" +ams="AMShdr\x1A\x02\x02" + +#dbm="DBM0" +dbm="NAME" +dbm="INFO" +dbm="SONG" +dbm="INST" +dbm="VENV" +dbm="PENV" +dbm="PNAM" +dbm="SMPL" +dbm="DSPE" +#dbm="MPEG" + +digi="DIGI Booster module\x00" + +dmf="DDMF" +#dmf="XTRACKER" +dmf="CMSG" +dmf="SEQU" +dmf="SMPI" +dmf="SMPD" +#dmf="SMPJ" +#dmf="ENDE" +#dmf="SETT" + +dsm="RIFF" +dsm="DSMF" + +dtm="D.T." +dtm="S.Q." +#dtm="PATT" +#dtm="INST" +dtm="DAPT" +dtm="DAIT" + +far="FAR\xFE" +far="\x0D\x0A\x1A" + +gdm="GDM\xFE" +gdm="GMFS" + +imf="IM10" +imf="IS10" + +it="IMPM" +it="IMPI" +it="IMPS" +#it="OMPT" +it="PNAM" +it="CNAM" +it="STPM" +it="XTPM" +it="CHBI" +it="FX00" +it="F255" +it="DWRT" +it="PROG" +it="CHFX" + +it="..TD" +it="DTFR" +it=".BPR" +it=".MPR" +it="...C" +it="SnhC" +it="..MT" +it=".MMP" +it=".VWC" +it="VWSL" +it=".APS" +it="VTSV" +it=".VGD" +it="..PR" +it="RSMP" +it="CUES" +it="SWNG" +it=".FSM" +it="AUTH" + +itp=".pti\x03\x01\x00\x00" + +j2b="MUSE\xDE\xAD\xBE\xAF" +j2b="MUSE\xDE\xAD\xBA\xBE" +j2b="AMFF" +j2b="AM " +j2b="MAIN" +j2b="INIT" +j2b="ORDR" +j2b="AI " +j2b="AS " + +MDL="DMDL" +# Most chunk IDs are commented out as they are substrings of other dictionary entries +#mdl="IN" +mdl="ME" +#mdl="PA" +#mdl="TR" +mdl="II" +#mdl="VE" +#mdl="PE" +#mdl="FE" +#mdl="IS" +#mdl="SA" + +med="MMD1" + +mo3="MO3\x05" + +# A couple of magic bytes are commented out because they do not modify the loader's behaviour, apart from setting a "made with" string. +mod="M.K." +#mod="M!K!" +mod="M&K!" +mod="N.T." +#mod="FEST" +#mod="NSMS" +#mod="LARD" +mod="OKTA" +#mod="OCTA" +#mod="CD61" +mod="CD81" +#mod="FA08" +mod="FLT8" +#mod="EXO8" +# Depending on the byte offset in the file, we generate either a "xCHN" or "xxCH" magic +mod="99CHN" +mod="TDZ8" +ice="MTN\x00" +ice="IT10" +pt36="CMNT" +pt36="PTDT" +sfx="SO31" +# External Startrekker instrument files. +stam="ST1.3 ModuleINFO" +stam="AudioSculpture10" + +mptm="->MPT_ORIGINAL_IT<-" +mptm=".tpm" +mptm="mptm" +mptm="\x89\x08" +mptm="\x8D\x08" +# No structural changes in these format versions +#mptm="\x8E\x08" +#mptm="\x8F\x08" +#mptm="\x90\x08" +mptm="\x91\x08" +mptm="228\x04" + +mt2="MT20" +#mt2="MadTracker 2.0" +mt2="BPM+" +mt2="TFXM" +mt2="TRKS" +mt2="TRKL" +mt2="PATN" +mt2="MSG\x00" +#mt2="PICT" +mt2="SUM\x00" +mt2="VST2" + +mtm="MTM\x10" + +okt="OKTASONG" +okt="CMOD" +okt="SAMP" +okt="SPEE" +okt="SLEN" +okt="PLEN" +okt="PATT" +okt="PBOD" +okt="SBOD" + +plm="PLM\x1A" +plm="PLS\x1A" + +psm="PSM " +psm="FILE" +psm="TITL" +psm="SDFT" +psm="DATE" +psm="OPLH" +psm="PPAN" +psm="DSAM" +psm="DSMP" +psm="MAINSONG" +psm="\x00\xFF\x00\x00\x01\x00" +psm16="PSM\xFE" +psm16="PORD" +#psm16="PPAN" +psm16="PSAH" +psm16="PPAT" + +ptm="PTMF" +ptm="\x1A\x03\x02" + +s3m="SCRM" +#s3m="SCRS" +#s3m="SCRI" + +stm="\x1A\x02\x15" + +stp="STP3\x02" + +ult="MAS_UTrack_V004" + +umx="\xC1\x83\x2A\x9E" +umx="music" +umx="sound" + +xm="Extended Module: " +xm="OpenMPT " +#xm="FastTracker v 2.00 " +xm="MilkyTracker " +xm="text" +xm="MIDI" + +it="..OF" +it="LTTP" +it="PTTF" +it="..Fd" +it="..VG" +it="...P" +it="..EV" +it="..EP" +it=".EiP" +it=".SLV" +it=".ELV" +it=".BSV" +it=".ESV" +it=".SLP" +it=".ELP" +it=".BSP" +it=".ESP" +it="SLiP" +it="ELiP" +it="BSiP" +it="ESiP" +it=".ANN" +it=".TCD" +it=".AND" +it="..SP" +it="..SV" +it=".CFI" +it=".RFI" +it="..BM" +it="..PM" +it="..CM" +it=".SPP" +it=".CPP" +it=".[PV" +it=".[PP" +it="[PiP" +it=".[EV" +it=".[EP" +it="[EiP" +it="..[K" +it="..[n" +it=".[MN" +it=".[nf" +it=".PiM" +it="..RV" +it="...R" +it="..SC" +it="..SR" +it="..MF" +it="HEVP" +it="HOVP" +it="NREP" +it="NREA" +it="NREV" +it="GLFP" +it="GLFA" +it="GLFV" +it="DWPM" + +mmcmp="ziRCONia\x0e\x00" + +xpk="XPKF\x00\x10\x00\x00SQSH" + +pp20="PP20" + +plugin_chorus="OMXD\x9C\x62\xE6\xEF" +plugin_compressor="OMXD\x79\x1F\x01\xEF" +plugin_distortion="OMXD\x90\x4C\x11\xEF" +plugin_echo="OMXD\x2C\x93\x3E\xEF" +plugin_flanger="OMXD\x92\x3D\xCA\xEF" +plugin_gargle="OMXD\x10\x82\xFD\xDA" +plugin_i3dl2reverb="OMXD\x71\x5E\x98\xEF" +plugin_parameq="OMXD\x89\xED\x0C\x12" +plugin_wavesreverb="OMDX\x68\x02\xFC\x87" +plugin_lfo="OMPTLFO " +plugin_dbproecho="DBM0Echo" + +midi="MThd\x00\x00\x00\x06\x00\x01\x00\x01\x01\xE0MTrk" + +wave="WAVEfmt " +wave="data" diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/build.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/build.sh new file mode 100755 index 000000000..833397efe --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/build.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +cd "${0%/*}" +cd ../.. +AFL_HARDEN=1 CONFIG=afl make clean all EXAMPLES=0 TEST=0 OPENMPT123=0 NO_VORBIS=1 NO_VORBISFILE=1 NO_MPG123=1 CHECKED_ADDRESS=1 diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz-main.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz-main.sh new file mode 100644 index 000000000..6f67ea491 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz-main.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +cd "${0%/*}" +. ./fuzz-settings.sh + +# Create tmpfs for storing temporary fuzzing data +mkdir $FUZZING_TEMPDIR +sudo mount -t tmpfs -o size=300M none $FUZZING_TEMPDIR +rm -rf $FUZZING_TEMPDIR/bin +mkdir $FUZZING_TEMPDIR/bin +cp -d ../../bin/* $FUZZING_TEMPDIR/bin/ + +#export AFL_PRELOAD=$AFL_DIR/libdislocator.so +LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p exploit -f $FUZZING_TEMPDIR/infile01 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -D -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile01 diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz-secondary1.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz-secondary1.sh new file mode 100644 index 000000000..2d6a867b6 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz-secondary1.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +cd "${0%/*}" +. ./fuzz-settings.sh + +#export AFL_PRELOAD=$AFL_DIR/libdislocator.so +LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p coe -f $FUZZING_TEMPDIR/infile02 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer02 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile02 diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz-secondary2.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz-secondary2.sh new file mode 100644 index 000000000..97b28395a --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz-secondary2.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +cd "${0%/*}" +. ./fuzz-settings.sh + +#export AFL_PRELOAD=$AFL_DIR/libdislocator.so +LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p explore -f $FUZZING_TEMPDIR/infile03 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer03 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile03 diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz-settings.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz-settings.sh new file mode 100755 index 000000000..1b280673e --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz-settings.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# Input data for fuzzer +# If you run the fuzzer for the first time, specify a directory with some input +# files for the fuzzer, e.g. +# FUZZING_INPUT="-i /home/foo/testcases/" +# If you want to continue fuzzing using the previous findings, use: +# FUZZING_INPUT=-i- +FUZZING_INPUT=-i- + +# Directory to place temporary fuzzing data into +FUZZING_TEMPDIR=~/libopenmpt-fuzzing-temp +# Directory to store permanent fuzzing data (e.g. found crashes) into +FUZZING_FINDINGS_DIR=~/libopenmpt-fuzzing +# Fuzzer timeout in ms, + = don't abort on timeout +FUZZING_TIMEOUT=5000+ +# Path to afl-fuzz binary +AFL_DIR=afl diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz.c b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz.c new file mode 100644 index 000000000..653dab6d6 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/fuzz.c @@ -0,0 +1,59 @@ +/* + * fuzz.c + * ------ + * Purpose: Tiny libopenmpt user to be used by fuzzing tools + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define BUFFERSIZE 450 // shouldn't match OpenMPT's internal mix buffer size (512) +#define SAMPLERATE 22050 + +static int16_t buffer[BUFFERSIZE]; + +int main( int argc, char * argv[] ) { + static FILE * file = NULL; + static openmpt_module * mod = NULL; + static size_t count = 0; + static int i = 0; + (void)argc; +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif + file = fopen( argv[1], "rb" ); + mod = openmpt_module_create( openmpt_stream_get_file_callbacks(), file, NULL, NULL, NULL ); + fclose( file ); + if ( mod == NULL ) return 1; + openmpt_module_ctl_set( mod, "render.resampler.emulate_amiga", (openmpt_module_get_num_orders( mod ) & 1) ? "0" : "1" ); + /* render about a second of the module for fuzzing the actual mix routines */ + for(; i < 50; i++) { + count = openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer ); + if ( count == 0 ) { + break; + } + } + openmpt_module_set_position_seconds( mod, 1.0 ); + openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer ); + openmpt_module_set_position_order_row( mod, 3, 16 ); + openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer ); + + /* fuzz string-related stuff */ + openmpt_free_string ( openmpt_module_get_metadata( mod, "date" ) ); + openmpt_free_string ( openmpt_module_get_metadata( mod, "message" ) ); + openmpt_module_destroy( mod ); + return 0; +} diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/get-afl.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/get-afl.sh new file mode 100755 index 000000000..b9973722b --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/get-afl.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +cd "${0%/*}" + +GET_AFL_VERSION?="$(wget --quiet -O - "https://api.github.com/repos/AFLplusplus/AFLplusplus/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')" +AFL_FILENAME="$GET_AFL_VERSION.tar.gz" +AFL_URL="https://github.com/AFLplusplus/AFLplusplus/archive/$AFL_FILENAME" + +rm $AFL_FILENAME +wget $AFL_URL || exit +tar -xzvf $AFL_FILENAME +rm $AFL_FILENAME +cd AFLplusplus-* +make source-only || exit +cd .. +rm -rf afl +mv AFLplusplus-* afl \ No newline at end of file diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/readme.md b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/readme.md new file mode 100644 index 000000000..bb19163ec --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/fuzzing/readme.md @@ -0,0 +1,51 @@ +libopenmpt fuzz suite +===================== + +In this directory, you can find the necessary tools for fuzzing libopenmpt with +the American Fuzzy Lop fuzzer (afl++). + +Contents: + +* `all_formats.dict`: A dictionary containing magic bytes from all supported + module formats to make the life of the fuzzer a bit easier. +* `fuzz-main.sh`: Script to launch the main fuzzing process. If you want to + use just one fuzzer instance, run this one. +* `fuzz-secondary[1|2].sh`: Scripts to launch the secondary fuzzing process. It + is recommended to run at least two fuzzer instances, as the deterministic and + random fuzz mode have been found to complement each other really well. The two + scripts are set up to use different exploration strategies +* `fuzz-settings.sh`: Set up your preferences and afl settings here before the + first run. +* `fuzz.c`: A tiny C program that is used by the fuzzer to test libopenmpt. +* `get-afl.sh`: A simple script to obtain the latest version of afl++. + You can also make it download from a specific branch or tag, e.g. + `GET_AFL_VERSION=stable ./get-afl.sh` to download the latest stable but + unreleased code. + +Prerequisites +============= +* [afl++](https://github.com/AFLplusplus/AFLplusplus) - the makefile expects + this to be installed in `contrib/fuzzing/afl`, as it is automatically done by + the `get-afl.sh` install script. +* Clang with LLVM dev headers (llvm-config needs to be installed). + afl also works with gcc, but our makefile has been set up to make use of afl's + faster LLVM-LTO mode. + +How to use +========== +* Run `get-afl.sh`, or manually extract afl to `contrib/fuzzing/afl`, use + `make source-only` to build. If building fails because `llvm-config` cannot be + found, try prepending `LLVM_CONFIG=/usr/bin/llvm-config-12` or similar, and + read the afl manual. +* Build libopenmpt with the `build.sh` script in this directory. +* Set up `fuzz-settings.sh` to your taste. Most importantly, you will have to + specify the input directory for first use. + The default setup mounts a tmpfs folder for all temporary files. You may + change this behaviour if you do not have root privileges. +* Run `fuzz-main.sh` for the first (deterministic) instance of afl-fuzz. +* For a "secondary" instance to run on another core, run `fuzz-secondary1.sh` + and/or `fuzz-secondary2.sh`. +* If you want to make use of even more cores, create more copies + `fuzz-secondary2.sh` and adjust "infile03" / "fuzzer03" to + "infile04" / "fuzzer04" and so o (they need to be unique). Try variying the + fuzzing strategey (the -p parameter) to get results more quickly. diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE new file mode 100644 index 000000000..ae92400c3 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2004-2021, OpenMPT contributors +Copyright (c) 1997-2003, Olivier Lapicque +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the OpenMPT project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/Makefile.am b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/Makefile.am new file mode 100644 index 000000000..9f4f96516 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/Makefile.am @@ -0,0 +1,46 @@ + +ACLOCAL_AMFLAGS = -I m4 --install +EXTRA_DIST = +EXTRA_DIST += LICENSE +EXTRA_DIST += libopenmpt_modplug.pc.in +EXTRA_DIST += libmodplug.pc.in +EXTRA_DIST += test.sh +MOSTLYCLEANFILES = + +dist_doc_DATA = +dist_doc_DATA += LICENSE +nobase_dist_doc_DATA = + +bin_PROGRAMS = +check_PROGRAMS = +lib_LTLIBRARIES = + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = +nobase_include_HEADERS = + +if ENABLE_LIBOPENMPT_MODPLUG +lib_LTLIBRARIES += libopenmpt_modplug.la +libopenmpt_modplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined +libopenmpt_modplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS) +libopenmpt_modplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS) +libopenmpt_modplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS) +libopenmpt_modplug_la_LIBADD = $(LIBOPENMPT_LIBS) +libopenmpt_modplug_la_SOURCES = +libopenmpt_modplug_la_SOURCES += libopenmpt_modplug.c +libopenmpt_modplug_la_SOURCES += libopenmpt_modplug_cpp.cpp +endif + +if ENABLE_LIBMODPLUG +pkgconfig_DATA += libmodplug.pc +lib_LTLIBRARIES += libmodplug.la +libmodplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined +nobase_include_HEADERS += libmodplug/modplug.h libmodplug/sndfile.h libmodplug/stdafx.h +libmodplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS) +libmodplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS) +libmodplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS) +libmodplug_la_LIBADD = $(LIBOPENMPT_LIBS) +libmodplug_la_SOURCES = +libmodplug_la_SOURCES += libopenmpt_modplug.c +libmodplug_la_SOURCES += libopenmpt_modplug_cpp.cpp +endif diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/autogen.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/autogen.sh new file mode 100755 index 000000000..05ff21447 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/autogen.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +autoreconf -i diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/clean.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/clean.sh new file mode 100755 index 000000000..fac1804fb --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +./autogen.sh +./configure +make distclean diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/config.h.in b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/config.h.in new file mode 100644 index 000000000..b72c3f925 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/config.h.in @@ -0,0 +1,81 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* define if the compiler supports basic C++11 syntax */ +#undef HAVE_CXX11 + +/* define if the compiler supports basic C++14 syntax */ +#undef HAVE_CXX14 + +/* define if the compiler supports basic C++17 syntax */ +#undef HAVE_CXX17 + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/configure.ac b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/configure.ac new file mode 100644 index 000000000..fd543eca3 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/configure.ac @@ -0,0 +1,66 @@ +AC_INIT([libopenmpt-modplug], [0.8.8.5-openmpt1], [https://bugs.openmpt.org/], [libopenmpt-modplug], [https://lib.openmpt.org/]) +AC_PREREQ([2.68]) + +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_FILES([Makefile libopenmpt_modplug.pc libmodplug.pc]) + +AM_INIT_AUTOMAKE([1.11 -Wall -Werror foreign subdir-objects]) + +AM_PROG_AR + +LT_INIT + +AC_SYS_LARGEFILE + +PKG_PROG_PKG_CONFIG([0.24]) +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CXX +AC_PROG_INSTALL + +LIBOPENMPT_REQUIRES_PRIVATE= +LIBOPENMPT_LIBS_PRIVATE= + +# We want a modern C compiler +AC_PROG_CC_STDC +#AC_PROG_CC_C99 + +# We need C++11 support +AX_CXX_COMPILE_STDCXX(17, [noext], [optional]) +AS_IF([test "x$HAVE_CXX17" != "x1"], + [ + AX_CXX_COMPILE_STDCXX(14, [noext], [optional]) + AS_IF([test "x$HAVE_CXX14" != "x1"], + [ + AX_CXX_COMPILE_STDCXX(11, [noext], [mandatory]) + ],[] + ) + ],[] +) + +AC_LANG_PUSH([C]) +AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CFLAGS="$CFLAGS -fvisibility=hidden"]) +AX_CFLAGS_WARN_ALL +AC_LANG_POP([C]) + +AC_LANG_PUSH([C++]) +AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CXXFLAGS="$CXXFLAGS -fvisibility=hidden"]) +AX_CXXFLAGS_WARN_ALL +AC_LANG_POP([C++]) + +PKG_CHECK_MODULES([LIBOPENMPT], [libopenmpt]) + +# libmodplug emulation +AC_ARG_ENABLE([libopenmpt_modplug], AS_HELP_STRING([--disable-libopenmpt_modplug], [Disable the libopenmpt_modplug emulation library of the libmodplug interface.])) +AM_CONDITIONAL([ENABLE_LIBOPENMPT_MODPLUG], [test "x$enable_libopenmpt_modplug" != "xno"]) + +# libmodplug replacement +AC_ARG_ENABLE([libmodplug], AS_HELP_STRING([--enable-libmodplug], [Enable libmodplug replacement library based on libopenmpt. +WARNING: This will replace your current libmodplug installation. +CAUTION: The emulation of the libmodplug interface is not complete as libmodplug exposes lots of internal implementation details. If any of those is used by an application, the emulation via libopenmpt will fail and/or crash. +])) +AM_CONDITIONAL([ENABLE_LIBMODPLUG], [test "x$enable_libmodplug" = "xyes"]) + +AC_OUTPUT diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/dist.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/dist.sh new file mode 100755 index 000000000..e96826f07 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/dist.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e + +./test.sh + +./autogen.sh +./configure +make dist diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug.pc.in b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug.pc.in new file mode 100644 index 000000000..e8c796d1d --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=${prefix}/include + +Name: libmodplug +Description: The ModPlug mod file playing library (emulated via libopenmpt). +Version: @PACKAGE_VERSION@ +Requires.private: libopenmpt +Libs: -L${libdir} -lmodplug +Libs.private: +Cflags: -I${includedir} + diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/modplug.h b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/modplug.h new file mode 100644 index 000000000..8136fa9b1 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/modplug.h @@ -0,0 +1,186 @@ +/* + * This source code is public domain. + * + * Authors: Kenton Varda (C interface wrapper) + */ + +#ifndef MODPLUG_MODPLUG_H +#define MODPLUG_MODPLUG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +# if defined(MODPLUG_BUILD) && defined(DLL_EXPORT) /* building libmodplug as a dll for windows */ +# define MODPLUG_EXPORT __declspec(dllexport) +# elif defined(MODPLUG_BUILD) || defined(MODPLUG_STATIC) /* building or using static libmodplug for windows */ +# define MODPLUG_EXPORT +# else +# define MODPLUG_EXPORT __declspec(dllimport) /* using libmodplug dll for windows */ +# endif +/* FIXME: USE VISIBILITY ATTRIBUTES HERE */ +#elif defined(MODPLUG_BUILD) +#define MODPLUG_EXPORT +#else +#define MODPLUG_EXPORT +#endif + +struct _ModPlugFile; +typedef struct _ModPlugFile ModPlugFile; + +struct _ModPlugNote { + unsigned char Note; + unsigned char Instrument; + unsigned char VolumeEffect; + unsigned char Effect; + unsigned char Volume; + unsigned char Parameter; +}; +typedef struct _ModPlugNote ModPlugNote; + +typedef void (*ModPlugMixerProc)(int*, unsigned long, unsigned long); + +/* Load a mod file. [data] should point to a block of memory containing the complete + * file, and [size] should be the size of that block. + * Return the loaded mod file on success, or NULL on failure. */ +MODPLUG_EXPORT ModPlugFile* ModPlug_Load(const void* data, int size); +/* Unload a mod file. */ +MODPLUG_EXPORT void ModPlug_Unload(ModPlugFile* file); + +/* Read sample data into the buffer. Returns the number of bytes read. If the end + * of the mod has been reached, zero is returned. */ +MODPLUG_EXPORT int ModPlug_Read(ModPlugFile* file, void* buffer, int size); + +/* Get the name of the mod. The returned buffer is stored within the ModPlugFile + * structure and will remain valid until you unload the file. */ +MODPLUG_EXPORT const char* ModPlug_GetName(ModPlugFile* file); + +/* Get the length of the mod, in milliseconds. Note that this result is not always + * accurate, especially in the case of mods with loops. */ +MODPLUG_EXPORT int ModPlug_GetLength(ModPlugFile* file); + +/* Seek to a particular position in the song. Note that seeking and MODs don't mix very + * well. Some mods will be missing instruments for a short time after a seek, as ModPlug + * does not scan the sequence backwards to find out which instruments were supposed to be + * playing at that time. (Doing so would be difficult and not very reliable.) Also, + * note that seeking is not very exact in some mods -- especially those for which + * ModPlug_GetLength() does not report the full length. */ +MODPLUG_EXPORT void ModPlug_Seek(ModPlugFile* file, int millisecond); + +enum _ModPlug_Flags +{ + MODPLUG_ENABLE_OVERSAMPLING = 1 << 0, /* Enable oversampling (*highly* recommended) */ + MODPLUG_ENABLE_NOISE_REDUCTION = 1 << 1, /* Enable noise reduction */ + MODPLUG_ENABLE_REVERB = 1 << 2, /* Enable reverb */ + MODPLUG_ENABLE_MEGABASS = 1 << 3, /* Enable megabass */ + MODPLUG_ENABLE_SURROUND = 1 << 4 /* Enable surround sound. */ +}; + +enum _ModPlug_ResamplingMode +{ + MODPLUG_RESAMPLE_NEAREST = 0, /* No interpolation (very fast, extremely bad sound quality) */ + MODPLUG_RESAMPLE_LINEAR = 1, /* Linear interpolation (fast, good quality) */ + MODPLUG_RESAMPLE_SPLINE = 2, /* Cubic spline interpolation (high quality) */ + MODPLUG_RESAMPLE_FIR = 3 /* 8-tap fir filter (extremely high quality) */ +}; + +typedef struct _ModPlug_Settings +{ + int mFlags; /* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed */ + + /* Note that ModPlug always decodes sound at 44100kHz, 32 bit, stereo and then + * down-mixes to the settings you choose. */ + int mChannels; /* Number of channels - 1 for mono or 2 for stereo */ + int mBits; /* Bits per sample - 8, 16, or 32 */ + int mFrequency; /* Sampling rate - 11025, 22050, or 44100 */ + int mResamplingMode; /* One of MODPLUG_RESAMPLE_*, above */ + + int mStereoSeparation; /* Stereo separation, 1 - 256 */ + int mMaxMixChannels; /* Maximum number of mixing channels (polyphony), 32 - 256 */ + + int mReverbDepth; /* Reverb level 0(quiet)-100(loud) */ + int mReverbDelay; /* Reverb delay in ms, usually 40-200ms */ + int mBassAmount; /* XBass level 0(quiet)-100(loud) */ + int mBassRange; /* XBass cutoff in Hz 10-100 */ + int mSurroundDepth; /* Surround level 0(quiet)-100(heavy) */ + int mSurroundDelay; /* Surround delay in ms, usually 5-40ms */ + int mLoopCount; /* Number of times to loop. Zero prevents looping. + -1 loops forever. */ +} ModPlug_Settings; + +/* Get and set the mod decoder settings. All options, except for channels, bits-per-sample, + * sampling rate, and loop count, will take effect immediately. Those options which don't + * take effect immediately will take effect the next time you load a mod. */ +MODPLUG_EXPORT void ModPlug_GetSettings(ModPlug_Settings* settings); +MODPLUG_EXPORT void ModPlug_SetSettings(const ModPlug_Settings* settings); + +/* New ModPlug API Functions */ +/* NOTE: Master Volume (1-512) */ +MODPLUG_EXPORT unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ; +MODPLUG_EXPORT void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ; + +MODPLUG_EXPORT int ModPlug_GetCurrentSpeed(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentTempo(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentOrder(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentPattern(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentRow(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetPlayingChannels(ModPlugFile* file); + +MODPLUG_EXPORT void ModPlug_SeekOrder(ModPlugFile* file,int order); +MODPLUG_EXPORT int ModPlug_GetModuleType(ModPlugFile* file); +MODPLUG_EXPORT char* ModPlug_GetMessage(ModPlugFile* file); + + +#ifndef MODPLUG_NO_FILESAVE +/* + * EXPERIMENTAL Export Functions + */ +/*Export to a Scream Tracker 3 S3M module. EXPERIMENTAL (only works on Little-Endian platforms)*/ +MODPLUG_EXPORT char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath); + +/*Export to a Extended Module (XM). EXPERIMENTAL (only works on Little-Endian platforms)*/ +MODPLUG_EXPORT char ModPlug_ExportXM(ModPlugFile* file, const char* filepath); + +/*Export to a Amiga MOD file. EXPERIMENTAL.*/ +MODPLUG_EXPORT char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath); + +/*Export to a Impulse Tracker IT file. Should work OK in Little-Endian & Big-Endian platforms :-) */ +MODPLUG_EXPORT char ModPlug_ExportIT(ModPlugFile* file, const char* filepath); +#endif // MODPLUG_NO_FILESAVE + +MODPLUG_EXPORT unsigned int ModPlug_NumInstruments(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_NumSamples(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_NumPatterns(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_NumChannels(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff); +MODPLUG_EXPORT unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff); + +/* + * Retrieve pattern note-data + */ +MODPLUG_EXPORT ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows); + +/* + * ================= + * Mixer callback + * ================= + * + * Use this callback if you want to 'modify' the mixed data of LibModPlug. + * + * void proc(int* buffer,unsigned long channels,unsigned long nsamples) ; + * + * 'buffer': A buffer of mixed samples + * 'channels': N. of channels in the buffer + * 'nsamples': N. of samples in the buffeer (without taking care of n.channels) + * + * (Samples are signed 32-bit integers) + */ +MODPLUG_EXPORT void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ; +MODPLUG_EXPORT void ModPlug_UnloadMixerCallback(ModPlugFile* file) ; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/sndfile.h b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/sndfile.h new file mode 100644 index 000000000..0273191f3 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/sndfile.h @@ -0,0 +1,1017 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque , + * Adam Goode (endian and char fixes for PPC) +*/ + +#if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED) +#include "config.h" +#define CONFIG_H_INCLUDED 1 +#endif + +#ifndef MODPLUG_SNDFILE_H +#define MODPLUG_SNDFILE_H + +#ifdef UNDER_CE +int _strnicmp(const char *str1,const char *str2, int n); +#endif + +#ifndef LPCBYTE +typedef const BYTE * LPCBYTE; +#endif + +#define MOD_AMIGAC2 0x1AB +#define MAX_SAMPLE_LENGTH 16000000 +#define MAX_SAMPLE_RATE 192000 +#define MAX_ORDERS 256 +#define MAX_PATTERNS 240 +#define MAX_SAMPLES 240 +#define MAX_INSTRUMENTS MAX_SAMPLES +#ifdef MODPLUG_FASTSOUNDLIB +#define MAX_CHANNELS 80 +#else +#define MAX_CHANNELS 128 +#endif +#define MAX_BASECHANNELS 64 +#define MAX_ENVPOINTS 32 +#define MIN_PERIOD 0x0020 +#define MAX_PERIOD 0xFFFF +#define MAX_PATTERNNAME 32 +#define MAX_CHANNELNAME 20 +#define MAX_INFONAME 80 +#define MAX_EQ_BANDS 6 +#define MAX_MIXPLUGINS 8 + + +#define MOD_TYPE_NONE 0x00 +#define MOD_TYPE_MOD 0x01 +#define MOD_TYPE_S3M 0x02 +#define MOD_TYPE_XM 0x04 +#define MOD_TYPE_MED 0x08 +#define MOD_TYPE_MTM 0x10 +#define MOD_TYPE_IT 0x20 +#define MOD_TYPE_669 0x40 +#define MOD_TYPE_ULT 0x80 +#define MOD_TYPE_STM 0x100 +#define MOD_TYPE_FAR 0x200 +#define MOD_TYPE_WAV 0x400 +#define MOD_TYPE_AMF 0x800 +#define MOD_TYPE_AMS 0x1000 +#define MOD_TYPE_DSM 0x2000 +#define MOD_TYPE_MDL 0x4000 +#define MOD_TYPE_OKT 0x8000 +#define MOD_TYPE_MID 0x10000 +#define MOD_TYPE_DMF 0x20000 +#define MOD_TYPE_PTM 0x40000 +#define MOD_TYPE_DBM 0x80000 +#define MOD_TYPE_MT2 0x100000 +#define MOD_TYPE_AMF0 0x200000 +#define MOD_TYPE_PSM 0x400000 +#define MOD_TYPE_J2B 0x800000 +#define MOD_TYPE_ABC 0x1000000 +#define MOD_TYPE_PAT 0x2000000 +#define MOD_TYPE_UMX 0x80000000 // Fake type +#define MAX_MODTYPE 24 + + + +// Channel flags: +// Bits 0-7: Sample Flags +#define CHN_16BIT 0x01 +#define CHN_LOOP 0x02 +#define CHN_PINGPONGLOOP 0x04 +#define CHN_SUSTAINLOOP 0x08 +#define CHN_PINGPONGSUSTAIN 0x10 +#define CHN_PANNING 0x20 +#define CHN_STEREO 0x40 +#define CHN_PINGPONGFLAG 0x80 +// Bits 8-31: Channel Flags +#define CHN_MUTE 0x100 +#define CHN_KEYOFF 0x200 +#define CHN_NOTEFADE 0x400 +#define CHN_SURROUND 0x800 +#define CHN_NOIDO 0x1000 +#define CHN_HQSRC 0x2000 +#define CHN_FILTER 0x4000 +#define CHN_VOLUMERAMP 0x8000 +#define CHN_VIBRATO 0x10000 +#define CHN_TREMOLO 0x20000 +#define CHN_PANBRELLO 0x40000 +#define CHN_PORTAMENTO 0x80000 +#define CHN_GLISSANDO 0x100000 +#define CHN_VOLENV 0x200000 +#define CHN_PANENV 0x400000 +#define CHN_PITCHENV 0x800000 +#define CHN_FASTVOLRAMP 0x1000000 +#define CHN_EXTRALOUD 0x2000000 +#define CHN_REVERB 0x4000000 +#define CHN_NOREVERB 0x8000000 + + +#define ENV_VOLUME 0x0001 +#define ENV_VOLSUSTAIN 0x0002 +#define ENV_VOLLOOP 0x0004 +#define ENV_PANNING 0x0008 +#define ENV_PANSUSTAIN 0x0010 +#define ENV_PANLOOP 0x0020 +#define ENV_PITCH 0x0040 +#define ENV_PITCHSUSTAIN 0x0080 +#define ENV_PITCHLOOP 0x0100 +#define ENV_SETPANNING 0x0200 +#define ENV_FILTER 0x0400 +#define ENV_VOLCARRY 0x0800 +#define ENV_PANCARRY 0x1000 +#define ENV_PITCHCARRY 0x2000 + +#define CMD_NONE 0 +#define CMD_ARPEGGIO 1 +#define CMD_PORTAMENTOUP 2 +#define CMD_PORTAMENTODOWN 3 +#define CMD_TONEPORTAMENTO 4 +#define CMD_VIBRATO 5 +#define CMD_TONEPORTAVOL 6 +#define CMD_VIBRATOVOL 7 +#define CMD_TREMOLO 8 +#define CMD_PANNING8 9 +#define CMD_OFFSET 10 +#define CMD_VOLUMESLIDE 11 +#define CMD_POSITIONJUMP 12 +#define CMD_VOLUME 13 +#define CMD_PATTERNBREAK 14 +#define CMD_RETRIG 15 +#define CMD_SPEED 16 +#define CMD_TEMPO 17 +#define CMD_TREMOR 18 +#define CMD_MODCMDEX 19 +#define CMD_S3MCMDEX 20 +#define CMD_CHANNELVOLUME 21 +#define CMD_CHANNELVOLSLIDE 22 +#define CMD_GLOBALVOLUME 23 +#define CMD_GLOBALVOLSLIDE 24 +#define CMD_KEYOFF 25 +#define CMD_FINEVIBRATO 26 +#define CMD_PANBRELLO 27 +#define CMD_XFINEPORTAUPDOWN 28 +#define CMD_PANNINGSLIDE 29 +#define CMD_SETENVPOSITION 30 +#define CMD_MIDI 31 + + +// Volume Column commands +#define VOLCMD_VOLUME 1 +#define VOLCMD_PANNING 2 +#define VOLCMD_VOLSLIDEUP 3 +#define VOLCMD_VOLSLIDEDOWN 4 +#define VOLCMD_FINEVOLUP 5 +#define VOLCMD_FINEVOLDOWN 6 +#define VOLCMD_VIBRATOSPEED 7 +#define VOLCMD_VIBRATO 8 +#define VOLCMD_PANSLIDELEFT 9 +#define VOLCMD_PANSLIDERIGHT 10 +#define VOLCMD_TONEPORTAMENTO 11 +#define VOLCMD_PORTAUP 12 +#define VOLCMD_PORTADOWN 13 + +#define RSF_16BIT 0x04 +#define RSF_STEREO 0x08 + +#define RS_PCM8S 0 // 8-bit signed +#define RS_PCM8U 1 // 8-bit unsigned +#define RS_PCM8D 2 // 8-bit delta values +#define RS_ADPCM4 3 // 4-bit ADPCM-packed +#define RS_PCM16D 4 // 16-bit delta values +#define RS_PCM16S 5 // 16-bit signed +#define RS_PCM16U 6 // 16-bit unsigned +#define RS_PCM16M 7 // 16-bit motorola order +#define RS_STPCM8S (RS_PCM8S|RSF_STEREO) // stereo 8-bit signed +#define RS_STPCM8U (RS_PCM8U|RSF_STEREO) // stereo 8-bit unsigned +#define RS_STPCM8D (RS_PCM8D|RSF_STEREO) // stereo 8-bit delta values +#define RS_STPCM16S (RS_PCM16S|RSF_STEREO) // stereo 16-bit signed +#define RS_STPCM16U (RS_PCM16U|RSF_STEREO) // stereo 16-bit unsigned +#define RS_STPCM16D (RS_PCM16D|RSF_STEREO) // stereo 16-bit delta values +#define RS_STPCM16M (RS_PCM16M|RSF_STEREO) // stereo 16-bit signed big endian +// IT 2.14 compressed samples +#define RS_IT2148 0x10 +#define RS_IT21416 0x14 +#define RS_IT2158 0x12 +#define RS_IT21516 0x16 +// AMS Packed Samples +#define RS_AMS8 0x11 +#define RS_AMS16 0x15 +// DMF Huffman compression +#define RS_DMF8 0x13 +#define RS_DMF16 0x17 +// MDL Huffman compression +#define RS_MDL8 0x20 +#define RS_MDL16 0x24 +#define RS_PTM8DTO16 0x25 +// Stereo Interleaved Samples +#define RS_STIPCM8S (RS_PCM8S|0x40|RSF_STEREO) // stereo 8-bit signed +#define RS_STIPCM8U (RS_PCM8U|0x40|RSF_STEREO) // stereo 8-bit unsigned +#define RS_STIPCM16S (RS_PCM16S|0x40|RSF_STEREO) // stereo 16-bit signed +#define RS_STIPCM16U (RS_PCM16U|0x40|RSF_STEREO) // stereo 16-bit unsigned +#define RS_STIPCM16M (RS_PCM16M|0x40|RSF_STEREO) // stereo 16-bit signed big endian +// 24-bit signed +#define RS_PCM24S (RS_PCM16S|0x80) // mono 24-bit signed +#define RS_STIPCM24S (RS_PCM16S|0x80|RSF_STEREO) // stereo 24-bit signed +#define RS_PCM32S (RS_PCM16S|0xC0) // mono 24-bit signed +#define RS_STIPCM32S (RS_PCM16S|0xC0|RSF_STEREO) // stereo 24-bit signed + +// NNA types +#define NNA_NOTECUT 0 +#define NNA_CONTINUE 1 +#define NNA_NOTEOFF 2 +#define NNA_NOTEFADE 3 + +// DCT types +#define DCT_NONE 0 +#define DCT_NOTE 1 +#define DCT_SAMPLE 2 +#define DCT_INSTRUMENT 3 + +// DNA types +#define DNA_NOTECUT 0 +#define DNA_NOTEOFF 1 +#define DNA_NOTEFADE 2 + +// Mixer Hardware-Dependent features +#define SYSMIX_ENABLEMMX 0x01 +#define SYSMIX_WINDOWSNT 0x02 +#define SYSMIX_SLOWCPU 0x04 +#define SYSMIX_FASTCPU 0x08 + +// Module flags +#define SONG_EMBEDMIDICFG 0x0001 +#define SONG_FASTVOLSLIDES 0x0002 +#define SONG_ITOLDEFFECTS 0x0004 +#define SONG_ITCOMPATMODE 0x0008 +#define SONG_LINEARSLIDES 0x0010 +#define SONG_PATTERNLOOP 0x0020 +#define SONG_STEP 0x0040 +#define SONG_PAUSED 0x0080 +#define SONG_FADINGSONG 0x0100 +#define SONG_ENDREACHED 0x0200 +#define SONG_GLOBALFADE 0x0400 +#define SONG_CPUVERYHIGH 0x0800 +#define SONG_FIRSTTICK 0x1000 +#define SONG_MPTFILTERMODE 0x2000 +#define SONG_SURROUNDPAN 0x4000 +#define SONG_EXFILTERRANGE 0x8000 +#define SONG_AMIGALIMITS 0x10000 + +// Global Options (Renderer) +#define SNDMIX_REVERSESTEREO 0x0001 +#define SNDMIX_NOISEREDUCTION 0x0002 +#define SNDMIX_AGC 0x0004 +#define SNDMIX_NORESAMPLING 0x0008 +#define SNDMIX_HQRESAMPLER 0x0010 +#define SNDMIX_MEGABASS 0x0020 +#define SNDMIX_SURROUND 0x0040 +#define SNDMIX_REVERB 0x0080 +#define SNDMIX_EQ 0x0100 +#define SNDMIX_SOFTPANNING 0x0200 +#define SNDMIX_ULTRAHQSRCMODE 0x0400 +// Misc Flags (can safely be turned on or off) +#define SNDMIX_DIRECTTODISK 0x10000 +#define SNDMIX_ENABLEMMX 0x20000 +#define SNDMIX_NOBACKWARDJUMPS 0x40000 +#define SNDMIX_MAXDEFAULTPAN 0x80000 // Used by the MOD loader + + +// Reverb Types (GM2 Presets) +enum { + REVERBTYPE_SMALLROOM, + REVERBTYPE_MEDIUMROOM, + REVERBTYPE_LARGEROOM, + REVERBTYPE_SMALLHALL, + REVERBTYPE_MEDIUMHALL, + REVERBTYPE_LARGEHALL, + NUM_REVERBTYPES +}; + + +enum { + SRCMODE_NEAREST, + SRCMODE_LINEAR, + SRCMODE_SPLINE, + SRCMODE_POLYPHASE, + NUM_SRC_MODES +}; + + +// Sample Struct +typedef struct _MODINSTRUMENT +{ + UINT nLength,nLoopStart,nLoopEnd; + UINT nSustainStart, nSustainEnd; + signed char *pSample; + UINT nC4Speed; + WORD nPan; + WORD nVolume; + WORD nGlobalVol; + WORD uFlags; + signed char RelativeTone; + signed char nFineTune; + BYTE nVibType; + BYTE nVibSweep; + BYTE nVibDepth; + BYTE nVibRate; + CHAR name[22]; +} MODINSTRUMENT; + + +// Instrument Struct +typedef struct _INSTRUMENTHEADER +{ + UINT nFadeOut; + DWORD dwFlags; + WORD nGlobalVol; + WORD nPan; + WORD VolPoints[MAX_ENVPOINTS]; + WORD PanPoints[MAX_ENVPOINTS]; + WORD PitchPoints[MAX_ENVPOINTS]; + BYTE VolEnv[MAX_ENVPOINTS]; + BYTE PanEnv[MAX_ENVPOINTS]; + BYTE PitchEnv[MAX_ENVPOINTS]; + BYTE Keyboard[128]; + BYTE NoteMap[128]; + + BYTE nVolEnv; + BYTE nPanEnv; + BYTE nPitchEnv; + BYTE nVolLoopStart; + BYTE nVolLoopEnd; + BYTE nVolSustainBegin; + BYTE nVolSustainEnd; + BYTE nPanLoopStart; + BYTE nPanLoopEnd; + BYTE nPanSustainBegin; + BYTE nPanSustainEnd; + BYTE nPitchLoopStart; + BYTE nPitchLoopEnd; + BYTE nPitchSustainBegin; + BYTE nPitchSustainEnd; + BYTE nNNA; + BYTE nDCT; + BYTE nDNA; + BYTE nPanSwing; + BYTE nVolSwing; + BYTE nIFC; + BYTE nIFR; + WORD wMidiBank; + BYTE nMidiProgram; + BYTE nMidiChannel; + BYTE nMidiDrumKey; + signed char nPPS; + unsigned char nPPC; + CHAR name[32]; + CHAR filename[12]; +} INSTRUMENTHEADER; + + +// Channel Struct +typedef struct _MODCHANNEL +{ + // First 32-bytes: Most used mixing information: don't change it + signed char * pCurrentSample; + DWORD nPos; + DWORD nPosLo; // actually 16-bit + LONG nInc; // 16.16 + LONG nRightVol; + LONG nLeftVol; + LONG nRightRamp; + LONG nLeftRamp; + // 2nd cache line + DWORD nLength; + DWORD dwFlags; + DWORD nLoopStart; + DWORD nLoopEnd; + LONG nRampRightVol; + LONG nRampLeftVol; + LONG nFilter_Y1, nFilter_Y2, nFilter_Y3, nFilter_Y4; + LONG nFilter_A0, nFilter_B0, nFilter_B1; + LONG nROfs, nLOfs; + LONG nRampLength; + // Information not used in the mixer + signed char * pSample; + LONG nNewRightVol, nNewLeftVol; + LONG nRealVolume, nRealPan; + LONG nVolume, nPan, nFadeOutVol; + LONG nPeriod, nC4Speed, nPortamentoDest; + INSTRUMENTHEADER *pHeader; + MODINSTRUMENT *pInstrument; + DWORD nVolEnvPosition, nPanEnvPosition, nPitchEnvPosition; + DWORD nMasterChn, nVUMeter; + LONG nGlobalVol, nInsVol; + LONG nFineTune, nTranspose; + LONG nPortamentoSlide, nAutoVibDepth; + UINT nAutoVibPos, nVibratoPos, nTremoloPos, nPanbrelloPos; + // 16-bit members + signed short nVolSwing, nPanSwing; + // 8-bit members + BYTE nNote, nNNA; + BYTE nNewNote, nNewIns, nCommand, nArpeggio; + BYTE nOldVolumeSlide, nOldFineVolUpDown; + BYTE nOldPortaUpDown, nOldFinePortaUpDown; + BYTE nOldPanSlide, nOldChnVolSlide; + BYTE nVibratoType, nVibratoSpeed, nVibratoDepth; + BYTE nTremoloType, nTremoloSpeed, nTremoloDepth; + BYTE nPanbrelloType, nPanbrelloSpeed, nPanbrelloDepth; + BYTE nOldCmdEx, nOldVolParam, nOldTempo; + BYTE nOldOffset, nOldHiOffset; + BYTE nCutOff, nResonance; + BYTE nRetrigCount, nRetrigParam; + BYTE nTremorCount, nTremorParam; + BYTE nPatternLoop, nPatternLoopCount; + BYTE nRowNote, nRowInstr; + BYTE nRowVolCmd, nRowVolume; + BYTE nRowCommand, nRowParam; + BYTE nLeftVU, nRightVU; + BYTE nActiveMacro, nPadding; +} MODCHANNEL; + + +typedef struct _MODCHANNELSETTINGS +{ + UINT nPan; + UINT nVolume; + DWORD dwFlags; + UINT nMixPlugin; + char szName[MAX_CHANNELNAME]; // changed from CHAR +} MODCHANNELSETTINGS; + + +typedef struct _MODCOMMAND +{ + BYTE note; + BYTE instr; + BYTE volcmd; + BYTE command; + BYTE vol; + BYTE param; +} MODCOMMAND, *LPMODCOMMAND; + +//////////////////////////////////////////////////////////////////// +// Mix Plugins +#define MIXPLUG_MIXREADY 0x01 // Set when cleared + +class MODPLUG_EXPORT IMixPlugin +{ +public: + virtual ~IMixPlugin(); + virtual int AddRef() = 0; + virtual int Release() = 0; + virtual void SaveAllParameters() = 0; + virtual void RestoreAllParameters() = 0; + virtual void Process(float *pOutL, float *pOutR, unsigned long nSamples) = 0; + virtual void Init(unsigned long nFreq, int bReset) = 0; + virtual void MidiSend(DWORD dwMidiCode) = 0; + virtual void MidiCommand(UINT nMidiCh, UINT nMidiProg, UINT note, UINT vol) = 0; +}; + + +#define MIXPLUG_INPUTF_MASTEREFFECT 0x01 // Apply to master mix +#define MIXPLUG_INPUTF_BYPASS 0x02 // Bypass effect +#define MIXPLUG_INPUTF_WETMIX 0x04 // Wet Mix (dry added) + +typedef struct _SNDMIXPLUGINSTATE +{ + DWORD dwFlags; // MIXPLUG_XXXX + LONG nVolDecayL, nVolDecayR; // Buffer click removal + int *pMixBuffer; // Stereo effect send buffer + float *pOutBufferL; // Temp storage for int -> float conversion + float *pOutBufferR; +} SNDMIXPLUGINSTATE, *PSNDMIXPLUGINSTATE; + +typedef struct _SNDMIXPLUGININFO +{ + DWORD dwPluginId1; + DWORD dwPluginId2; + DWORD dwInputRouting; // MIXPLUG_INPUTF_XXXX + DWORD dwOutputRouting; // 0=mix 0x80+=fx + DWORD dwReserved[4]; // Reserved for routing info + CHAR szName[32]; + CHAR szLibraryName[64]; // original DLL name +} SNDMIXPLUGININFO, *PSNDMIXPLUGININFO; // Size should be 128 + +typedef struct _SNDMIXPLUGIN +{ + IMixPlugin *pMixPlugin; + PSNDMIXPLUGINSTATE pMixState; + ULONG nPluginDataSize; + PVOID pPluginData; + SNDMIXPLUGININFO Info; +} SNDMIXPLUGIN, *PSNDMIXPLUGIN; + +typedef BOOL (*PMIXPLUGINCREATEPROC)(PSNDMIXPLUGIN); + +//////////////////////////////////////////////////////////////////// + +enum { + MIDIOUT_START=0, + MIDIOUT_STOP, + MIDIOUT_TICK, + MIDIOUT_NOTEON, + MIDIOUT_NOTEOFF, + MIDIOUT_VOLUME, + MIDIOUT_PAN, + MIDIOUT_BANKSEL, + MIDIOUT_PROGRAM, +}; + + +typedef struct MODMIDICFG +{ + char szMidiGlb[9*32]; // changed from CHAR + char szMidiSFXExt[16*32]; // changed from CHAR + char szMidiZXXExt[128*32]; // changed from CHAR +} MODMIDICFG, *LPMODMIDICFG; + +#define NOTE_MAX 120 //Defines maximum notevalue as well as maximum number of notes. + +typedef VOID (* LPSNDMIXHOOKPROC)(int *, unsigned long, unsigned long); // buffer, samples, channels + + + +//============== +class MODPLUG_EXPORT CSoundFile +//============== +{ +public: // Static Members + static UINT m_nXBassDepth, m_nXBassRange; + static UINT m_nReverbDepth, m_nReverbDelay, gnReverbType; + static UINT m_nProLogicDepth, m_nProLogicDelay; + static UINT m_nStereoSeparation; + static UINT m_nMaxMixChannels; + static LONG m_nStreamVolume; + static DWORD gdwSysInfo, gdwSoundSetup, gdwMixingFreq, gnBitsPerSample, gnChannels; + static UINT gnAGC, gnVolumeRampSamples, gnVUMeter, gnCPUUsage; + static LPSNDMIXHOOKPROC gpSndMixHook; + static PMIXPLUGINCREATEPROC gpMixPluginCreateProc; + +public: // for Editing + MODCHANNEL Chn[MAX_CHANNELS]; // Channels + UINT ChnMix[MAX_CHANNELS]; // Channels to be mixed + MODINSTRUMENT Ins[MAX_SAMPLES]; // Instruments + INSTRUMENTHEADER *Headers[MAX_INSTRUMENTS]; // Instrument Headers + MODCHANNELSETTINGS ChnSettings[MAX_BASECHANNELS]; // Channels settings + MODCOMMAND *Patterns[MAX_PATTERNS]; // Patterns + WORD PatternSize[MAX_PATTERNS]; // Patterns Lengths + BYTE Order[MAX_ORDERS]; // Pattern Orders + MODMIDICFG m_MidiCfg; // Midi macro config table + SNDMIXPLUGIN m_MixPlugins[MAX_MIXPLUGINS]; // Mix plugins + UINT m_nDefaultSpeed, m_nDefaultTempo, m_nDefaultGlobalVolume; + DWORD m_dwSongFlags; // Song flags SONG_XXXX + UINT m_nChannels, m_nMixChannels, m_nMixStat, m_nBufferCount; + UINT m_nType, m_nSamples, m_nInstruments; + UINT m_nTickCount, m_nTotalCount, m_nPatternDelay, m_nFrameDelay; + UINT m_nMusicSpeed, m_nMusicTempo; + UINT m_nNextRow, m_nRow; + UINT m_nPattern,m_nCurrentPattern,m_nNextPattern,m_nRestartPos; + UINT m_nMasterVolume, m_nGlobalVolume, m_nSongPreAmp; + UINT m_nFreqFactor, m_nTempoFactor, m_nOldGlbVolSlide; + LONG m_nMinPeriod, m_nMaxPeriod, m_nRepeatCount, m_nInitialRepeatCount; + DWORD m_nGlobalFadeSamples, m_nGlobalFadeMaxSamples; + UINT m_nMaxOrderPosition; + UINT m_nPatternNames; + LPSTR m_lpszSongComments, m_lpszPatternNames; + char m_szNames[MAX_INSTRUMENTS][32]; // changed from CHAR + CHAR CompressionTable[16]; + +public: + CSoundFile(); + ~CSoundFile(); + +public: + BOOL Create(LPCBYTE lpStream, DWORD dwMemLength=0); + BOOL Destroy(); + UINT GetType() const { return m_nType; } + UINT GetNumChannels() const; + UINT GetLogicalChannels() const { return m_nChannels; } + BOOL SetMasterVolume(UINT vol, BOOL bAdjustAGC=FALSE); + UINT GetMasterVolume() const { return m_nMasterVolume; } + UINT GetNumPatterns() const; + UINT GetNumInstruments() const; + UINT GetNumSamples() const { return m_nSamples; } + UINT GetCurrentPos() const; + UINT GetCurrentPattern() const { return m_nPattern; } + UINT GetCurrentOrder() const { return m_nCurrentPattern; } + UINT GetSongComments(LPSTR s, UINT cbsize, UINT linesize=32); + UINT GetRawSongComments(LPSTR s, UINT cbsize, UINT linesize=32); + UINT GetMaxPosition() const; + void SetCurrentPos(UINT nPos); + void SetCurrentOrder(UINT nOrder); + void GetTitle(LPSTR s) const { lstrcpyn(s,m_szNames[0],32); } + LPCSTR GetTitle() const { return m_szNames[0]; } + UINT GetSampleName(UINT nSample,LPSTR s=NULL) const; + UINT GetInstrumentName(UINT nInstr,LPSTR s=NULL) const; + UINT GetMusicSpeed() const { return m_nMusicSpeed; } + UINT GetMusicTempo() const { return m_nMusicTempo; } + DWORD GetLength(BOOL bAdjust, BOOL bTotal=FALSE); + DWORD GetSongTime() { return GetLength(FALSE, TRUE); } + void SetRepeatCount(int n) { m_nRepeatCount = n; m_nInitialRepeatCount = n; } + int GetRepeatCount() const { return m_nRepeatCount; } + BOOL IsPaused() const { return (m_dwSongFlags & SONG_PAUSED) ? TRUE : FALSE; } + void LoopPattern(int nPat, int nRow=0); + void CheckCPUUsage(UINT nCPU); + BOOL SetPatternName(UINT nPat, LPCSTR lpszName); + BOOL GetPatternName(UINT nPat, LPSTR lpszName, UINT cbSize=MAX_PATTERNNAME) const; + // Module Loaders + BOOL ReadXM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadS3M(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMod(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMed(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadSTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadIT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL Read669(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadUlt(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadWav(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDSM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadFAR(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMS(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMDL(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadOKT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDMF(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDBM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMF(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMT2(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPSM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadJ2B(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadUMX(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadABC(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestABC(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMID(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestMID(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPAT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestPAT(LPCBYTE lpStream, DWORD dwMemLength); + // Save Functions +#ifndef MODPLUG_NO_FILESAVE + UINT WriteSample(FILE *f, MODINSTRUMENT *pins, UINT nFlags, UINT nMaxLen=0); + BOOL SaveXM(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveS3M(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveMod(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveIT(LPCSTR lpszFileName, UINT nPacking=0); +#endif // MODPLUG_NO_FILESAVE + // MOD Convert function + UINT GetBestSaveFormat() const; + UINT GetSaveFormats() const; + void ConvertModCommand(MODCOMMAND *) const; + void S3MConvert(MODCOMMAND *m, BOOL bIT) const; + void S3MSaveConvert(UINT *pcmd, UINT *pprm, BOOL bIT) const; + WORD ModSaveCommand(const MODCOMMAND *m, BOOL bXM) const; + +public: + // Real-time sound functions + VOID ResetChannels(); + + UINT Read(LPVOID lpBuffer, UINT cbBuffer); + UINT CreateStereoMix(int count); + BOOL FadeSong(UINT msec); + BOOL GlobalFadeSong(UINT msec); + UINT GetTotalTickCount() const { return m_nTotalCount; } + VOID ResetTotalTickCount() { m_nTotalCount = 0; } + +public: + // Mixer Config + static BOOL InitPlayer(BOOL bReset=FALSE); + static BOOL SetMixConfig(UINT nStereoSeparation, UINT nMaxMixChannels); + static BOOL SetWaveConfig(UINT nRate,UINT nBits,UINT nChannels,BOOL bMMX=FALSE); + static BOOL SetResamplingMode(UINT nMode); // SRCMODE_XXXX + static BOOL IsStereo() { return (gnChannels > 1) ? TRUE : FALSE; } + static DWORD GetSampleRate() { return gdwMixingFreq; } + static DWORD GetBitsPerSample() { return gnBitsPerSample; } + static DWORD InitSysInfo(); + static DWORD GetSysInfo() { return gdwSysInfo; } + // AGC + static BOOL GetAGC() { return (gdwSoundSetup & SNDMIX_AGC) ? TRUE : FALSE; } + static void SetAGC(BOOL b); + static void ResetAGC(); + static void ProcessAGC(int count); + + //GCCFIX -- added these functions back in! + static BOOL SetWaveConfigEx(BOOL bSurround,BOOL bNoOverSampling,BOOL bReverb,BOOL hqido,BOOL bMegaBass,BOOL bNR,BOOL bEQ); + // DSP Effects + static void InitializeDSP(BOOL bReset); + static void ProcessStereoDSP(int count); + static void ProcessMonoDSP(int count); + // [Reverb level 0(quiet)-100(loud)], [delay in ms, usually 40-200ms] + static BOOL SetReverbParameters(UINT nDepth, UINT nDelay); + // [XBass level 0(quiet)-100(loud)], [cutoff in Hz 10-100] + static BOOL SetXBassParameters(UINT nDepth, UINT nRange); + // [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-40ms] + static BOOL SetSurroundParameters(UINT nDepth, UINT nDelay); +public: + BOOL ReadNote(); + BOOL ProcessRow(); + BOOL ProcessEffects(); + UINT GetNNAChannel(UINT nChn) const; + void CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut); + void NoteChange(UINT nChn, int note, BOOL bPorta=FALSE, BOOL bResetEnv=TRUE); + void InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta=FALSE,BOOL bUpdVol=TRUE,BOOL bResetEnv=TRUE); + // Channel Effects + void PortamentoUp(MODCHANNEL *pChn, UINT param); + void PortamentoDown(MODCHANNEL *pChn, UINT param); + void FinePortamentoUp(MODCHANNEL *pChn, UINT param); + void FinePortamentoDown(MODCHANNEL *pChn, UINT param); + void ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param); + void ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param); + void TonePortamento(MODCHANNEL *pChn, UINT param); + void Vibrato(MODCHANNEL *pChn, UINT param); + void FineVibrato(MODCHANNEL *pChn, UINT param); + void VolumeSlide(MODCHANNEL *pChn, UINT param); + void PanningSlide(MODCHANNEL *pChn, UINT param); + void ChannelVolSlide(MODCHANNEL *pChn, UINT param); + void FineVolumeUp(MODCHANNEL *pChn, UINT param); + void FineVolumeDown(MODCHANNEL *pChn, UINT param); + void Tremolo(MODCHANNEL *pChn, UINT param); + void Panbrello(MODCHANNEL *pChn, UINT param); + void RetrigNote(UINT nChn, UINT param); + void NoteCut(UINT nChn, UINT nTick); + void KeyOff(UINT nChn); + int PatternLoop(MODCHANNEL *, UINT param); + void ExtendedMODCommands(UINT nChn, UINT param); + void ExtendedS3MCommands(UINT nChn, UINT param); + void ExtendedChannelEffect(MODCHANNEL *, UINT param); + void ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param=0); + void SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier=256) const; + // Low-Level effect processing + void DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide); + // Global Effects + void SetTempo(UINT param); + void SetSpeed(UINT param); + void GlobalVolSlide(UINT param); + DWORD IsSongFinished(UINT nOrder, UINT nRow) const; + BOOL IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const; + // Read/Write sample functions + signed char GetDeltaValue(signed char prev, UINT n) const { return (signed char)(prev + CompressionTable[n & 0x0F]); } + UINT PackSample(int &sample, int next); + BOOL CanPackSample(LPSTR pSample, UINT nLen, UINT nPacking, BYTE *result=NULL); + UINT ReadSample(MODINSTRUMENT *pIns, UINT nFlags, LPCSTR pMemFile, DWORD dwMemLength); + BOOL DestroySample(UINT nSample); + BOOL DestroyInstrument(UINT nInstr); + BOOL IsSampleUsed(UINT nSample); + BOOL IsInstrumentUsed(UINT nInstr); + BOOL RemoveInstrumentSamples(UINT nInstr); + UINT DetectUnusedSamples(BOOL *); + BOOL RemoveSelectedSamples(BOOL *); + void AdjustSampleLoop(MODINSTRUMENT *pIns); + // I/O from another sound file + BOOL ReadInstrumentFromSong(UINT nInstr, CSoundFile *, UINT nSrcInstrument); + BOOL ReadSampleFromSong(UINT nSample, CSoundFile *, UINT nSrcSample); + // Period/Note functions + UINT GetNoteFromPeriod(UINT period) const; + UINT GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const; + UINT GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac=0) const; + // Misc functions + MODINSTRUMENT *GetSample(UINT n) { return Ins+n; } + void ResetMidiCfg(); + UINT MapMidiInstrument(DWORD dwProgram, UINT nChannel, UINT nNote); + BOOL ITInstrToMPT(const void *p, INSTRUMENTHEADER *penv, UINT trkvers); + UINT SaveMixPlugins(FILE *f=NULL, BOOL bUpdate=TRUE); + UINT LoadMixPlugins(const void *pData, UINT nLen); +#ifndef NO_FILTER + DWORD CutOffToFrequency(UINT nCutOff, int flt_modifier=256) const; // [0-255] => [1-10KHz] +#endif + + // Static helper functions +public: + static DWORD TransposeToFrequency(int transp, int ftune=0); + static int FrequencyToTranspose(DWORD freq); + static void FrequencyToTranspose(MODINSTRUMENT *psmp); + + // System-Dependant functions +public: + static MODCOMMAND *AllocatePattern(UINT rows, UINT nchns); + static signed char* AllocateSample(UINT nbytes); + static void FreePattern(LPVOID pat); + static void FreeSample(LPVOID p); + static UINT Normalize24BitBuffer(LPBYTE pbuffer, UINT cbsizebytes, DWORD lmax24, DWORD dwByteInc); +}; + + +// inline DWORD BigEndian(DWORD x) { return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24); } +// inline WORD BigEndianW(WORD x) { return (WORD)(((x >> 8) & 0xFF) | ((x << 8) & 0xFF00)); } + + +////////////////////////////////////////////////////////// +// WAVE format information + +#pragma pack(1) + +// Standard IFF chunks IDs +#define IFFID_FORM 0x4d524f46 +#define IFFID_RIFF 0x46464952 +#define IFFID_WAVE 0x45564157 +#define IFFID_LIST 0x5453494C +#define IFFID_INFO 0x4F464E49 + +// IFF Info fields +#define IFFID_ICOP 0x504F4349 +#define IFFID_IART 0x54524149 +#define IFFID_IPRD 0x44525049 +#define IFFID_INAM 0x4D414E49 +#define IFFID_ICMT 0x544D4349 +#define IFFID_IENG 0x474E4549 +#define IFFID_ISFT 0x54465349 +#define IFFID_ISBJ 0x4A425349 +#define IFFID_IGNR 0x524E4749 +#define IFFID_ICRD 0x44524349 + +// Wave IFF chunks IDs +#define IFFID_wave 0x65766177 +#define IFFID_fmt 0x20746D66 +#define IFFID_wsmp 0x706D7377 +#define IFFID_pcm 0x206d6370 +#define IFFID_data 0x61746164 +#define IFFID_smpl 0x6C706D73 +#define IFFID_xtra 0x61727478 + +typedef struct WAVEFILEHEADER +{ + DWORD id_RIFF; // "RIFF" + DWORD filesize; // file length-8 + DWORD id_WAVE; +} WAVEFILEHEADER; + + +typedef struct WAVEFORMATHEADER +{ + DWORD id_fmt; // "fmt " + DWORD hdrlen; // 16 + WORD format; // 1 + WORD channels; // 1:mono, 2:stereo + DWORD freqHz; // sampling freq + DWORD bytessec; // bytes/sec=freqHz*samplesize + WORD samplesize; // sizeof(sample) + WORD bitspersample; // bits per sample (8/16) +} WAVEFORMATHEADER; + + +typedef struct WAVEDATAHEADER +{ + DWORD id_data; // "data" + DWORD length; // length of data +} WAVEDATAHEADER; + + +typedef struct WAVESMPLHEADER +{ + // SMPL + DWORD smpl_id; // "smpl" -> 0x6C706D73 + DWORD smpl_len; // length of smpl: 3Ch (54h with sustain loop) + DWORD dwManufacturer; + DWORD dwProduct; + DWORD dwSamplePeriod; // 1000000000/freqHz + DWORD dwBaseNote; // 3Ch = C-4 -> 60 + RelativeTone + DWORD dwPitchFraction; + DWORD dwSMPTEFormat; + DWORD dwSMPTEOffset; + DWORD dwSampleLoops; // number of loops + DWORD cbSamplerData; +} WAVESMPLHEADER; + + +typedef struct SAMPLELOOPSTRUCT +{ + DWORD dwIdentifier; + DWORD dwLoopType; // 0=normal, 1=bidi + DWORD dwLoopStart; + DWORD dwLoopEnd; // Byte offset ? + DWORD dwFraction; + DWORD dwPlayCount; // Loop Count, 0=infinite +} SAMPLELOOPSTRUCT; + + +typedef struct WAVESAMPLERINFO +{ + WAVESMPLHEADER wsiHdr; + SAMPLELOOPSTRUCT wsiLoops[2]; +} WAVESAMPLERINFO; + + +typedef struct WAVELISTHEADER +{ + DWORD list_id; // "LIST" -> 0x5453494C + DWORD list_len; + DWORD info; // "INFO" +} WAVELISTHEADER; + + +typedef struct WAVEEXTRAHEADER +{ + DWORD xtra_id; // "xtra" -> 0x61727478 + DWORD xtra_len; + DWORD dwFlags; + WORD wPan; + WORD wVolume; + WORD wGlobalVol; + WORD wReserved; + BYTE nVibType; + BYTE nVibSweep; + BYTE nVibDepth; + BYTE nVibRate; +} WAVEEXTRAHEADER; + +#pragma pack() + +/////////////////////////////////////////////////////////// +// Low-level Mixing functions + +#define MIXBUFFERSIZE 512 +#define MIXING_ATTENUATION 4 +#define MIXING_CLIPMIN (-0x08000000) +#define MIXING_CLIPMAX (0x07FFFFFF) +#define VOLUMERAMPPRECISION 12 +#define FADESONGDELAY 100 +#define EQ_BUFFERSIZE (MIXBUFFERSIZE) +#define AGC_PRECISION 9 +#define AGC_UNITY (1 << AGC_PRECISION) + +// Calling conventions +#ifdef MSC_VER +#define MPPASMCALL __cdecl +#define MPPFASTCALL __fastcall +#else +#define MPPASMCALL +#define MPPFASTCALL +#endif + +#define MOD2XMFineTune(k) ((int)( (signed char)((k)<<4) )) +#define XM2MODFineTune(k) ((int)( (k>>4)&0x0f )) + +int _muldiv(long a, long b, long c); +int _muldivr(long a, long b, long c); + + +// Byte swapping functions from the GNU C Library and libsdl + +/* Swap bytes in 16 bit value. */ +#ifdef __GNUC__ +# define bswap_16(x) \ + (__extension__ \ + ({ unsigned short int __bsx = (x); \ + ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); })) +#else +static __inline unsigned short int +bswap_16 (unsigned short int __bsx) +{ + return ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); +} +#endif + +/* Swap bytes in 32 bit value. */ +#ifdef __GNUC__ +# define bswap_32(x) \ + (__extension__ \ + ({ unsigned int __bsx = (x); \ + ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | \ + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); })) +#else +static __inline unsigned int +bswap_32 (unsigned int __bsx) +{ + return ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); +} +#endif + +#if (defined ARM) && (defined _WIN32_WCE) +static __inline unsigned short int +ARM_get16(const void *data) +{ + unsigned short int s; + memcpy(&s,data,sizeof(s)); + return s; +} + +static __inline unsigned int +ARM_get32(const void *data) +{ + unsigned int s; + memcpy(&s,data,sizeof(s)); + return s; +} + +#define bswapLE16(X) ARM_get16(&X) +#define bswapLE32(X) ARM_get32(&X) +#define bswapBE16(X) bswap_16(ARM_get16(&X)) +#define bswapBE32(X) bswap_32(ARM_get32(&X)) + +// From libsdl +#elif defined(WORDS_BIGENDIAN) && WORDS_BIGENDIAN +#define bswapLE16(X) bswap_16(X) +#define bswapLE32(X) bswap_32(X) +#define bswapBE16(X) (X) +#define bswapBE32(X) (X) +#else +#define bswapLE16(X) (X) +#define bswapLE32(X) (X) +#define bswapBE16(X) bswap_16(X) +#define bswapBE32(X) bswap_32(X) +#endif + +#endif diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/stdafx.h b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/stdafx.h new file mode 100644 index 000000000..735b8df5b --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/stdafx.h @@ -0,0 +1,146 @@ +/* + * This source code is public domain. + * + * Authors: Rani Assaf , + * Olivier Lapicque , + * Adam Goode (endian and char fixes for PPC) + */ + +#ifndef MODPLUG_STDAFX_H +#define MODPLUG_STDAFX_H + +/* Autoconf detection of stdint/inttypes */ +#if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED) +# include "config.h" +# define CONFIG_H_INCLUDED 1 +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif + + +#ifdef _WIN32 + +#ifdef MSC_VER +#pragma warning (disable:4201) +#pragma warning (disable:4514) +#endif + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include + +#define srandom(_seed) srand(_seed) +#define random() rand() +#define sleep(_ms) Sleep(_ms) + +inline void ProcessPlugins(int n) {} + +#define strncasecmp(a,b,c) strncmp(a,b,c) +#define strcasecmp(a,b) strcmp(a,b) +#define strnicmp(a,b,c) strncasecmp(a,b,c) +#define HAVE_SINF 1 + +#ifndef isblank +#define isblank(c) ((c) == ' ' || (c) == '\t') +#endif + +#else + +#include +#include +#include +#ifdef HAVE_MALLOC_H +#include +#endif + +typedef int8_t CHAR; +typedef uint8_t UCHAR; +typedef uint8_t* PUCHAR; +typedef uint16_t USHORT; +typedef uint32_t ULONG; +typedef uint32_t UINT; +typedef uint32_t DWORD; +typedef int32_t LONG; +typedef int64_t LONGLONG; +typedef int32_t* LPLONG; +typedef uint32_t* LPDWORD; +typedef uint16_t WORD; +typedef uint8_t BYTE; +typedef uint8_t* LPBYTE; +typedef bool BOOL; +typedef char* LPSTR; +typedef void* LPVOID; +typedef uint16_t* LPWORD; +typedef const char* LPCSTR; +typedef void* PVOID; +typedef void VOID; + +inline LONG MulDiv (long a, long b, long c) +{ + // if (!c) return 0; + return ((uint64_t) a * (uint64_t) b ) / c; +} + +#define MODPLUG_NO_FILESAVE +#define NO_AGC +#define LPCTSTR LPCSTR +#define lstrcpyn strncpy +#define lstrcpy strcpy +#define lstrcmp strcmp +#define WAVE_FORMAT_PCM 1 +//#define ENABLE_EQ + +#define GHND 0 + +inline int8_t * GlobalAllocPtr(unsigned int, size_t size) +{ + int8_t * p = (int8_t *) malloc(size); + + if (p != NULL) memset(p, 0, size); + return p; +} + +inline void ProcessPlugins(int /* n */ ) {} + +#define GlobalFreePtr(p) free((void *)(p)) + +#define strnicmp(a,b,c) strncasecmp(a,b,c) +#define wsprintf sprintf + +#ifndef FALSE +#define FALSE false +#endif + +#ifndef TRUE +#define TRUE true +#endif + +#endif // _WIN32 + +#if defined(_WIN32) || defined(__CYGWIN__) +# if defined(MODPLUG_BUILD) && defined(DLL_EXPORT) /* building libmodplug as a dll for windows */ +# define MODPLUG_EXPORT __declspec(dllexport) +# elif defined(MODPLUG_BUILD) || defined(MODPLUG_STATIC) /* building or using static libmodplug for windows */ +# define MODPLUG_EXPORT +# else +# define MODPLUG_EXPORT __declspec(dllimport) /* using libmodplug dll for windows */ +# endif +/* FIXME: USE VISIBILITY ATTRIBUTES HERE */ +#elif defined(MODPLUG_BUILD) +#define MODPLUG_EXPORT +#else +#define MODPLUG_EXPORT +#endif + +#endif + + + diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.c b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.c new file mode 100644 index 000000000..51dd3d79f --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.c @@ -0,0 +1,604 @@ +/* + * libopenmpt_modplug.c + * -------------------- + * Purpose: libopenmpt emulation of the libmodplug interface + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#ifndef NO_LIBMODPLUG + +#ifdef LIBOPENMPT_BUILD_DLL +#undef LIBOPENMPT_BUILD_DLL +#endif + +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#endif /* _MSC_VER */ + +#include + +#include +#include +#include +#include +#include +#include + +#define MODPLUG_BUILD +#ifdef _MSC_VER +#ifdef MPT_BUILD_MSVC_SHARED +#define DLL_EXPORT +#endif /* MPT_BUILD_MSVC_SHARED */ +#ifdef MPT_BUILD_MSVC_STATIC +#define MODPLUG_STATIC +#endif /* MPT_BUILD_MSVC_STATIC */ +#endif /* _MSC_VER */ +#ifdef _MSC_VER +#define LIBOPENMPT_MODPLUG_API +#else /* !_MSC_VER */ +#define LIBOPENMPT_MODPLUG_API LIBOPENMPT_API_HELPER_EXPORT +#endif /* _MSC_VER */ +#include "libmodplug/modplug.h" + +/* from libmodplug/sndfile.h */ +/* header is not c clean */ +#define MIXING_ATTENUATION 4 +#define MOD_TYPE_NONE 0x0 +#define MOD_TYPE_MOD 0x1 +#define MOD_TYPE_S3M 0x2 +#define MOD_TYPE_XM 0x4 +#define MOD_TYPE_MED 0x8 +#define MOD_TYPE_MTM 0x10 +#define MOD_TYPE_IT 0x20 +#define MOD_TYPE_669 0x40 +#define MOD_TYPE_ULT 0x80 +#define MOD_TYPE_STM 0x100 +#define MOD_TYPE_FAR 0x200 +#define MOD_TYPE_WAV 0x400 +#define MOD_TYPE_AMF 0x800 +#define MOD_TYPE_AMS 0x1000 +#define MOD_TYPE_DSM 0x2000 +#define MOD_TYPE_MDL 0x4000 +#define MOD_TYPE_OKT 0x8000 +#define MOD_TYPE_MID 0x10000 +#define MOD_TYPE_DMF 0x20000 +#define MOD_TYPE_PTM 0x40000 +#define MOD_TYPE_DBM 0x80000 +#define MOD_TYPE_MT2 0x100000 +#define MOD_TYPE_AMF0 0x200000 +#define MOD_TYPE_PSM 0x400000 +#define MOD_TYPE_J2B 0x800000 +#define MOD_TYPE_ABC 0x1000000 +#define MOD_TYPE_PAT 0x2000000 +#define MOD_TYPE_UMX 0x80000000 // Fake type + +#define BUFFER_COUNT 1024 + +struct _ModPlugFile { + openmpt_module* mod; + signed short* buf; + signed int* mixerbuf; + char* name; + char* message; + ModPlug_Settings settings; + ModPlugMixerProc mixerproc; + ModPlugNote** patterns; +}; + +static ModPlug_Settings globalsettings = { + MODPLUG_ENABLE_OVERSAMPLING|MODPLUG_ENABLE_NOISE_REDUCTION, + 2, + 16, + 44100, + MODPLUG_RESAMPLE_LINEAR, + 128, + 256, + 0, + 0, + 0, + 0, + 0, + 0, + 0 +}; + +static int32_t modplugresamplingmode_to_filterlength(int mode) +{ + if(mode<0){ + return 1; + } + switch(mode){ + case MODPLUG_RESAMPLE_NEAREST: return 1; break; + case MODPLUG_RESAMPLE_LINEAR: return 2; break; + case MODPLUG_RESAMPLE_SPLINE: return 4; break; + case MODPLUG_RESAMPLE_FIR: return 8; break; + } + return 8; +} + +LIBOPENMPT_MODPLUG_API ModPlugFile* ModPlug_Load(const void* data, int size) +{ + ModPlugFile* file = malloc(sizeof(ModPlugFile)); + const char* name = NULL; + const char* message = NULL; + if(!file) return NULL; + memset(file,0,sizeof(ModPlugFile)); + memcpy(&file->settings,&globalsettings,sizeof(ModPlug_Settings)); + file->mod = openmpt_module_create_from_memory2(data,size,NULL,NULL,NULL,NULL,NULL,NULL,NULL); + if(!file->mod){ + free(file); + return NULL; + } + file->buf = malloc(BUFFER_COUNT*sizeof(signed short)*4); + if(!file->buf){ + openmpt_module_destroy(file->mod); + free(file); + return NULL; + } + openmpt_module_set_repeat_count(file->mod,file->settings.mLoopCount); + name = openmpt_module_get_metadata(file->mod,"title"); + if(name){ + file->name = malloc(strlen(name)+1); + if(file->name){ + strcpy(file->name,name); + } + openmpt_free_string(name); + name = NULL; + }else{ + file->name = malloc(strlen("")+1); + if(file->name){ + strcpy(file->name,""); + } + } + message = openmpt_module_get_metadata(file->mod,"message"); + if(message){ + file->message = malloc(strlen(message)+1); + if(file->message){ + strcpy(file->message,message); + } + openmpt_free_string(message); + message = NULL; + }else{ + file->message = malloc(strlen("")+1); + if(file->message){ + strcpy(file->message,""); + } + } + openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT,file->settings.mStereoSeparation*100/128); + openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH,modplugresamplingmode_to_filterlength(file->settings.mResamplingMode)); + return file; +} + +LIBOPENMPT_MODPLUG_API void ModPlug_Unload(ModPlugFile* file) +{ + int p; + if(!file) return; + if(file->patterns){ + for(p=0;pmod);p++){ + if(file->patterns[p]){ + free(file->patterns[p]); + file->patterns[p] = NULL; + } + } + free(file->patterns); + file->patterns = NULL; + } + if(file->mixerbuf){ + free(file->mixerbuf); + file->mixerbuf = NULL; + } + openmpt_module_destroy(file->mod); + file->mod = NULL; + free(file->name); + file->name = NULL; + free(file->message); + file->message = NULL; + free(file->buf); + file->buf = NULL; + free(file); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_Read(ModPlugFile* file, void* buffer, int size) +{ + int framesize; + int framecount; + int frames; + int rendered; + int frame; + int channel; + int totalrendered; + signed short* in; + signed int* mixbuf; + unsigned char* buf8; + signed short* buf16; + signed int* buf32; + if(!file) return 0; + framesize = file->settings.mBits/8*file->settings.mChannels; + framecount = size/framesize; + buf8 = buffer; + buf16 = buffer; + buf32 = buffer; + totalrendered = 0; + while(framecount>0){ + frames = framecount; + if(frames>BUFFER_COUNT){ + frames = BUFFER_COUNT; + } + if(file->settings.mChannels==1){ + rendered = (int)openmpt_module_read_mono(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0]); + }else if(file->settings.mChannels==2){ + rendered = (int)openmpt_module_read_stereo(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0],&file->buf[frames*1]); + }else if(file->settings.mChannels==4){ + rendered = (int)openmpt_module_read_quad(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0],&file->buf[frames*1],&file->buf[frames*2],&file->buf[frames*3]); + }else{ + return 0; + } + in = file->buf; + if(file->mixerproc&&file->mixerbuf){ + mixbuf=file->mixerbuf; + for(frame=0;framesettings.mChannels;channel++){ + *mixbuf = in[frames*channel+frame]<<(32-16-1-MIXING_ATTENUATION); + mixbuf++; + } + } + file->mixerproc(file->mixerbuf,file->settings.mChannels*frames,file->settings.mChannels); + mixbuf=file->mixerbuf; + for(frame=0;framesettings.mChannels;channel++){ + in[frames*channel+frame] = *mixbuf>>(32-16-1-MIXING_ATTENUATION); + mixbuf++; + } + } + } + if(file->settings.mBits==8){ + for(frame=0;framesettings.mChannels;channel++){ + *buf8 = in[frames*channel+frame]/256+0x80; + buf8++; + } + } + }else if(file->settings.mBits==16){ + for(frame=0;framesettings.mChannels;channel++){ + *buf16 = in[frames*channel+frame]; + buf16++; + } + } + }else if(file->settings.mBits==32){ + for(frame=0;framesettings.mChannels;channel++){ + *buf32 = in[frames*channel+frame] << (32-16-1-MIXING_ATTENUATION); + buf32++; + } + } + }else{ + return 0; + } + totalrendered += rendered; + framecount -= frames; + if(!rendered) break; + } + memset(((char*)buffer)+totalrendered*framesize,0,size-totalrendered*framesize); + return totalrendered*framesize; +} + +LIBOPENMPT_MODPLUG_API const char* ModPlug_GetName(ModPlugFile* file) +{ + if(!file) return NULL; + return file->name; +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetLength(ModPlugFile* file) +{ + if(!file) return 0; + return (int)(openmpt_module_get_duration_seconds(file->mod)*1000.0); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_Seek(ModPlugFile* file, int millisecond) +{ + if(!file) return; + openmpt_module_set_position_seconds(file->mod,(double)millisecond*0.001); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_GetSettings(ModPlug_Settings* settings) +{ + if(!settings) return; + memcpy(settings,&globalsettings,sizeof(ModPlug_Settings)); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_SetSettings(const ModPlug_Settings* settings) +{ + if(!settings) return; + memcpy(&globalsettings,settings,sizeof(ModPlug_Settings)); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) +{ + int32_t val; + if(!file) return 0; + val = 0; + if(!openmpt_module_get_render_param(file->mod,OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL,&val)) return 128; + return (unsigned int)(128.0*pow(10.0,val*0.0005)); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) +{ + if(!file) return; + openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL,(int32_t)(2000.0*log10(cvol/128.0))); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentSpeed(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_speed(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentTempo(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_tempo(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentOrder(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_order(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentPattern(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_pattern(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentRow(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_row(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetPlayingChannels(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_playing_channels(file->mod); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_SeekOrder(ModPlugFile* file,int order) +{ + if(!file) return; + openmpt_module_set_position_order_row(file->mod,order,0); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetModuleType(ModPlugFile* file) +{ + const char* type; + int retval; + if(!file) return 0; + type = openmpt_module_get_metadata(file->mod,"type"); + retval = MOD_TYPE_NONE; + if(!type){ + return retval; + } + if(!strcmp(type,"mod")){ + retval = MOD_TYPE_MOD; + }else if(!strcmp(type,"s3m")){ + retval = MOD_TYPE_S3M; + }else if(!strcmp(type,"xm")){ + retval = MOD_TYPE_XM; + }else if(!strcmp(type,"med")){ + retval = MOD_TYPE_MED; + }else if(!strcmp(type,"mtm")){ + retval = MOD_TYPE_MTM; + }else if(!strcmp(type,"it")){ + retval = MOD_TYPE_IT; + }else if(!strcmp(type,"669")){ + retval = MOD_TYPE_669; + }else if(!strcmp(type,"ult")){ + retval = MOD_TYPE_ULT; + }else if(!strcmp(type,"stm")){ + retval = MOD_TYPE_STM; + }else if(!strcmp(type,"far")){ + retval = MOD_TYPE_FAR; + }else if(!strcmp(type,"s3m")){ + retval = MOD_TYPE_WAV; + }else if(!strcmp(type,"amf")){ + retval = MOD_TYPE_AMF; + }else if(!strcmp(type,"ams")){ + retval = MOD_TYPE_AMS; + }else if(!strcmp(type,"dsm")){ + retval = MOD_TYPE_DSM; + }else if(!strcmp(type,"mdl")){ + retval = MOD_TYPE_MDL; + }else if(!strcmp(type,"okt")){ + retval = MOD_TYPE_OKT; + }else if(!strcmp(type,"mid")){ + retval = MOD_TYPE_MID; + }else if(!strcmp(type,"dmf")){ + retval = MOD_TYPE_DMF; + }else if(!strcmp(type,"ptm")){ + retval = MOD_TYPE_PTM; + }else if(!strcmp(type,"dbm")){ + retval = MOD_TYPE_DBM; + }else if(!strcmp(type,"mt2")){ + retval = MOD_TYPE_MT2; + }else if(!strcmp(type,"amf0")){ + retval = MOD_TYPE_AMF0; + }else if(!strcmp(type,"psm")){ + retval = MOD_TYPE_PSM; + }else if(!strcmp(type,"j2b")){ + retval = MOD_TYPE_J2B; + }else if(!strcmp(type,"abc")){ + retval = MOD_TYPE_ABC; + }else if(!strcmp(type,"pat")){ + retval = MOD_TYPE_PAT; + }else if(!strcmp(type,"umx")){ + retval = MOD_TYPE_UMX; + }else{ + retval = MOD_TYPE_IT; /* fallback, most complex type */ + } + openmpt_free_string(type); + return retval; +} + +LIBOPENMPT_MODPLUG_API char* ModPlug_GetMessage(ModPlugFile* file) +{ + if(!file) return NULL; + return file->message; +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumInstruments(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_instruments(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumSamples(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_samples(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumPatterns(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_patterns(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumChannels(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_channels(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff) +{ + const char* str; + char buf[32]; + if(!file) return 0; + str = openmpt_module_get_sample_name(file->mod,qual-1); + memset(buf,0,32); + if(str){ + strncpy(buf,str,31); + openmpt_free_string(str); + } + if(buff){ + strncpy(buff,buf,32); + } + return (unsigned int)strlen(buf); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff) +{ + const char* str; + char buf[32]; + if(!file) return 0; + str = openmpt_module_get_instrument_name(file->mod,qual-1); + memset(buf,0,32); + if(str){ + strncpy(buf,str,31); + openmpt_free_string(str); + } + if(buff){ + strncpy(buff,buf,32); + } + return (unsigned int)strlen(buf); +} + +LIBOPENMPT_MODPLUG_API ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows) +{ + int c; + int r; + int numr; + int numc; + ModPlugNote note; + if(!file) return NULL; + if(numrows){ + *numrows = openmpt_module_get_pattern_num_rows(file->mod,pattern); + } + if(pattern<0||pattern>=openmpt_module_get_num_patterns(file->mod)){ + return NULL; + } + if(!file->patterns){ + file->patterns = malloc(sizeof(ModPlugNote*)*openmpt_module_get_pattern_num_rows(file->mod,pattern)); + if(!file->patterns) return NULL; + memset(file->patterns,0,sizeof(ModPlugNote*)*openmpt_module_get_pattern_num_rows(file->mod,pattern)); + } + if(!file->patterns[pattern]){ + file->patterns[pattern] = malloc(sizeof(ModPlugNote)*openmpt_module_get_pattern_num_rows(file->mod,pattern)*openmpt_module_get_num_channels(file->mod)); + if(!file->patterns[pattern]) return NULL; + memset(file->patterns[pattern],0,sizeof(ModPlugNote)*openmpt_module_get_pattern_num_rows(file->mod,pattern)*openmpt_module_get_num_channels(file->mod)); + } + numr = openmpt_module_get_pattern_num_rows(file->mod,pattern); + numc = openmpt_module_get_num_channels(file->mod); + for(r=0;rmod,pattern,r,c,OPENMPT_MODULE_COMMAND_NOTE); + note.Instrument = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_INSTRUMENT); + note.VolumeEffect = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_VOLUMEEFFECT); + note.Effect = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_EFFECT); + note.Volume = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_VOLUME); + note.Parameter = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_PARAMETER); + memcpy(&file->patterns[pattern][r*numc+c],¬e,sizeof(ModPlugNote)); + } + } + return file->patterns[pattern]; +} + +LIBOPENMPT_MODPLUG_API void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) +{ + if(!file) return; + if(!file->mixerbuf){ + file->mixerbuf = malloc(BUFFER_COUNT*sizeof(signed int)*4); + } + file->mixerproc = proc; +} + +LIBOPENMPT_MODPLUG_API void ModPlug_UnloadMixerCallback(ModPlugFile* file) +{ + if(!file) return; + file->mixerproc = NULL; + if(file->mixerbuf){ + free(file->mixerbuf); + file->mixerbuf = NULL; + } +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportS3M(%s) not implemented.\n",filepath); + return 0; +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportXM(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportXM(%s) not implemented.\n",filepath); + return 0; +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportMOD(%s) not implemented.\n",filepath); + return 0; +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportIT(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportIT(%s) not implemented.\n",filepath); + return 0; +} + +#endif /* NO_LIBMODPLUG */ diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.pc.in b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.pc.in new file mode 100644 index 000000000..89c1fee89 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=${prefix}/include + +Name: libopenmpt_modplug +Description: The ModPlug mod file playing library (emulated via libopenmpt). +Version: @PACKAGE_VERSION@ +Requires.private: libopenmpt +Libs: -L${libdir} -lopenmpt_modplug +Libs.private: +Cflags: -I${includedir} diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug_cpp.cpp b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug_cpp.cpp new file mode 100644 index 000000000..fd5375d3b --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug_cpp.cpp @@ -0,0 +1,887 @@ +/* + * libopenmpt_modplug_cpp.cpp + * -------------------------- + * Purpose: libopenmpt emulation of the libmodplug c++ interface + * Notes : WARNING! THIS IS A HACK! + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#ifndef NO_LIBMODPLUG + +/* + +*********************************************************************** +WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +*********************************************************************** + +This is a dirty hack to emulate just so much of the libmodplug c++ +interface so that the current known users (mainly xmms-modplug itself, +gstreamer modplug, audacious, and stuff based on those) work. This is +neither a complete nor a correct implementation. +Metadata and other state is not provided or updated. + +*/ + +#ifdef UNICODE +#undef UNICODE +#endif +#ifdef _UNICODE +#undef _UNICODE +#endif + +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#endif /* _MSC_VER */ + +#include + +#include +#include + +#include +#include +#include +#include + +#define MODPLUG_BUILD +#ifdef _MSC_VER +/* libmodplug C++ header is broken for MSVC DLL builds */ +#define MODPLUG_STATIC +#endif /* _MSC_VER */ +#ifdef _MSC_VER +#define LIBOPENMPT_MODPLUG_API +#else /* !_MSC_VER */ +#define LIBOPENMPT_MODPLUG_API LIBOPENMPT_API_HELPER_EXPORT +#endif /* _MSC_VER */ +class LIBOPENMPT_MODPLUG_API CSoundFile; +#include "libmodplug/stdafx.h" +#include "libmodplug/sndfile.h" + +namespace { +template +void Clear( T & x ) +{ + std::memset( &x, 0, sizeof(T) ); +} +} + +//#define mpcpplog() fprintf(stderr, "%s %i\n", __func__, __LINE__) +#define mpcpplog() do{}while(0) + +#define UNUSED(x) (void)((x)) + +union self_t { + CHAR CompressionTable[16]; + openmpt::module * self_; +}; + +static void set_self( CSoundFile * that, openmpt::module * self_ ) { + self_t self_union; + Clear(self_union); + self_union.self_ = self_; + std::memcpy( that->CompressionTable, self_union.CompressionTable, sizeof( self_union.CompressionTable ) ); +} + +static openmpt::module * get_self( const CSoundFile * that ) { + self_t self_union; + Clear(self_union); + std::memcpy( self_union.CompressionTable, that->CompressionTable, sizeof( self_union.CompressionTable ) ); + return self_union.self_; +} + +#define mod ( get_self( this ) ) + +#define update_state() \ + if ( mod ) m_nCurrentPattern = mod->get_current_order(); \ + if ( mod ) m_nPattern = mod->get_current_pattern(); \ + if ( mod ) m_nMusicSpeed = mod->get_current_speed(); \ + if ( mod ) m_nMusicTempo = mod->get_current_tempo(); \ +/**/ + +UINT CSoundFile::m_nXBassDepth = 0; +UINT CSoundFile::m_nXBassRange = 0; +UINT CSoundFile::m_nReverbDepth = 0; +UINT CSoundFile::m_nReverbDelay = 0; +UINT CSoundFile::gnReverbType = 0; +UINT CSoundFile::m_nProLogicDepth = 0; +UINT CSoundFile::m_nProLogicDelay = 0; +UINT CSoundFile::m_nStereoSeparation = 128; +UINT CSoundFile::m_nMaxMixChannels = 256; +LONG CSoundFile::m_nStreamVolume = 0x8000; +DWORD CSoundFile::gdwSysInfo = 0; +DWORD CSoundFile::gdwSoundSetup = 0; +DWORD CSoundFile::gdwMixingFreq = 44100; +DWORD CSoundFile::gnBitsPerSample = 16; +DWORD CSoundFile::gnChannels = 2; +UINT CSoundFile::gnAGC = 0; +UINT CSoundFile::gnVolumeRampSamples = 0; +UINT CSoundFile::gnVUMeter = 0; +UINT CSoundFile::gnCPUUsage = 0; +LPSNDMIXHOOKPROC CSoundFile::gpSndMixHook = 0; +PMIXPLUGINCREATEPROC CSoundFile::gpMixPluginCreateProc = 0; + +CSoundFile::CSoundFile() { + mpcpplog(); + Clear(Chn); + Clear(ChnMix); + Clear(Ins); + Clear(Headers); + Clear(ChnSettings); + Clear(Patterns); + Clear(PatternSize); + Clear(Order); + Clear(m_MidiCfg); + Clear(m_MixPlugins); + Clear(m_nDefaultSpeed); + Clear(m_nDefaultTempo); + Clear(m_nDefaultGlobalVolume); + Clear(m_dwSongFlags); + Clear(m_nChannels); + Clear(m_nMixChannels); + Clear(m_nMixStat); + Clear(m_nBufferCount); + Clear(m_nType); + Clear(m_nSamples); + Clear(m_nInstruments); + Clear(m_nTickCount); + Clear(m_nTotalCount); + Clear(m_nPatternDelay); + Clear(m_nFrameDelay); + Clear(m_nMusicSpeed); + Clear(m_nMusicTempo); + Clear(m_nNextRow); + Clear(m_nRow); + Clear(m_nPattern); + Clear(m_nCurrentPattern); + Clear(m_nNextPattern); + Clear(m_nRestartPos); + Clear(m_nMasterVolume); + Clear(m_nGlobalVolume); + Clear(m_nSongPreAmp); + Clear(m_nFreqFactor); + Clear(m_nTempoFactor); + Clear(m_nOldGlbVolSlide); + Clear(m_nMinPeriod); + Clear(m_nMaxPeriod); + Clear(m_nRepeatCount); + Clear(m_nInitialRepeatCount); + Clear(m_nGlobalFadeSamples); + Clear(m_nGlobalFadeMaxSamples); + Clear(m_nMaxOrderPosition); + Clear(m_nPatternNames); + Clear(m_lpszSongComments); + Clear(m_lpszPatternNames); + Clear(m_szNames); + Clear(CompressionTable); +} + +CSoundFile::~CSoundFile() { + mpcpplog(); + Destroy(); +} + +BOOL CSoundFile::Create( LPCBYTE lpStream, DWORD dwMemLength ) { + mpcpplog(); + try { + openmpt::module * m = new openmpt::module( lpStream, dwMemLength ); + set_self( this, m ); + std::strncpy( m_szNames[0], mod->get_metadata("title").c_str(), sizeof( m_szNames[0] ) - 1 ); + m_szNames[0][ sizeof( m_szNames[0] ) - 1 ] = '\0'; + std::string type = mod->get_metadata("type"); + m_nType = MOD_TYPE_NONE; + if ( type == "mod" ) { + m_nType = MOD_TYPE_MOD; + } else if ( type == "s3m" ) { + m_nType = MOD_TYPE_S3M; + } else if ( type == "xm" ) { + m_nType = MOD_TYPE_XM; + } else if ( type == "med" ) { + m_nType = MOD_TYPE_MED; + } else if ( type == "mtm" ) { + m_nType = MOD_TYPE_MTM; + } else if ( type == "it" ) { + m_nType = MOD_TYPE_IT; + } else if ( type == "669" ) { + m_nType = MOD_TYPE_669; + } else if ( type == "ult" ) { + m_nType = MOD_TYPE_ULT; + } else if ( type == "stm" ) { + m_nType = MOD_TYPE_STM; + } else if ( type == "far" ) { + m_nType = MOD_TYPE_FAR; + } else if ( type == "s3m" ) { + m_nType = MOD_TYPE_WAV; + } else if ( type == "amf" ) { + m_nType = MOD_TYPE_AMF; + } else if ( type == "ams" ) { + m_nType = MOD_TYPE_AMS; + } else if ( type == "dsm" ) { + m_nType = MOD_TYPE_DSM; + } else if ( type == "mdl" ) { + m_nType = MOD_TYPE_MDL; + } else if ( type == "okt" ) { + m_nType = MOD_TYPE_OKT; + } else if ( type == "mid" ) { + m_nType = MOD_TYPE_MID; + } else if ( type == "dmf" ) { + m_nType = MOD_TYPE_DMF; + } else if ( type == "ptm" ) { + m_nType = MOD_TYPE_PTM; + } else if ( type == "dbm" ) { + m_nType = MOD_TYPE_DBM; + } else if ( type == "mt2" ) { + m_nType = MOD_TYPE_MT2; + } else if ( type == "amf0" ) { + m_nType = MOD_TYPE_AMF0; + } else if ( type == "psm" ) { + m_nType = MOD_TYPE_PSM; + } else if ( type == "j2b" ) { + m_nType = MOD_TYPE_J2B; + } else if ( type == "abc" ) { + m_nType = MOD_TYPE_ABC; + } else if ( type == "pat" ) { + m_nType = MOD_TYPE_PAT; + } else if ( type == "umx" ) { + m_nType = MOD_TYPE_UMX; + } else { + m_nType = MOD_TYPE_IT; // fallback, most complex type + } + m_nChannels = mod->get_num_channels(); + m_nMasterVolume = 128; + m_nSamples = mod->get_num_samples(); + update_state(); + return TRUE; + } catch ( ... ) { + Destroy(); + return FALSE; + } +} + +BOOL CSoundFile::Destroy() { + mpcpplog(); + if ( mod ) { + delete mod; + set_self( this, 0 ); + } + return TRUE; +} + +UINT CSoundFile::GetNumChannels() const { + mpcpplog(); + return mod->get_num_channels(); +} + +static std::int32_t vol128_To_millibel( unsigned int vol ) { + return static_cast( 2000.0 * std::log10( static_cast( vol ) / 128.0 ) ); +} + +BOOL CSoundFile::SetMasterVolume( UINT vol, BOOL bAdjustAGC ) { + UNUSED(bAdjustAGC); + mpcpplog(); + m_nMasterVolume = vol; + mod->set_render_param( openmpt::module::RENDER_MASTERGAIN_MILLIBEL, vol128_To_millibel( m_nMasterVolume ) ); + return TRUE; +} + +UINT CSoundFile::GetNumPatterns() const { + mpcpplog(); + return mod->get_num_patterns(); +} + +UINT CSoundFile::GetNumInstruments() const { + mpcpplog(); + return mod->get_num_instruments(); +} + +void CSoundFile::SetCurrentOrder( UINT nOrder ) { + mpcpplog(); + mod->set_position_order_row( nOrder, 0 ); + update_state(); +} + +UINT CSoundFile::GetSampleName( UINT nSample, LPSTR s ) const { + mpcpplog(); + char buf[32]; + std::memset( buf, 0, 32 ); + if ( mod ) { + std::vector names = mod->get_sample_names(); + if ( 1 <= nSample && nSample <= names.size() ) { + std::strncpy( buf, names[ nSample - 1 ].c_str(), 31 ); + } + } + if ( s ) { + std::strncpy( s, buf, 32 ); + } + return static_cast( std::strlen( buf ) ); +} + +UINT CSoundFile::GetInstrumentName( UINT nInstr, LPSTR s ) const { + mpcpplog(); + char buf[32]; + std::memset( buf, 0, 32 ); + if ( mod ) { + std::vector names = mod->get_instrument_names(); + if ( 1 <= nInstr && nInstr <= names.size() ) { + std::strncpy( buf, names[ nInstr - 1 ].c_str(), 31 ); + } + } + if ( s ) { + std::strncpy( s, buf, 32 ); + } + return static_cast( std::strlen( buf ) ); +} + +void CSoundFile::LoopPattern( int nPat, int nRow ) { + UNUSED(nPat); + UNUSED(nRow); + mpcpplog(); + // todo +} + +void CSoundFile::CheckCPUUsage( UINT nCPU ) { + UNUSED(nCPU); + mpcpplog(); +} + +BOOL CSoundFile::SetPatternName( UINT nPat, LPCSTR lpszName ) { + UNUSED(nPat); + mpcpplog(); + if ( !lpszName ) { + return FALSE; + } + // todo + return TRUE; +} + +BOOL CSoundFile::GetPatternName( UINT nPat, LPSTR lpszName, UINT cbSize ) const { + UNUSED(nPat); + mpcpplog(); + if ( !lpszName || cbSize <= 0 ) { + return FALSE; + } + std::memset( lpszName, 0, cbSize ); + // todo + return TRUE; +} + +BOOL CSoundFile::ReadXM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadS3M(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMod(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMed(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadSTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadIT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::Read669(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadUlt(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadWav(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadDSM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadFAR(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMDL(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadOKT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadDMF(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadPTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadDBM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadAMF(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMT2(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadJ2B(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadUMX(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadABC(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::TestABC(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMID(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::TestMID(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadPAT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::TestPAT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } + +#ifndef MODPLUG_NO_FILESAVE + +UINT CSoundFile::WriteSample( FILE * f, MODINSTRUMENT * pins, UINT nFlags, UINT nMaxLen ) { + UNUSED(f); + UNUSED(pins); + UNUSED(nFlags); + UNUSED(nMaxLen); + mpcpplog(); + return 0; +} + +BOOL CSoundFile::SaveXM( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +BOOL CSoundFile::SaveS3M( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +BOOL CSoundFile::SaveMod( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +BOOL CSoundFile::SaveIT( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +#endif + +UINT CSoundFile::GetBestSaveFormat() const { + mpcpplog(); + return MOD_TYPE_IT; +} + +UINT CSoundFile::GetSaveFormats() const { + mpcpplog(); + return MOD_TYPE_IT; +} + +void CSoundFile::ConvertModCommand( MODCOMMAND * ) const { + mpcpplog(); +} + +void CSoundFile::S3MConvert( MODCOMMAND * m, BOOL bIT ) const { + UNUSED(m); + UNUSED(bIT); + mpcpplog(); +} + +void CSoundFile::S3MSaveConvert( UINT * pcmd, UINT * pprm, BOOL bIT ) const { + UNUSED(pcmd); + UNUSED(pprm); + UNUSED(bIT); + mpcpplog(); +} + +WORD CSoundFile::ModSaveCommand( const MODCOMMAND * m, BOOL bXM ) const { + UNUSED(m); + UNUSED(bXM); + mpcpplog(); + return 0; +} + +VOID CSoundFile::ResetChannels() { + mpcpplog(); +} + +UINT CSoundFile::CreateStereoMix( int count ) { + UNUSED(count); + mpcpplog(); + return 0; +} + +BOOL CSoundFile::FadeSong( UINT msec ) { + UNUSED(msec); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::GlobalFadeSong( UINT msec ) { + UNUSED(msec); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::InitPlayer( BOOL bReset ) { + UNUSED(bReset); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::SetMixConfig( UINT nStereoSeparation, UINT nMaxMixChannels ) { + UNUSED(nMaxMixChannels); + mpcpplog(); + m_nStereoSeparation = nStereoSeparation; + return TRUE; +} + +DWORD CSoundFile::InitSysInfo() { + mpcpplog(); + return 0; +} + +void CSoundFile::SetAGC( BOOL b ) { + UNUSED(b); + mpcpplog(); +} + +void CSoundFile::ResetAGC() { + mpcpplog(); +} + +void CSoundFile::ProcessAGC( int count ) { + UNUSED(count); + mpcpplog(); +} + +BOOL CSoundFile::SetWaveConfig( UINT nRate, UINT nBits, UINT nChannels, BOOL bMMX ) { + UNUSED(bMMX); + mpcpplog(); + gdwMixingFreq = nRate; + gnBitsPerSample = nBits; + gnChannels = nChannels; + return TRUE; +} + +BOOL CSoundFile::SetWaveConfigEx( BOOL bSurround, BOOL bNoOverSampling, BOOL bReverb, BOOL hqido, BOOL bMegaBass, BOOL bNR, BOOL bEQ ) { + UNUSED(bSurround); + UNUSED(bReverb); + UNUSED(hqido); + UNUSED(bMegaBass); + UNUSED(bEQ); + mpcpplog(); + DWORD d = gdwSoundSetup & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + if ( bNoOverSampling ) { + d |= SNDMIX_NORESAMPLING; + } else if ( !hqido ) { + d |= 0; + } else if ( !bNR ) { + d |= SNDMIX_HQRESAMPLER; + } else { + d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + } + gdwSoundSetup = d; + return TRUE; +} + +BOOL CSoundFile::SetResamplingMode( UINT nMode ) { + mpcpplog(); + DWORD d = gdwSoundSetup & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + switch ( nMode ) { + case SRCMODE_NEAREST: + d |= SNDMIX_NORESAMPLING; + break; + case SRCMODE_LINEAR: + break; + case SRCMODE_SPLINE: + d |= SNDMIX_HQRESAMPLER; + break; + case SRCMODE_POLYPHASE: + d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + break; + default: + return FALSE; + break; + } + gdwSoundSetup = d; + return TRUE; +} + +BOOL CSoundFile::SetReverbParameters( UINT nDepth, UINT nDelay ) { + UNUSED(nDepth); + UNUSED(nDelay); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::SetXBassParameters( UINT nDepth, UINT nRange ) { + UNUSED(nDepth); + UNUSED(nRange); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::SetSurroundParameters( UINT nDepth, UINT nDelay ) { + UNUSED(nDepth); + UNUSED(nDelay); + mpcpplog(); + return TRUE; +} + +UINT CSoundFile::GetMaxPosition() const { + mpcpplog(); + // rows in original, just use seconds here + if ( mod ) return static_cast( mod->get_duration_seconds() + 0.5 ); + return 0; +} + +DWORD CSoundFile::GetLength( BOOL bAdjust, BOOL bTotal ) { + UNUSED(bAdjust); + UNUSED(bTotal); + mpcpplog(); + if ( mod ) return static_cast( mod->get_duration_seconds() + 0.5 ); + return 0; +} + +UINT CSoundFile::GetSongComments( LPSTR s, UINT cbsize, UINT linesize ) { + UNUSED(linesize); + mpcpplog(); + if ( !s ) { + return 0; + } + if ( cbsize <= 0 ) { + return 0; + } + if ( !mod ) { + s[0] = '\0'; + return 1; + } + std::strncpy( s, mod->get_metadata("message").c_str(), cbsize ); + s[ cbsize - 1 ] = '\0'; + return static_cast( std::strlen( s ) + 1 ); +} + +UINT CSoundFile::GetRawSongComments( LPSTR s, UINT cbsize, UINT linesize ) { + UNUSED(linesize); + mpcpplog(); + if ( !s ) { + return 0; + } + if ( cbsize <= 0 ) { + return 0; + } + if ( !mod ) { + s[0] = '\0'; + return 1; + } + std::strncpy( s, mod->get_metadata("message_raw").c_str(), cbsize ); + s[ cbsize - 1 ] = '\0'; + return static_cast( std::strlen( s ) + 1 ); +} + +void CSoundFile::SetCurrentPos( UINT nPos ) { + mpcpplog(); + if ( mod ) mod->set_position_seconds( nPos ); + update_state(); +} + +UINT CSoundFile::GetCurrentPos() const { + mpcpplog(); + if ( mod ) return static_cast( mod->get_position_seconds() + 0.5 ); + return 0; +} + +static int get_stereo_separation() { + mpcpplog(); + return CSoundFile::m_nStereoSeparation * 100 / 128; +} + +static int get_filter_length() { + mpcpplog(); + if ( ( CSoundFile::gdwSoundSetup & (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE) ) == (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE) ) { + return 8; + } else if ( ( CSoundFile::gdwSoundSetup & SNDMIX_HQRESAMPLER ) == SNDMIX_HQRESAMPLER ) { + return 4; + } else if ( ( CSoundFile::gdwSoundSetup & SNDMIX_NORESAMPLING ) == SNDMIX_NORESAMPLING ) { + return 1; + } else { + return 2; + } +} + +static std::size_t get_sample_size() { + return (CSoundFile::gnBitsPerSample/8); +} + +static std::size_t get_num_channels() { + return CSoundFile::gnChannels; +} + +static std::size_t get_frame_size() { + return get_sample_size() * get_num_channels(); +} + +static int get_samplerate() { + return CSoundFile::gdwMixingFreq; +} + +UINT CSoundFile::Read( LPVOID lpBuffer, UINT cbBuffer ) { + mpcpplog(); + if ( !mod ) { + return 0; + } + mpcpplog(); + if ( !lpBuffer ) { + return 0; + } + mpcpplog(); + if ( cbBuffer <= 0 ) { + return 0; + } + mpcpplog(); + if ( get_samplerate() <= 0 ) { + return 0; + } + mpcpplog(); + if ( get_sample_size() != 1 && get_sample_size() != 2 && get_sample_size() != 4 ) { + return 0; + } + mpcpplog(); + if ( get_num_channels() != 1 && get_num_channels() != 2 && get_num_channels() != 4 ) { + return 0; + } + mpcpplog(); + std::memset( lpBuffer, 0, cbBuffer ); + const std::size_t frames_torender = cbBuffer / get_frame_size(); + short * out = reinterpret_cast( lpBuffer ); + std::vector tmpbuf; + if ( get_sample_size() == 1 || get_sample_size() == 4 ) { + tmpbuf.resize( frames_torender * get_num_channels() ); + out = &tmpbuf[0]; + } + + mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, get_stereo_separation() ); + mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, get_filter_length() ); + std::size_t frames_rendered = 0; + if ( get_num_channels() == 1 ) { + frames_rendered = mod->read( get_samplerate(), frames_torender, out ); + } else if ( get_num_channels() == 4 ) { + frames_rendered = mod->read_interleaved_quad( get_samplerate(), frames_torender, out ); + } else { + frames_rendered = mod->read_interleaved_stereo( get_samplerate(), frames_torender, out ); + } + + if ( get_sample_size() == 1 ) { + unsigned char * dst = reinterpret_cast( lpBuffer ); + for ( std::size_t sample = 0; sample < frames_rendered * get_num_channels(); ++sample ) { + dst[sample] = ( tmpbuf[sample] / 0x100 ) + 0x80; + } + } else if ( get_sample_size() == 4 ) { + int * dst = reinterpret_cast( lpBuffer ); + for ( std::size_t sample = 0; sample < frames_rendered * get_num_channels(); ++sample ) { + dst[sample] = tmpbuf[sample] << (32-16-1-MIXING_ATTENUATION); + } + } + update_state(); + return static_cast( frames_rendered ); +} + + +/* + +gstreamer modplug calls: + +mSoundFile->Create +mSoundFile->Destroy + +mSoundFile->SetWaveConfig +mSoundFile->SetWaveConfigEx +mSoundFile->SetResamplingMode +mSoundFile->SetSurroundParameters +mSoundFile->SetXBassParameters +mSoundFile->SetReverbParameters + +mSoundFile->GetMaxPosition (inline, -> GetLength) +mSoundFile->GetSongTime + +mSoundFile->GetTitle (inline) +mSoundFile->GetSongComments + +mSoundFile->SetCurrentPos +mSoundFile->Read + +mSoundFile->GetCurrentPos +mSoundFile->GetMusicTempo (inline) + +*/ + + +// really very internal symbols, probably nothing calls these directly + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wunused-parameter" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4100) +#endif + +BOOL CSoundFile::ReadNote() { mpcpplog(); return 0; } +BOOL CSoundFile::ProcessRow() { mpcpplog(); return 0; } +BOOL CSoundFile::ProcessEffects() { mpcpplog(); return 0; } +UINT CSoundFile::GetNNAChannel(UINT nChn) const { mpcpplog(); return 0; } +void CSoundFile::CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut) { mpcpplog(); } +void CSoundFile::NoteChange(UINT nChn, int note, BOOL bPorta, BOOL bResetEnv) { mpcpplog(); } +void CSoundFile::InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta,BOOL bUpdVol,BOOL bResetEnv) { mpcpplog(); } +void CSoundFile::PortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::PortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FinePortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FinePortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::TonePortamento(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::Vibrato(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FineVibrato(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::VolumeSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::PanningSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::ChannelVolSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FineVolumeUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FineVolumeDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::Tremolo(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::Panbrello(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::RetrigNote(UINT nChn, UINT param) { mpcpplog(); } +void CSoundFile::NoteCut(UINT nChn, UINT nTick) { mpcpplog(); } +void CSoundFile::KeyOff(UINT nChn) { mpcpplog(); } +int CSoundFile::PatternLoop(MODCHANNEL *, UINT param) { mpcpplog(); return 0; } +void CSoundFile::ExtendedMODCommands(UINT nChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtendedS3MCommands(UINT nChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtendedChannelEffect(MODCHANNEL *, UINT param) { mpcpplog(); } +void CSoundFile::ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param) { mpcpplog(); } +void CSoundFile::SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier) const { mpcpplog(); } +void CSoundFile::DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide) { mpcpplog(); } +void CSoundFile::SetTempo(UINT param) { mpcpplog(); } +void CSoundFile::SetSpeed(UINT param) { mpcpplog(); } +void CSoundFile::GlobalVolSlide(UINT param) { mpcpplog(); } +DWORD CSoundFile::IsSongFinished(UINT nOrder, UINT nRow) const { mpcpplog(); return 0; } +BOOL CSoundFile::IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const { mpcpplog(); return 0; } +UINT CSoundFile::PackSample(int &sample, int next) { mpcpplog(); return 0; } +BOOL CSoundFile::CanPackSample(LPSTR pSample, UINT nLen, UINT nPacking, BYTE *result) { mpcpplog(); return 0; } +UINT CSoundFile::ReadSample(MODINSTRUMENT *pIns, UINT nFlags, LPCSTR pMemFile, DWORD dwMemLength) { mpcpplog(); return 0; } +BOOL CSoundFile::DestroySample(UINT nSample) { mpcpplog(); return 0; } +BOOL CSoundFile::DestroyInstrument(UINT nInstr) { mpcpplog(); return 0; } +BOOL CSoundFile::IsSampleUsed(UINT nSample) { mpcpplog(); return 0; } +BOOL CSoundFile::IsInstrumentUsed(UINT nInstr) { mpcpplog(); return 0; } +BOOL CSoundFile::RemoveInstrumentSamples(UINT nInstr) { mpcpplog(); return 0; } +UINT CSoundFile::DetectUnusedSamples(BOOL *) { mpcpplog(); return 0; } +BOOL CSoundFile::RemoveSelectedSamples(BOOL *) { mpcpplog(); return 0; } +void CSoundFile::AdjustSampleLoop(MODINSTRUMENT *pIns) { mpcpplog(); } +BOOL CSoundFile::ReadInstrumentFromSong(UINT nInstr, CSoundFile *, UINT nSrcInstrument) { mpcpplog(); return 0; } +BOOL CSoundFile::ReadSampleFromSong(UINT nSample, CSoundFile *, UINT nSrcSample) { mpcpplog(); return 0; } +UINT CSoundFile::GetNoteFromPeriod(UINT period) const { mpcpplog(); return 0; } +UINT CSoundFile::GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const { mpcpplog(); return 0; } +UINT CSoundFile::GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac) const { mpcpplog(); return 0; } +void CSoundFile::ResetMidiCfg() { mpcpplog(); } +UINT CSoundFile::MapMidiInstrument(DWORD dwProgram, UINT nChannel, UINT nNote) { mpcpplog(); return 0; } +BOOL CSoundFile::ITInstrToMPT(const void *p, INSTRUMENTHEADER *penv, UINT trkvers) { mpcpplog(); return 0; } +UINT CSoundFile::SaveMixPlugins(FILE *f, BOOL bUpdate) { mpcpplog(); return 0; } +UINT CSoundFile::LoadMixPlugins(const void *pData, UINT nLen) { mpcpplog(); return 0; } +#ifndef NO_FILTER +DWORD CSoundFile::CutOffToFrequency(UINT nCutOff, int flt_modifier) const { mpcpplog(); return 0; } +#endif +DWORD CSoundFile::TransposeToFrequency(int transp, int ftune) { mpcpplog(); return 0; } +int CSoundFile::FrequencyToTranspose(DWORD freq) { mpcpplog(); return 0; } +void CSoundFile::FrequencyToTranspose(MODINSTRUMENT *psmp) { mpcpplog(); } +MODCOMMAND *CSoundFile::AllocatePattern(UINT rows, UINT nchns) { mpcpplog(); return 0; } +signed char* CSoundFile::AllocateSample(UINT nbytes) { mpcpplog(); return 0; } +void CSoundFile::FreePattern(LPVOID pat) { mpcpplog(); } +void CSoundFile::FreeSample(LPVOID p) { mpcpplog(); } +UINT CSoundFile::Normalize24BitBuffer(LPBYTE pbuffer, UINT cbsizebytes, DWORD lmax24, DWORD dwByteInc) { mpcpplog(); return 0; } + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + + +#endif // NO_LIBMODPLUG diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/m4/ax_cxx_compile_stdcxx.m4 b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 000000000..0b6cb3a7d --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,972 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016, 2018 Krzesimir Nowak +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 9 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus <= 201402L + +#error "This is not a C++17 compiler" + +#else + +#if defined(__clang__) + #define REALLY_CLANG +#else + #if defined(__GNUC__) + #define REALLY_GCC + #endif +#endif + +#include +#include +#include + +namespace cxx17 +{ + +#if !defined(REALLY_CLANG) + namespace test_constexpr_lambdas + { + + // TODO: test it with clang++ from git + + constexpr int foo = [](){return 42;}(); + + } +#endif // !defined(REALLY_CLANG) + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + +#if !defined(REALLY_CLANG) + namespace test_template_argument_deduction_for_class_templates + { + + // TODO: test it with clang++ from git + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } +#endif // !defined(REALLY_CLANG) + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + +#if !defined(REALLY_CLANG) + namespace test_structured_bindings + { + + // TODO: test it with clang++ from git + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } +#endif // !defined(REALLY_CLANG) + +#if !defined(REALLY_CLANG) + namespace test_exception_spec_type_system + { + + // TODO: test it with clang++ from git + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } +#endif // !defined(REALLY_CLANG) + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus <= 201402L + +]]) diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/test.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/test.sh new file mode 100755 index 000000000..7df7ed3fb --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.8.5/test.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +./autogen.sh + +./configure +make +make distcheck +make distclean diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE new file mode 100644 index 000000000..ae92400c3 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2004-2021, OpenMPT contributors +Copyright (c) 1997-2003, Olivier Lapicque +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the OpenMPT project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/Makefile.am b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/Makefile.am new file mode 100644 index 000000000..9f4f96516 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/Makefile.am @@ -0,0 +1,46 @@ + +ACLOCAL_AMFLAGS = -I m4 --install +EXTRA_DIST = +EXTRA_DIST += LICENSE +EXTRA_DIST += libopenmpt_modplug.pc.in +EXTRA_DIST += libmodplug.pc.in +EXTRA_DIST += test.sh +MOSTLYCLEANFILES = + +dist_doc_DATA = +dist_doc_DATA += LICENSE +nobase_dist_doc_DATA = + +bin_PROGRAMS = +check_PROGRAMS = +lib_LTLIBRARIES = + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = +nobase_include_HEADERS = + +if ENABLE_LIBOPENMPT_MODPLUG +lib_LTLIBRARIES += libopenmpt_modplug.la +libopenmpt_modplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined +libopenmpt_modplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS) +libopenmpt_modplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS) +libopenmpt_modplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS) +libopenmpt_modplug_la_LIBADD = $(LIBOPENMPT_LIBS) +libopenmpt_modplug_la_SOURCES = +libopenmpt_modplug_la_SOURCES += libopenmpt_modplug.c +libopenmpt_modplug_la_SOURCES += libopenmpt_modplug_cpp.cpp +endif + +if ENABLE_LIBMODPLUG +pkgconfig_DATA += libmodplug.pc +lib_LTLIBRARIES += libmodplug.la +libmodplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined +nobase_include_HEADERS += libmodplug/modplug.h libmodplug/sndfile.h libmodplug/stdafx.h +libmodplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS) +libmodplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS) +libmodplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS) +libmodplug_la_LIBADD = $(LIBOPENMPT_LIBS) +libmodplug_la_SOURCES = +libmodplug_la_SOURCES += libopenmpt_modplug.c +libmodplug_la_SOURCES += libopenmpt_modplug_cpp.cpp +endif diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/autogen.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/autogen.sh new file mode 100755 index 000000000..05ff21447 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/autogen.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +autoreconf -i diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/clean.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/clean.sh new file mode 100755 index 000000000..fac1804fb --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +./autogen.sh +./configure +make distclean diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/config.h.in b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/config.h.in new file mode 100644 index 000000000..b72c3f925 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/config.h.in @@ -0,0 +1,81 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* define if the compiler supports basic C++11 syntax */ +#undef HAVE_CXX11 + +/* define if the compiler supports basic C++14 syntax */ +#undef HAVE_CXX14 + +/* define if the compiler supports basic C++17 syntax */ +#undef HAVE_CXX17 + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/configure.ac b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/configure.ac new file mode 100644 index 000000000..341700686 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/configure.ac @@ -0,0 +1,66 @@ +AC_INIT([libopenmpt-modplug], [0.8.9.0-openmpt1], [https://bugs.openmpt.org/], [libopenmpt-modplug], [https://lib.openmpt.org/]) +AC_PREREQ([2.68]) + +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_FILES([Makefile libopenmpt_modplug.pc libmodplug.pc]) + +AM_INIT_AUTOMAKE([1.11 -Wall -Werror foreign subdir-objects]) + +AM_PROG_AR + +LT_INIT + +AC_SYS_LARGEFILE + +PKG_PROG_PKG_CONFIG([0.24]) +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CXX +AC_PROG_INSTALL + +LIBOPENMPT_REQUIRES_PRIVATE= +LIBOPENMPT_LIBS_PRIVATE= + +# We want a modern C compiler +AC_PROG_CC_STDC +#AC_PROG_CC_C99 + +# We need C++11 support +AX_CXX_COMPILE_STDCXX(17, [noext], [optional]) +AS_IF([test "x$HAVE_CXX17" != "x1"], + [ + AX_CXX_COMPILE_STDCXX(14, [noext], [optional]) + AS_IF([test "x$HAVE_CXX14" != "x1"], + [ + AX_CXX_COMPILE_STDCXX(11, [noext], [mandatory]) + ],[] + ) + ],[] +) + +AC_LANG_PUSH([C]) +AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CFLAGS="$CFLAGS -fvisibility=hidden -DSYM_VISIBILITY"]) +AX_CFLAGS_WARN_ALL +AC_LANG_POP([C]) + +AC_LANG_PUSH([C++]) +AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CXXFLAGS="$CXXFLAGS -fvisibility=hidden -DSYM_VISIBILITY"]) +AX_CXXFLAGS_WARN_ALL +AC_LANG_POP([C++]) + +PKG_CHECK_MODULES([LIBOPENMPT], [libopenmpt]) + +# libmodplug emulation +AC_ARG_ENABLE([libopenmpt_modplug], AS_HELP_STRING([--disable-libopenmpt_modplug], [Disable the libopenmpt_modplug emulation library of the libmodplug interface.])) +AM_CONDITIONAL([ENABLE_LIBOPENMPT_MODPLUG], [test "x$enable_libopenmpt_modplug" != "xno"]) + +# libmodplug replacement +AC_ARG_ENABLE([libmodplug], AS_HELP_STRING([--enable-libmodplug], [Enable libmodplug replacement library based on libopenmpt. +WARNING: This will replace your current libmodplug installation. +CAUTION: The emulation of the libmodplug interface is not complete as libmodplug exposes lots of internal implementation details. If any of those is used by an application, the emulation via libopenmpt will fail and/or crash. +])) +AM_CONDITIONAL([ENABLE_LIBMODPLUG], [test "x$enable_libmodplug" = "xyes"]) + +AC_OUTPUT diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/dist.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/dist.sh new file mode 100755 index 000000000..e96826f07 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/dist.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e + +./test.sh + +./autogen.sh +./configure +make dist diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug.pc.in b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug.pc.in new file mode 100644 index 000000000..e8c796d1d --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=${prefix}/include + +Name: libmodplug +Description: The ModPlug mod file playing library (emulated via libopenmpt). +Version: @PACKAGE_VERSION@ +Requires.private: libopenmpt +Libs: -L${libdir} -lmodplug +Libs.private: +Cflags: -I${includedir} + diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/modplug.h b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/modplug.h new file mode 100644 index 000000000..62dd1f42c --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/modplug.h @@ -0,0 +1,185 @@ +/* + * This source code is public domain. + * + * Authors: Kenton Varda (C interface wrapper) + */ + +#ifndef MODPLUG_MODPLUG_H +#define MODPLUG_MODPLUG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +# if defined(MODPLUG_BUILD) && defined(DLL_EXPORT) /* building libmodplug as a dll for windows */ +# define MODPLUG_EXPORT __declspec(dllexport) +# elif defined(MODPLUG_BUILD) || defined(MODPLUG_STATIC) /* building or using static libmodplug for windows */ +# define MODPLUG_EXPORT +# else +# define MODPLUG_EXPORT __declspec(dllimport) /* using libmodplug dll for windows */ +# endif +#elif defined(MODPLUG_BUILD) && defined(SYM_VISIBILITY) +# define MODPLUG_EXPORT __attribute__((visibility("default"))) +#else +#define MODPLUG_EXPORT +#endif + +struct _ModPlugFile; +typedef struct _ModPlugFile ModPlugFile; + +struct _ModPlugNote { + unsigned char Note; + unsigned char Instrument; + unsigned char VolumeEffect; + unsigned char Effect; + unsigned char Volume; + unsigned char Parameter; +}; +typedef struct _ModPlugNote ModPlugNote; + +typedef void (*ModPlugMixerProc)(int*, unsigned long, unsigned long); + +/* Load a mod file. [data] should point to a block of memory containing the complete + * file, and [size] should be the size of that block. + * Return the loaded mod file on success, or NULL on failure. */ +MODPLUG_EXPORT ModPlugFile* ModPlug_Load(const void* data, int size); +/* Unload a mod file. */ +MODPLUG_EXPORT void ModPlug_Unload(ModPlugFile* file); + +/* Read sample data into the buffer. Returns the number of bytes read. If the end + * of the mod has been reached, zero is returned. */ +MODPLUG_EXPORT int ModPlug_Read(ModPlugFile* file, void* buffer, int size); + +/* Get the name of the mod. The returned buffer is stored within the ModPlugFile + * structure and will remain valid until you unload the file. */ +MODPLUG_EXPORT const char* ModPlug_GetName(ModPlugFile* file); + +/* Get the length of the mod, in milliseconds. Note that this result is not always + * accurate, especially in the case of mods with loops. */ +MODPLUG_EXPORT int ModPlug_GetLength(ModPlugFile* file); + +/* Seek to a particular position in the song. Note that seeking and MODs don't mix very + * well. Some mods will be missing instruments for a short time after a seek, as ModPlug + * does not scan the sequence backwards to find out which instruments were supposed to be + * playing at that time. (Doing so would be difficult and not very reliable.) Also, + * note that seeking is not very exact in some mods -- especially those for which + * ModPlug_GetLength() does not report the full length. */ +MODPLUG_EXPORT void ModPlug_Seek(ModPlugFile* file, int millisecond); + +enum _ModPlug_Flags +{ + MODPLUG_ENABLE_OVERSAMPLING = 1 << 0, /* Enable oversampling (*highly* recommended) */ + MODPLUG_ENABLE_NOISE_REDUCTION = 1 << 1, /* Enable noise reduction */ + MODPLUG_ENABLE_REVERB = 1 << 2, /* Enable reverb */ + MODPLUG_ENABLE_MEGABASS = 1 << 3, /* Enable megabass */ + MODPLUG_ENABLE_SURROUND = 1 << 4 /* Enable surround sound. */ +}; + +enum _ModPlug_ResamplingMode +{ + MODPLUG_RESAMPLE_NEAREST = 0, /* No interpolation (very fast, extremely bad sound quality) */ + MODPLUG_RESAMPLE_LINEAR = 1, /* Linear interpolation (fast, good quality) */ + MODPLUG_RESAMPLE_SPLINE = 2, /* Cubic spline interpolation (high quality) */ + MODPLUG_RESAMPLE_FIR = 3 /* 8-tap fir filter (extremely high quality) */ +}; + +typedef struct _ModPlug_Settings +{ + int mFlags; /* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed */ + + /* Note that ModPlug always decodes sound at 44100kHz, 32 bit, stereo and then + * down-mixes to the settings you choose. */ + int mChannels; /* Number of channels - 1 for mono or 2 for stereo */ + int mBits; /* Bits per sample - 8, 16, or 32 */ + int mFrequency; /* Sampling rate - 11025, 22050, or 44100 */ + int mResamplingMode; /* One of MODPLUG_RESAMPLE_*, above */ + + int mStereoSeparation; /* Stereo separation, 1 - 256 */ + int mMaxMixChannels; /* Maximum number of mixing channels (polyphony), 32 - 256 */ + + int mReverbDepth; /* Reverb level 0(quiet)-100(loud) */ + int mReverbDelay; /* Reverb delay in ms, usually 40-200ms */ + int mBassAmount; /* XBass level 0(quiet)-100(loud) */ + int mBassRange; /* XBass cutoff in Hz 10-100 */ + int mSurroundDepth; /* Surround level 0(quiet)-100(heavy) */ + int mSurroundDelay; /* Surround delay in ms, usually 5-40ms */ + int mLoopCount; /* Number of times to loop. Zero prevents looping. + * -1 loops forever. */ +} ModPlug_Settings; + +/* Get and set the mod decoder settings. All options, except for channels, bits-per-sample, + * sampling rate, and loop count, will take effect immediately. Those options which don't + * take effect immediately will take effect the next time you load a mod. */ +MODPLUG_EXPORT void ModPlug_GetSettings(ModPlug_Settings* settings); +MODPLUG_EXPORT void ModPlug_SetSettings(const ModPlug_Settings* settings); + +/* New ModPlug API Functions */ +/* NOTE: Master Volume (1-512) */ +MODPLUG_EXPORT unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ; +MODPLUG_EXPORT void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ; + +MODPLUG_EXPORT int ModPlug_GetCurrentSpeed(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentTempo(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentOrder(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentPattern(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentRow(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetPlayingChannels(ModPlugFile* file); + +MODPLUG_EXPORT void ModPlug_SeekOrder(ModPlugFile* file,int order); +MODPLUG_EXPORT int ModPlug_GetModuleType(ModPlugFile* file); +MODPLUG_EXPORT char* ModPlug_GetMessage(ModPlugFile* file); + +#define MODPLUG_NO_FILESAVE /* experimental yet. must match stdafx.h. */ +#ifndef MODPLUG_NO_FILESAVE +/* + * EXPERIMENTAL Export Functions + */ +/*Export to a Scream Tracker 3 S3M module. EXPERIMENTAL (only works on Little-Endian platforms)*/ +MODPLUG_EXPORT char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath); + +/*Export to a Extended Module (XM). EXPERIMENTAL (only works on Little-Endian platforms)*/ +MODPLUG_EXPORT char ModPlug_ExportXM(ModPlugFile* file, const char* filepath); + +/*Export to a Amiga MOD file. EXPERIMENTAL.*/ +MODPLUG_EXPORT char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath); + +/*Export to a Impulse Tracker IT file. Should work OK in Little-Endian & Big-Endian platforms :-) */ +MODPLUG_EXPORT char ModPlug_ExportIT(ModPlugFile* file, const char* filepath); +#endif /* MODPLUG_NO_FILESAVE */ + +MODPLUG_EXPORT unsigned int ModPlug_NumInstruments(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_NumSamples(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_NumPatterns(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_NumChannels(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff); +MODPLUG_EXPORT unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff); + +/* + * Retrieve pattern note-data + */ +MODPLUG_EXPORT ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows); + +/* + * ================= + * Mixer callback + * ================= + * + * Use this callback if you want to 'modify' the mixed data of LibModPlug. + * + * void proc(int* buffer,unsigned long channels,unsigned long nsamples) ; + * + * 'buffer': A buffer of mixed samples + * 'channels': N. of channels in the buffer + * 'nsamples': N. of samples in the buffeer (without taking care of n.channels) + * + * (Samples are signed 32-bit integers) + */ +MODPLUG_EXPORT void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ; +MODPLUG_EXPORT void ModPlug_UnloadMixerCallback(ModPlugFile* file) ; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/sndfile.h b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/sndfile.h new file mode 100644 index 000000000..f390e4144 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/sndfile.h @@ -0,0 +1,1028 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque , + * Adam Goode (endian and char fixes for PPC) +*/ + +#if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED) +#include "config.h" +#define CONFIG_H_INCLUDED 1 +#endif + +#ifndef MODPLUG_SNDFILE_H +#define MODPLUG_SNDFILE_H + +#ifdef UNDER_CE +int _strnicmp(const char *str1,const char *str2, int n); +#endif + +#ifndef LPCBYTE +typedef const BYTE * LPCBYTE; +#endif + +#define MOD_AMIGAC2 0x1AB +#define MAX_SAMPLE_LENGTH 16000000 +#define MAX_SAMPLE_RATE 192000 +#define MAX_ORDERS 256 +#define MAX_PATTERNS 240 +#define MAX_SAMPLES 240 +#define MAX_INSTRUMENTS MAX_SAMPLES +#ifdef MODPLUG_FASTSOUNDLIB +#define MAX_CHANNELS 80 +#else +#define MAX_CHANNELS 128 +#endif +#define MAX_BASECHANNELS 64 +#define MAX_ENVPOINTS 32 +#define MIN_PERIOD 0x0020 +#define MAX_PERIOD 0xFFFF +#define MAX_PATTERNNAME 32 +#define MAX_CHANNELNAME 20 +#define MAX_INFONAME 80 +#define MAX_EQ_BANDS 6 +#define MAX_MIXPLUGINS 8 + + +#define MOD_TYPE_NONE 0x00 +#define MOD_TYPE_MOD 0x01 +#define MOD_TYPE_S3M 0x02 +#define MOD_TYPE_XM 0x04 +#define MOD_TYPE_MED 0x08 +#define MOD_TYPE_MTM 0x10 +#define MOD_TYPE_IT 0x20 +#define MOD_TYPE_669 0x40 +#define MOD_TYPE_ULT 0x80 +#define MOD_TYPE_STM 0x100 +#define MOD_TYPE_FAR 0x200 +#define MOD_TYPE_WAV 0x400 +#define MOD_TYPE_AMF 0x800 +#define MOD_TYPE_AMS 0x1000 +#define MOD_TYPE_DSM 0x2000 +#define MOD_TYPE_MDL 0x4000 +#define MOD_TYPE_OKT 0x8000 +#define MOD_TYPE_MID 0x10000 +#define MOD_TYPE_DMF 0x20000 +#define MOD_TYPE_PTM 0x40000 +#define MOD_TYPE_DBM 0x80000 +#define MOD_TYPE_MT2 0x100000 +#define MOD_TYPE_AMF0 0x200000 +#define MOD_TYPE_PSM 0x400000 +#define MOD_TYPE_J2B 0x800000 +#define MOD_TYPE_ABC 0x1000000 +#define MOD_TYPE_PAT 0x2000000 +#define MOD_TYPE_UMX 0x80000000 // Fake type +#define MAX_MODTYPE 24 + + + +// Channel flags: +// Bits 0-7: Sample Flags +#define CHN_16BIT 0x01 +#define CHN_LOOP 0x02 +#define CHN_PINGPONGLOOP 0x04 +#define CHN_SUSTAINLOOP 0x08 +#define CHN_PINGPONGSUSTAIN 0x10 +#define CHN_PANNING 0x20 +#define CHN_STEREO 0x40 +#define CHN_PINGPONGFLAG 0x80 +// Bits 8-31: Channel Flags +#define CHN_MUTE 0x100 +#define CHN_KEYOFF 0x200 +#define CHN_NOTEFADE 0x400 +#define CHN_SURROUND 0x800 +#define CHN_NOIDO 0x1000 +#define CHN_HQSRC 0x2000 +#define CHN_FILTER 0x4000 +#define CHN_VOLUMERAMP 0x8000 +#define CHN_VIBRATO 0x10000 +#define CHN_TREMOLO 0x20000 +#define CHN_PANBRELLO 0x40000 +#define CHN_PORTAMENTO 0x80000 +#define CHN_GLISSANDO 0x100000 +#define CHN_VOLENV 0x200000 +#define CHN_PANENV 0x400000 +#define CHN_PITCHENV 0x800000 +#define CHN_FASTVOLRAMP 0x1000000 +#define CHN_EXTRALOUD 0x2000000 +#define CHN_REVERB 0x4000000 +#define CHN_NOREVERB 0x8000000 + + +#define ENV_VOLUME 0x0001 +#define ENV_VOLSUSTAIN 0x0002 +#define ENV_VOLLOOP 0x0004 +#define ENV_PANNING 0x0008 +#define ENV_PANSUSTAIN 0x0010 +#define ENV_PANLOOP 0x0020 +#define ENV_PITCH 0x0040 +#define ENV_PITCHSUSTAIN 0x0080 +#define ENV_PITCHLOOP 0x0100 +#define ENV_SETPANNING 0x0200 +#define ENV_FILTER 0x0400 +#define ENV_VOLCARRY 0x0800 +#define ENV_PANCARRY 0x1000 +#define ENV_PITCHCARRY 0x2000 + +#define CMD_NONE 0 +#define CMD_ARPEGGIO 1 +#define CMD_PORTAMENTOUP 2 +#define CMD_PORTAMENTODOWN 3 +#define CMD_TONEPORTAMENTO 4 +#define CMD_VIBRATO 5 +#define CMD_TONEPORTAVOL 6 +#define CMD_VIBRATOVOL 7 +#define CMD_TREMOLO 8 +#define CMD_PANNING8 9 +#define CMD_OFFSET 10 +#define CMD_VOLUMESLIDE 11 +#define CMD_POSITIONJUMP 12 +#define CMD_VOLUME 13 +#define CMD_PATTERNBREAK 14 +#define CMD_RETRIG 15 +#define CMD_SPEED 16 +#define CMD_TEMPO 17 +#define CMD_TREMOR 18 +#define CMD_MODCMDEX 19 +#define CMD_S3MCMDEX 20 +#define CMD_CHANNELVOLUME 21 +#define CMD_CHANNELVOLSLIDE 22 +#define CMD_GLOBALVOLUME 23 +#define CMD_GLOBALVOLSLIDE 24 +#define CMD_KEYOFF 25 +#define CMD_FINEVIBRATO 26 +#define CMD_PANBRELLO 27 +#define CMD_XFINEPORTAUPDOWN 28 +#define CMD_PANNINGSLIDE 29 +#define CMD_SETENVPOSITION 30 +#define CMD_MIDI 31 + + +// Volume Column commands +#define VOLCMD_VOLUME 1 +#define VOLCMD_PANNING 2 +#define VOLCMD_VOLSLIDEUP 3 +#define VOLCMD_VOLSLIDEDOWN 4 +#define VOLCMD_FINEVOLUP 5 +#define VOLCMD_FINEVOLDOWN 6 +#define VOLCMD_VIBRATOSPEED 7 +#define VOLCMD_VIBRATO 8 +#define VOLCMD_PANSLIDELEFT 9 +#define VOLCMD_PANSLIDERIGHT 10 +#define VOLCMD_TONEPORTAMENTO 11 +#define VOLCMD_PORTAUP 12 +#define VOLCMD_PORTADOWN 13 + +#define RSF_16BIT 0x04 +#define RSF_STEREO 0x08 + +#define RS_PCM8S 0 // 8-bit signed +#define RS_PCM8U 1 // 8-bit unsigned +#define RS_PCM8D 2 // 8-bit delta values +#define RS_ADPCM4 3 // 4-bit ADPCM-packed +#define RS_PCM16D 4 // 16-bit delta values +#define RS_PCM16S 5 // 16-bit signed +#define RS_PCM16U 6 // 16-bit unsigned +#define RS_PCM16M 7 // 16-bit motorola order +#define RS_STPCM8S (RS_PCM8S|RSF_STEREO) // stereo 8-bit signed +#define RS_STPCM8U (RS_PCM8U|RSF_STEREO) // stereo 8-bit unsigned +#define RS_STPCM8D (RS_PCM8D|RSF_STEREO) // stereo 8-bit delta values +#define RS_STPCM16S (RS_PCM16S|RSF_STEREO) // stereo 16-bit signed +#define RS_STPCM16U (RS_PCM16U|RSF_STEREO) // stereo 16-bit unsigned +#define RS_STPCM16D (RS_PCM16D|RSF_STEREO) // stereo 16-bit delta values +#define RS_STPCM16M (RS_PCM16M|RSF_STEREO) // stereo 16-bit signed big endian +// IT 2.14 compressed samples +#define RS_IT2148 0x10 +#define RS_IT21416 0x14 +#define RS_IT2158 0x12 +#define RS_IT21516 0x16 +// AMS Packed Samples +#define RS_AMS8 0x11 +#define RS_AMS16 0x15 +// DMF Huffman compression +#define RS_DMF8 0x13 +#define RS_DMF16 0x17 +// MDL Huffman compression +#define RS_MDL8 0x20 +#define RS_MDL16 0x24 +#define RS_PTM8DTO16 0x25 +// Stereo Interleaved Samples +#define RS_STIPCM8S (RS_PCM8S|0x40|RSF_STEREO) // stereo 8-bit signed +#define RS_STIPCM8U (RS_PCM8U|0x40|RSF_STEREO) // stereo 8-bit unsigned +#define RS_STIPCM16S (RS_PCM16S|0x40|RSF_STEREO) // stereo 16-bit signed +#define RS_STIPCM16U (RS_PCM16U|0x40|RSF_STEREO) // stereo 16-bit unsigned +#define RS_STIPCM16M (RS_PCM16M|0x40|RSF_STEREO) // stereo 16-bit signed big endian +// 24-bit signed +#define RS_PCM24S (RS_PCM16S|0x80) // mono 24-bit signed +#define RS_STIPCM24S (RS_PCM16S|0x80|RSF_STEREO) // stereo 24-bit signed +#define RS_PCM32S (RS_PCM16S|0xC0) // mono 24-bit signed +#define RS_STIPCM32S (RS_PCM16S|0xC0|RSF_STEREO) // stereo 24-bit signed + +// NNA types +#define NNA_NOTECUT 0 +#define NNA_CONTINUE 1 +#define NNA_NOTEOFF 2 +#define NNA_NOTEFADE 3 + +// DCT types +#define DCT_NONE 0 +#define DCT_NOTE 1 +#define DCT_SAMPLE 2 +#define DCT_INSTRUMENT 3 + +// DNA types +#define DNA_NOTECUT 0 +#define DNA_NOTEOFF 1 +#define DNA_NOTEFADE 2 + +// Mixer Hardware-Dependent features +#define SYSMIX_ENABLEMMX 0x01 +#define SYSMIX_WINDOWSNT 0x02 +#define SYSMIX_SLOWCPU 0x04 +#define SYSMIX_FASTCPU 0x08 + +// Module flags +#define SONG_EMBEDMIDICFG 0x0001 +#define SONG_FASTVOLSLIDES 0x0002 +#define SONG_ITOLDEFFECTS 0x0004 +#define SONG_ITCOMPATMODE 0x0008 +#define SONG_LINEARSLIDES 0x0010 +#define SONG_PATTERNLOOP 0x0020 +#define SONG_STEP 0x0040 +#define SONG_PAUSED 0x0080 +#define SONG_FADINGSONG 0x0100 +#define SONG_ENDREACHED 0x0200 +#define SONG_GLOBALFADE 0x0400 +#define SONG_CPUVERYHIGH 0x0800 +#define SONG_FIRSTTICK 0x1000 +#define SONG_MPTFILTERMODE 0x2000 +#define SONG_SURROUNDPAN 0x4000 +#define SONG_EXFILTERRANGE 0x8000 +#define SONG_AMIGALIMITS 0x10000 + +// Global Options (Renderer) +#define SNDMIX_REVERSESTEREO 0x0001 +#define SNDMIX_NOISEREDUCTION 0x0002 +#define SNDMIX_AGC 0x0004 +#define SNDMIX_NORESAMPLING 0x0008 +#define SNDMIX_HQRESAMPLER 0x0010 +#define SNDMIX_MEGABASS 0x0020 +#define SNDMIX_SURROUND 0x0040 +#define SNDMIX_REVERB 0x0080 +#define SNDMIX_EQ 0x0100 +#define SNDMIX_SOFTPANNING 0x0200 +#define SNDMIX_ULTRAHQSRCMODE 0x0400 +// Misc Flags (can safely be turned on or off) +#define SNDMIX_DIRECTTODISK 0x10000 +#define SNDMIX_ENABLEMMX 0x20000 +#define SNDMIX_NOBACKWARDJUMPS 0x40000 +#define SNDMIX_MAXDEFAULTPAN 0x80000 // Used by the MOD loader + + +// Reverb Types (GM2 Presets) +enum { + REVERBTYPE_SMALLROOM, + REVERBTYPE_MEDIUMROOM, + REVERBTYPE_LARGEROOM, + REVERBTYPE_SMALLHALL, + REVERBTYPE_MEDIUMHALL, + REVERBTYPE_LARGEHALL, + NUM_REVERBTYPES +}; + + +enum { + SRCMODE_NEAREST, + SRCMODE_LINEAR, + SRCMODE_SPLINE, + SRCMODE_POLYPHASE, + NUM_SRC_MODES +}; + + +// Sample Struct +typedef struct _MODINSTRUMENT +{ + UINT nLength,nLoopStart,nLoopEnd; + UINT nSustainStart, nSustainEnd; + signed char *pSample; + UINT nC4Speed; + WORD nPan; + WORD nVolume; + WORD nGlobalVol; + WORD uFlags; + signed char RelativeTone; + signed char nFineTune; + BYTE nVibType; + BYTE nVibSweep; + BYTE nVibDepth; + BYTE nVibRate; + CHAR name[22]; +} MODINSTRUMENT; + + +// Instrument Struct +typedef struct _INSTRUMENTHEADER +{ + UINT nFadeOut; + DWORD dwFlags; + WORD nGlobalVol; + WORD nPan; + WORD VolPoints[MAX_ENVPOINTS]; + WORD PanPoints[MAX_ENVPOINTS]; + WORD PitchPoints[MAX_ENVPOINTS]; + BYTE VolEnv[MAX_ENVPOINTS]; + BYTE PanEnv[MAX_ENVPOINTS]; + BYTE PitchEnv[MAX_ENVPOINTS]; + BYTE Keyboard[128]; + BYTE NoteMap[128]; + + BYTE nVolEnv; + BYTE nPanEnv; + BYTE nPitchEnv; + BYTE nVolLoopStart; + BYTE nVolLoopEnd; + BYTE nVolSustainBegin; + BYTE nVolSustainEnd; + BYTE nPanLoopStart; + BYTE nPanLoopEnd; + BYTE nPanSustainBegin; + BYTE nPanSustainEnd; + BYTE nPitchLoopStart; + BYTE nPitchLoopEnd; + BYTE nPitchSustainBegin; + BYTE nPitchSustainEnd; + BYTE nNNA; + BYTE nDCT; + BYTE nDNA; + BYTE nPanSwing; + BYTE nVolSwing; + BYTE nIFC; + BYTE nIFR; + WORD wMidiBank; + BYTE nMidiProgram; + BYTE nMidiChannel; + BYTE nMidiDrumKey; + signed char nPPS; + unsigned char nPPC; + CHAR name[32]; + CHAR filename[12]; +} INSTRUMENTHEADER; + + +// Channel Struct +typedef struct _MODCHANNEL +{ + // First 32-bytes: Most used mixing information: don't change it + signed char * pCurrentSample; + DWORD nPos; + DWORD nPosLo; // actually 16-bit + LONG nInc; // 16.16 + LONG nRightVol; + LONG nLeftVol; + LONG nRightRamp; + LONG nLeftRamp; + // 2nd cache line + DWORD nLength; + DWORD dwFlags; + DWORD nLoopStart; + DWORD nLoopEnd; + LONG nRampRightVol; + LONG nRampLeftVol; + LONG nFilter_Y1, nFilter_Y2, nFilter_Y3, nFilter_Y4; + LONG nFilter_A0, nFilter_B0, nFilter_B1; + LONG nROfs, nLOfs; + LONG nRampLength; + // Information not used in the mixer + signed char * pSample; + LONG nNewRightVol, nNewLeftVol; + LONG nRealVolume, nRealPan; + LONG nVolume, nPan, nFadeOutVol; + LONG nPeriod, nC4Speed, nPortamentoDest; + INSTRUMENTHEADER *pHeader; + MODINSTRUMENT *pInstrument; + DWORD nVolEnvPosition, nPanEnvPosition, nPitchEnvPosition; + DWORD nMasterChn, nVUMeter; + LONG nGlobalVol, nInsVol; + LONG nFineTune, nTranspose; + LONG nPortamentoSlide, nAutoVibDepth; + UINT nAutoVibPos, nVibratoPos, nTremoloPos, nPanbrelloPos; + // 16-bit members + signed short nVolSwing, nPanSwing; + // 8-bit members + BYTE nNote, nNNA; + BYTE nNewNote, nNewIns, nCommand, nArpeggio; + BYTE nOldVolumeSlide, nOldFineVolUpDown; + BYTE nOldPortaUpDown, nOldFinePortaUpDown; + BYTE nOldPanSlide, nOldChnVolSlide; + BYTE nVibratoType, nVibratoSpeed, nVibratoDepth; + BYTE nTremoloType, nTremoloSpeed, nTremoloDepth; + BYTE nPanbrelloType, nPanbrelloSpeed, nPanbrelloDepth; + BYTE nOldCmdEx, nOldVolParam, nOldTempo; + BYTE nOldOffset, nOldHiOffset; + BYTE nCutOff, nResonance; + BYTE nRetrigCount, nRetrigParam; + BYTE nTremorCount, nTremorParam; + BYTE nPatternLoop, nPatternLoopCount; + BYTE nRowNote, nRowInstr; + BYTE nRowVolCmd, nRowVolume; + BYTE nRowCommand, nRowParam; + BYTE nLeftVU, nRightVU; + BYTE nActiveMacro, nPadding; +} MODCHANNEL; + + +typedef struct _MODCHANNELSETTINGS +{ + UINT nPan; + UINT nVolume; + DWORD dwFlags; + UINT nMixPlugin; + char szName[MAX_CHANNELNAME]; // changed from CHAR +} MODCHANNELSETTINGS; + + +typedef struct _MODCOMMAND +{ + BYTE note; + BYTE instr; + BYTE volcmd; + BYTE command; + BYTE vol; + BYTE param; +} MODCOMMAND, *LPMODCOMMAND; + +//////////////////////////////////////////////////////////////////// +// Mix Plugins +#define MIXPLUG_MIXREADY 0x01 // Set when cleared + +class MODPLUG_EXPORT IMixPlugin +{ +public: + virtual ~IMixPlugin() {}; + virtual int AddRef() = 0; + virtual int Release() = 0; + virtual void SaveAllParameters() = 0; + virtual void RestoreAllParameters() = 0; + virtual void Process(float *pOutL, float *pOutR, unsigned long nSamples) = 0; + virtual void Init(unsigned long nFreq, int bReset) = 0; + virtual void MidiSend(DWORD dwMidiCode) = 0; + virtual void MidiCommand(UINT nMidiCh, UINT nMidiProg, UINT note, UINT vol) = 0; +}; + + +#define MIXPLUG_INPUTF_MASTEREFFECT 0x01 // Apply to master mix +#define MIXPLUG_INPUTF_BYPASS 0x02 // Bypass effect +#define MIXPLUG_INPUTF_WETMIX 0x04 // Wet Mix (dry added) + +typedef struct _SNDMIXPLUGINSTATE +{ + DWORD dwFlags; // MIXPLUG_XXXX + LONG nVolDecayL, nVolDecayR; // Buffer click removal + int *pMixBuffer; // Stereo effect send buffer + float *pOutBufferL; // Temp storage for int -> float conversion + float *pOutBufferR; +} SNDMIXPLUGINSTATE, *PSNDMIXPLUGINSTATE; + +typedef struct _SNDMIXPLUGININFO +{ + DWORD dwPluginId1; + DWORD dwPluginId2; + DWORD dwInputRouting; // MIXPLUG_INPUTF_XXXX + DWORD dwOutputRouting; // 0=mix 0x80+=fx + DWORD dwReserved[4]; // Reserved for routing info + CHAR szName[32]; + CHAR szLibraryName[64]; // original DLL name +} SNDMIXPLUGININFO, *PSNDMIXPLUGININFO; // Size should be 128 + +typedef struct _SNDMIXPLUGIN +{ + IMixPlugin *pMixPlugin; + PSNDMIXPLUGINSTATE pMixState; + ULONG nPluginDataSize; + PVOID pPluginData; + SNDMIXPLUGININFO Info; +} SNDMIXPLUGIN, *PSNDMIXPLUGIN; + +typedef BOOL (*PMIXPLUGINCREATEPROC)(PSNDMIXPLUGIN); + +//////////////////////////////////////////////////////////////////// + +enum { + MIDIOUT_START=0, + MIDIOUT_STOP, + MIDIOUT_TICK, + MIDIOUT_NOTEON, + MIDIOUT_NOTEOFF, + MIDIOUT_VOLUME, + MIDIOUT_PAN, + MIDIOUT_BANKSEL, + MIDIOUT_PROGRAM, +}; + + +typedef struct MODMIDICFG +{ + char szMidiGlb[9*32]; // changed from CHAR + char szMidiSFXExt[16*32]; // changed from CHAR + char szMidiZXXExt[128*32]; // changed from CHAR +} MODMIDICFG, *LPMODMIDICFG; + +#define NOTE_MAX 120 //Defines maximum notevalue as well as maximum number of notes. + +typedef VOID (* LPSNDMIXHOOKPROC)(int *, unsigned long, unsigned long); // buffer, samples, channels + + + +//============== +class MODPLUG_EXPORT CSoundFile +//============== +{ +public: // Static Members + static UINT m_nXBassDepth; + static UINT m_nXBassRange; + static UINT m_nReverbDepth; + static UINT m_nReverbDelay; + static UINT gnReverbType; + static UINT m_nProLogicDepth; + static UINT m_nProLogicDelay; + static UINT m_nStereoSeparation; + static UINT m_nMaxMixChannels; + static LONG m_nStreamVolume; + static DWORD gdwSysInfo; + static DWORD gdwSoundSetup; + static DWORD gdwMixingFreq; + static DWORD gnBitsPerSample; + static DWORD gnChannels; + static UINT gnAGC; + static UINT gnVolumeRampSamples; + static UINT gnVUMeter; + static UINT gnCPUUsage; + static LPSNDMIXHOOKPROC gpSndMixHook; + static PMIXPLUGINCREATEPROC gpMixPluginCreateProc; + +public: // for Editing + MODCHANNEL Chn[MAX_CHANNELS]; // Channels + UINT ChnMix[MAX_CHANNELS]; // Channels to be mixed + MODINSTRUMENT Ins[MAX_SAMPLES]; // Instruments + INSTRUMENTHEADER *Headers[MAX_INSTRUMENTS]; // Instrument Headers + MODCHANNELSETTINGS ChnSettings[MAX_BASECHANNELS]; // Channels settings + MODCOMMAND *Patterns[MAX_PATTERNS]; // Patterns + WORD PatternSize[MAX_PATTERNS]; // Patterns Lengths + BYTE Order[MAX_ORDERS]; // Pattern Orders + MODMIDICFG m_MidiCfg; // Midi macro config table + SNDMIXPLUGIN m_MixPlugins[MAX_MIXPLUGINS]; // Mix plugins + UINT m_nDefaultSpeed, m_nDefaultTempo, m_nDefaultGlobalVolume; + DWORD m_dwSongFlags; // Song flags SONG_XXXX + UINT m_nChannels, m_nMixChannels, m_nMixStat, m_nBufferCount; + UINT m_nType, m_nSamples, m_nInstruments; + UINT m_nTickCount, m_nTotalCount, m_nPatternDelay, m_nFrameDelay; + UINT m_nMusicSpeed, m_nMusicTempo; + UINT m_nNextRow, m_nRow, m_nNextStartRow; + UINT m_nPattern,m_nCurrentPattern,m_nNextPattern,m_nRestartPos; + UINT m_nMasterVolume, m_nGlobalVolume, m_nSongPreAmp; + UINT m_nFreqFactor, m_nTempoFactor, m_nOldGlbVolSlide; + LONG m_nMinPeriod, m_nMaxPeriod, m_nRepeatCount, m_nInitialRepeatCount; + DWORD m_nGlobalFadeSamples, m_nGlobalFadeMaxSamples; + UINT m_nMaxOrderPosition; + UINT m_nPatternNames; + LPSTR m_lpszSongComments, m_lpszPatternNames; + char m_szNames[MAX_INSTRUMENTS][32]; // changed from CHAR + CHAR CompressionTable[16]; + +public: + CSoundFile(); + ~CSoundFile(); + +public: + BOOL Create(LPCBYTE lpStream, DWORD dwMemLength=0); + BOOL Destroy(); + UINT GetType() const { return m_nType; } + UINT GetNumChannels() const; + UINT GetLogicalChannels() const { return m_nChannels; } + BOOL SetMasterVolume(UINT vol, BOOL bAdjustAGC=FALSE); + UINT GetMasterVolume() const { return m_nMasterVolume; } + UINT GetNumPatterns() const; + UINT GetNumInstruments() const; + UINT GetNumSamples() const { return m_nSamples; } + UINT GetCurrentPos() const; + UINT GetCurrentPattern() const { return m_nPattern; } + UINT GetCurrentOrder() const { return m_nCurrentPattern; } + UINT GetSongComments(LPSTR s, UINT cbsize, UINT linesize=32); + UINT GetRawSongComments(LPSTR s, UINT cbsize, UINT linesize=32); + UINT GetMaxPosition() const; + void SetCurrentPos(UINT nPos); + void SetCurrentOrder(UINT nOrder); + void GetTitle(LPSTR s) const { lstrcpyn(s,m_szNames[0],32); } + LPCSTR GetTitle() const { return m_szNames[0]; } + UINT GetSampleName(UINT nSample,LPSTR s=NULL) const; + UINT GetInstrumentName(UINT nInstr,LPSTR s=NULL) const; + UINT GetMusicSpeed() const { return m_nMusicSpeed; } + UINT GetMusicTempo() const { return m_nMusicTempo; } + DWORD GetLength(BOOL bAdjust, BOOL bTotal=FALSE); + DWORD GetSongTime() { return GetLength(FALSE, TRUE); } + void SetRepeatCount(int n) { m_nRepeatCount = n; m_nInitialRepeatCount = n; } + int GetRepeatCount() const { return m_nRepeatCount; } + BOOL IsPaused() const { return (m_dwSongFlags & SONG_PAUSED) ? TRUE : FALSE; } + void LoopPattern(int nPat, int nRow=0); + void CheckCPUUsage(UINT nCPU); + BOOL SetPatternName(UINT nPat, LPCSTR lpszName); + BOOL GetPatternName(UINT nPat, LPSTR lpszName, UINT cbSize=MAX_PATTERNNAME) const; + // Module Loaders + BOOL ReadXM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadS3M(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMod(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMed(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadSTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadIT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL Read669(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadUlt(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadWav(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDSM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadFAR(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMS(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMDL(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadOKT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDMF(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDBM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMF(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMT2(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPSM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadJ2B(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadUMX(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadABC(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestABC(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMID(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestMID(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPAT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestPAT(LPCBYTE lpStream, DWORD dwMemLength); + // Save Functions +#ifndef MODPLUG_NO_FILESAVE + UINT WriteSample(FILE *f, MODINSTRUMENT *pins, UINT nFlags, UINT nMaxLen=0); + BOOL SaveXM(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveS3M(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveMod(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveIT(LPCSTR lpszFileName, UINT nPacking=0); +#endif // MODPLUG_NO_FILESAVE + // MOD Convert function + UINT GetBestSaveFormat() const; + UINT GetSaveFormats() const; + void ConvertModCommand(MODCOMMAND *) const; + void S3MConvert(MODCOMMAND *m, BOOL bIT) const; + void S3MSaveConvert(UINT *pcmd, UINT *pprm, BOOL bIT) const; + WORD ModSaveCommand(const MODCOMMAND *m, BOOL bXM) const; + +public: + // Real-time sound functions + VOID ResetChannels(); + + UINT Read(LPVOID lpBuffer, UINT cbBuffer); + UINT CreateStereoMix(int count); + BOOL FadeSong(UINT msec); + BOOL GlobalFadeSong(UINT msec); + UINT GetTotalTickCount() const { return m_nTotalCount; } + VOID ResetTotalTickCount() { m_nTotalCount = 0; } + +public: + // Mixer Config + static BOOL InitPlayer(BOOL bReset=FALSE); + static BOOL SetMixConfig(UINT nStereoSeparation, UINT nMaxMixChannels); + static BOOL SetWaveConfig(UINT nRate,UINT nBits,UINT nChannels,BOOL bMMX=FALSE); + static BOOL SetResamplingMode(UINT nMode); // SRCMODE_XXXX + static BOOL IsStereo() { return (gnChannels > 1) ? TRUE : FALSE; } + static DWORD GetSampleRate() { return gdwMixingFreq; } + static DWORD GetBitsPerSample() { return gnBitsPerSample; } + static DWORD InitSysInfo(); + static DWORD GetSysInfo() { return gdwSysInfo; } + // AGC + static BOOL GetAGC() { return (gdwSoundSetup & SNDMIX_AGC) ? TRUE : FALSE; } + static void SetAGC(BOOL b); + static void ResetAGC(); + static void ProcessAGC(int count); + + //GCCFIX -- added these functions back in! + static BOOL SetWaveConfigEx(BOOL bSurround,BOOL bNoOverSampling,BOOL bReverb,BOOL hqido,BOOL bMegaBass,BOOL bNR,BOOL bEQ); + // DSP Effects + static void InitializeDSP(BOOL bReset); + static void ProcessStereoDSP(int count); + static void ProcessMonoDSP(int count); + // [Reverb level 0(quiet)-100(loud)], [delay in ms, usually 40-200ms] + static BOOL SetReverbParameters(UINT nDepth, UINT nDelay); + // [XBass level 0(quiet)-100(loud)], [cutoff in Hz 10-100] + static BOOL SetXBassParameters(UINT nDepth, UINT nRange); + // [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-40ms] + static BOOL SetSurroundParameters(UINT nDepth, UINT nDelay); +public: + BOOL ReadNote(); + BOOL ProcessRow(); + BOOL ProcessEffects(); + UINT GetNNAChannel(UINT nChn) const; + void CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut); + void NoteChange(UINT nChn, int note, BOOL bPorta=FALSE, BOOL bResetEnv=TRUE); + void InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta=FALSE,BOOL bUpdVol=TRUE,BOOL bResetEnv=TRUE); + // Channel Effects + void PortamentoUp(MODCHANNEL *pChn, UINT param); + void PortamentoDown(MODCHANNEL *pChn, UINT param); + void FinePortamentoUp(MODCHANNEL *pChn, UINT param); + void FinePortamentoDown(MODCHANNEL *pChn, UINT param); + void ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param); + void ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param); + void TonePortamento(MODCHANNEL *pChn, UINT param); + void Vibrato(MODCHANNEL *pChn, UINT param); + void FineVibrato(MODCHANNEL *pChn, UINT param); + void VolumeSlide(MODCHANNEL *pChn, UINT param); + void PanningSlide(MODCHANNEL *pChn, UINT param); + void ChannelVolSlide(MODCHANNEL *pChn, UINT param); + void FineVolumeUp(MODCHANNEL *pChn, UINT param); + void FineVolumeDown(MODCHANNEL *pChn, UINT param); + void Tremolo(MODCHANNEL *pChn, UINT param); + void Panbrello(MODCHANNEL *pChn, UINT param); + void RetrigNote(UINT nChn, UINT param); + void NoteCut(UINT nChn, UINT nTick); + void KeyOff(UINT nChn); + int PatternLoop(MODCHANNEL *, UINT param); + void ExtendedMODCommands(UINT nChn, UINT param); + void ExtendedS3MCommands(UINT nChn, UINT param); + void ExtendedChannelEffect(MODCHANNEL *, UINT param); + void ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param=0); + void SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier=256) const; + // Low-Level effect processing + void DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide); + // Global Effects + void SetTempo(UINT param); + void SetSpeed(UINT param); + void GlobalVolSlide(UINT param); + DWORD IsSongFinished(UINT nOrder, UINT nRow) const; + BOOL IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const; + // Read/Write sample functions + signed char GetDeltaValue(signed char prev, UINT n) const { return (signed char)(prev + CompressionTable[n & 0x0F]); } + UINT PackSample(int &sample, int next); + BOOL CanPackSample(LPSTR pSample, UINT nLen, UINT nPacking, BYTE *result=NULL); + UINT ReadSample(MODINSTRUMENT *pIns, UINT nFlags, LPCSTR pMemFile, DWORD dwMemLength); + BOOL DestroySample(UINT nSample); + BOOL DestroyInstrument(UINT nInstr); + BOOL IsSampleUsed(UINT nSample); + BOOL IsInstrumentUsed(UINT nInstr); + BOOL RemoveInstrumentSamples(UINT nInstr); + UINT DetectUnusedSamples(BOOL *); + BOOL RemoveSelectedSamples(BOOL *); + void AdjustSampleLoop(MODINSTRUMENT *pIns); + // I/O from another sound file + BOOL ReadInstrumentFromSong(UINT nInstr, CSoundFile *, UINT nSrcInstrument); + BOOL ReadSampleFromSong(UINT nSample, CSoundFile *, UINT nSrcSample); + // Period/Note functions + UINT GetNoteFromPeriod(UINT period) const; + UINT GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const; + UINT GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac=0) const; + // Misc functions + MODINSTRUMENT *GetSample(UINT n) { return Ins+n; } + void ResetMidiCfg(); + UINT MapMidiInstrument(DWORD dwProgram, UINT nChannel, UINT nNote); + BOOL ITInstrToMPT(const void *p, INSTRUMENTHEADER *penv, UINT trkvers); + UINT SaveMixPlugins(FILE *f=NULL, BOOL bUpdate=TRUE); + UINT LoadMixPlugins(const void *pData, UINT nLen); +#ifndef NO_FILTER + DWORD CutOffToFrequency(UINT nCutOff, int flt_modifier=256) const; // [0-255] => [1-10KHz] +#endif + + // Static helper functions +public: + static DWORD TransposeToFrequency(int transp, int ftune=0); + static int FrequencyToTranspose(DWORD freq); + static void FrequencyToTranspose(MODINSTRUMENT *psmp); + + // System-Dependant functions +public: + static MODCOMMAND *AllocatePattern(UINT rows, UINT nchns); + static signed char* AllocateSample(UINT nbytes); + static void FreePattern(LPVOID pat); + static void FreeSample(LPVOID p); + static UINT Normalize24BitBuffer(LPBYTE pbuffer, UINT cbsizebytes, DWORD lmax24, DWORD dwByteInc); +}; + + +// inline DWORD BigEndian(DWORD x) { return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24); } +// inline WORD BigEndianW(WORD x) { return (WORD)(((x >> 8) & 0xFF) | ((x << 8) & 0xFF00)); } + + +////////////////////////////////////////////////////////// +// WAVE format information + +#pragma pack(1) + +// Standard IFF chunks IDs +#define IFFID_FORM 0x4d524f46 +#define IFFID_RIFF 0x46464952 +#define IFFID_WAVE 0x45564157 +#define IFFID_LIST 0x5453494C +#define IFFID_INFO 0x4F464E49 + +// IFF Info fields +#define IFFID_ICOP 0x504F4349 +#define IFFID_IART 0x54524149 +#define IFFID_IPRD 0x44525049 +#define IFFID_INAM 0x4D414E49 +#define IFFID_ICMT 0x544D4349 +#define IFFID_IENG 0x474E4549 +#define IFFID_ISFT 0x54465349 +#define IFFID_ISBJ 0x4A425349 +#define IFFID_IGNR 0x524E4749 +#define IFFID_ICRD 0x44524349 + +// Wave IFF chunks IDs +#define IFFID_wave 0x65766177 +#define IFFID_fmt 0x20746D66 +#define IFFID_wsmp 0x706D7377 +#define IFFID_pcm 0x206d6370 +#define IFFID_data 0x61746164 +#define IFFID_smpl 0x6C706D73 +#define IFFID_xtra 0x61727478 + +typedef struct WAVEFILEHEADER +{ + DWORD id_RIFF; // "RIFF" + DWORD filesize; // file length-8 + DWORD id_WAVE; +} WAVEFILEHEADER; + + +typedef struct WAVEFORMATHEADER +{ + DWORD id_fmt; // "fmt " + DWORD hdrlen; // 16 + WORD format; // 1 + WORD channels; // 1:mono, 2:stereo + DWORD freqHz; // sampling freq + DWORD bytessec; // bytes/sec=freqHz*samplesize + WORD samplesize; // sizeof(sample) + WORD bitspersample; // bits per sample (8/16) +} WAVEFORMATHEADER; + + +typedef struct WAVEDATAHEADER +{ + DWORD id_data; // "data" + DWORD length; // length of data +} WAVEDATAHEADER; + + +typedef struct WAVESMPLHEADER +{ + // SMPL + DWORD smpl_id; // "smpl" -> 0x6C706D73 + DWORD smpl_len; // length of smpl: 3Ch (54h with sustain loop) + DWORD dwManufacturer; + DWORD dwProduct; + DWORD dwSamplePeriod; // 1000000000/freqHz + DWORD dwBaseNote; // 3Ch = C-4 -> 60 + RelativeTone + DWORD dwPitchFraction; + DWORD dwSMPTEFormat; + DWORD dwSMPTEOffset; + DWORD dwSampleLoops; // number of loops + DWORD cbSamplerData; +} WAVESMPLHEADER; + + +typedef struct SAMPLELOOPSTRUCT +{ + DWORD dwIdentifier; + DWORD dwLoopType; // 0=normal, 1=bidi + DWORD dwLoopStart; + DWORD dwLoopEnd; // Byte offset ? + DWORD dwFraction; + DWORD dwPlayCount; // Loop Count, 0=infinite +} SAMPLELOOPSTRUCT; + + +typedef struct WAVESAMPLERINFO +{ + WAVESMPLHEADER wsiHdr; + SAMPLELOOPSTRUCT wsiLoops[2]; +} WAVESAMPLERINFO; + + +typedef struct WAVELISTHEADER +{ + DWORD list_id; // "LIST" -> 0x5453494C + DWORD list_len; + DWORD info; // "INFO" +} WAVELISTHEADER; + + +typedef struct WAVEEXTRAHEADER +{ + DWORD xtra_id; // "xtra" -> 0x61727478 + DWORD xtra_len; + DWORD dwFlags; + WORD wPan; + WORD wVolume; + WORD wGlobalVol; + WORD wReserved; + BYTE nVibType; + BYTE nVibSweep; + BYTE nVibDepth; + BYTE nVibRate; +} WAVEEXTRAHEADER; + +#pragma pack() + +/////////////////////////////////////////////////////////// +// Low-level Mixing functions + +#define MIXBUFFERSIZE 512 +#define MIXING_ATTENUATION 4 +#define MIXING_CLIPMIN (-0x08000000) +#define MIXING_CLIPMAX (0x07FFFFFF) +#define VOLUMERAMPPRECISION 12 +#define FADESONGDELAY 100 +#define EQ_BUFFERSIZE (MIXBUFFERSIZE) +#define AGC_PRECISION 9 +#define AGC_UNITY (1 << AGC_PRECISION) + +// Calling conventions +#ifdef MSC_VER +#define MPPASMCALL __cdecl +#define MPPFASTCALL __fastcall +#else +#define MPPASMCALL +#define MPPFASTCALL +#endif + +#define MOD2XMFineTune(k) ((int)( (signed char)((k)<<4) )) +#define XM2MODFineTune(k) ((int)( (k>>4)&0x0f )) + +int _muldiv(long a, long b, long c); +int _muldivr(long a, long b, long c); + + +// Byte swapping functions from the GNU C Library and libsdl + +/* Swap bytes in 16 bit value. */ +#ifdef __GNUC__ +# define bswap_16(x) \ + (__extension__ \ + ({ unsigned short int __bsx = (x); \ + ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); })) +#else +static __inline unsigned short int +bswap_16 (unsigned short int __bsx) +{ + return ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); +} +#endif + +/* Swap bytes in 32 bit value. */ +#ifdef __GNUC__ +# define bswap_32(x) \ + (__extension__ \ + ({ unsigned int __bsx = (x); \ + ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | \ + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); })) +#else +static __inline unsigned int +bswap_32 (unsigned int __bsx) +{ + return ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); +} +#endif + +#if (defined ARM) && (defined _WIN32_WCE) +static __inline unsigned short int +ARM_get16(const void *data) +{ + unsigned short int s; + memcpy(&s,data,sizeof(s)); + return s; +} + +static __inline unsigned int +ARM_get32(const void *data) +{ + unsigned int s; + memcpy(&s,data,sizeof(s)); + return s; +} + +#define bswapLE16(X) ARM_get16(&X) +#define bswapLE32(X) ARM_get32(&X) +#define bswapBE16(X) bswap_16(ARM_get16(&X)) +#define bswapBE32(X) bswap_32(ARM_get32(&X)) + +// From libsdl +#elif defined(WORDS_BIGENDIAN) && WORDS_BIGENDIAN +#define bswapLE16(X) bswap_16(X) +#define bswapLE32(X) bswap_32(X) +#define bswapBE16(X) (X) +#define bswapBE32(X) (X) +#else +#define bswapLE16(X) (X) +#define bswapLE32(X) (X) +#define bswapBE16(X) bswap_16(X) +#define bswapBE32(X) bswap_32(X) +#endif + +#endif diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/stdafx.h b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/stdafx.h new file mode 100644 index 000000000..5daea9182 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/stdafx.h @@ -0,0 +1,141 @@ +/* + * This source code is public domain. + * + * Authors: Rani Assaf , + * Olivier Lapicque , + * Adam Goode (endian and char fixes for PPC) + */ + +#ifndef MODPLUG_STDAFX_H +#define MODPLUG_STDAFX_H + +/* Autoconf detection of stdint/inttypes */ +#if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED) +# include "config.h" +# define CONFIG_H_INCLUDED 1 +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif + +/* disable AGC and FILESAVE for all targets for uniformity. */ +#define NO_AGC +#define MODPLUG_NO_FILESAVE + +#ifdef _WIN32 + +#ifdef MSC_VER +#pragma warning (disable:4201) +#pragma warning (disable:4514) +#endif + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include + +#define srandom(_seed) srand(_seed) +#define random() rand() +#define sleep(_ms) Sleep(_ms) + +inline void ProcessPlugins(int n) {} + +#undef strcasecmp +#undef strncasecmp +#define strcasecmp(a,b) _stricmp(a,b) +#define strncasecmp(a,b,c) _strnicmp(a,b,c) + +#define HAVE_SINF 1 + +#ifndef isblank +#define isblank(c) ((c) == ' ' || (c) == '\t') +#endif + +#else + +#include +#include +#include +#ifdef HAVE_MALLOC_H +#include +#endif + +typedef int8_t CHAR; +typedef uint8_t UCHAR; +typedef uint8_t* PUCHAR; +typedef uint16_t USHORT; +typedef uint32_t ULONG; +typedef uint32_t UINT; +typedef uint32_t DWORD; +typedef int32_t LONG; +typedef int64_t LONGLONG; +typedef int32_t* LPLONG; +typedef uint32_t* LPDWORD; +typedef uint16_t WORD; +typedef uint8_t BYTE; +typedef uint8_t* LPBYTE; +typedef bool BOOL; +typedef char* LPSTR; +typedef void* LPVOID; +typedef uint16_t* LPWORD; +typedef const char* LPCSTR; +typedef void* PVOID; +typedef void VOID; + +inline LONG MulDiv (long a, long b, long c) +{ +/*if (!c) return 0;*/ + return ((uint64_t) a * (uint64_t) b ) / c; +} + +#define LPCTSTR LPCSTR +#define lstrcpyn strncpy +#define lstrcpy strcpy +#define lstrcmp strcmp +#define wsprintf sprintf + +#define WAVE_FORMAT_PCM 1 + +#define GHND 0 +#define GlobalFreePtr(p) free((void *)(p)) +inline int8_t * GlobalAllocPtr(unsigned int, size_t size) +{ + int8_t * p = (int8_t *) malloc(size); + + if (p != NULL) memset(p, 0, size); + return p; +} + +inline void ProcessPlugins(int /* n */ ) {} + +#ifndef FALSE +#define FALSE false +#endif + +#ifndef TRUE +#define TRUE true +#endif + +#endif /* _WIN32 */ + +#if defined(_WIN32) || defined(__CYGWIN__) +# if defined(MODPLUG_BUILD) && defined(DLL_EXPORT) /* building libmodplug as a dll for windows */ +# define MODPLUG_EXPORT __declspec(dllexport) +# elif defined(MODPLUG_BUILD) || defined(MODPLUG_STATIC) /* building or using static libmodplug for windows */ +# define MODPLUG_EXPORT +# else +# define MODPLUG_EXPORT __declspec(dllimport) /* using libmodplug dll for windows */ +# endif +#elif defined(MODPLUG_BUILD) && defined(SYM_VISIBILITY) +# define MODPLUG_EXPORT __attribute__((visibility("default"))) +#else +#define MODPLUG_EXPORT +#endif + +#endif diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.c b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.c new file mode 100644 index 000000000..51dd3d79f --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.c @@ -0,0 +1,604 @@ +/* + * libopenmpt_modplug.c + * -------------------- + * Purpose: libopenmpt emulation of the libmodplug interface + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#ifndef NO_LIBMODPLUG + +#ifdef LIBOPENMPT_BUILD_DLL +#undef LIBOPENMPT_BUILD_DLL +#endif + +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#endif /* _MSC_VER */ + +#include + +#include +#include +#include +#include +#include +#include + +#define MODPLUG_BUILD +#ifdef _MSC_VER +#ifdef MPT_BUILD_MSVC_SHARED +#define DLL_EXPORT +#endif /* MPT_BUILD_MSVC_SHARED */ +#ifdef MPT_BUILD_MSVC_STATIC +#define MODPLUG_STATIC +#endif /* MPT_BUILD_MSVC_STATIC */ +#endif /* _MSC_VER */ +#ifdef _MSC_VER +#define LIBOPENMPT_MODPLUG_API +#else /* !_MSC_VER */ +#define LIBOPENMPT_MODPLUG_API LIBOPENMPT_API_HELPER_EXPORT +#endif /* _MSC_VER */ +#include "libmodplug/modplug.h" + +/* from libmodplug/sndfile.h */ +/* header is not c clean */ +#define MIXING_ATTENUATION 4 +#define MOD_TYPE_NONE 0x0 +#define MOD_TYPE_MOD 0x1 +#define MOD_TYPE_S3M 0x2 +#define MOD_TYPE_XM 0x4 +#define MOD_TYPE_MED 0x8 +#define MOD_TYPE_MTM 0x10 +#define MOD_TYPE_IT 0x20 +#define MOD_TYPE_669 0x40 +#define MOD_TYPE_ULT 0x80 +#define MOD_TYPE_STM 0x100 +#define MOD_TYPE_FAR 0x200 +#define MOD_TYPE_WAV 0x400 +#define MOD_TYPE_AMF 0x800 +#define MOD_TYPE_AMS 0x1000 +#define MOD_TYPE_DSM 0x2000 +#define MOD_TYPE_MDL 0x4000 +#define MOD_TYPE_OKT 0x8000 +#define MOD_TYPE_MID 0x10000 +#define MOD_TYPE_DMF 0x20000 +#define MOD_TYPE_PTM 0x40000 +#define MOD_TYPE_DBM 0x80000 +#define MOD_TYPE_MT2 0x100000 +#define MOD_TYPE_AMF0 0x200000 +#define MOD_TYPE_PSM 0x400000 +#define MOD_TYPE_J2B 0x800000 +#define MOD_TYPE_ABC 0x1000000 +#define MOD_TYPE_PAT 0x2000000 +#define MOD_TYPE_UMX 0x80000000 // Fake type + +#define BUFFER_COUNT 1024 + +struct _ModPlugFile { + openmpt_module* mod; + signed short* buf; + signed int* mixerbuf; + char* name; + char* message; + ModPlug_Settings settings; + ModPlugMixerProc mixerproc; + ModPlugNote** patterns; +}; + +static ModPlug_Settings globalsettings = { + MODPLUG_ENABLE_OVERSAMPLING|MODPLUG_ENABLE_NOISE_REDUCTION, + 2, + 16, + 44100, + MODPLUG_RESAMPLE_LINEAR, + 128, + 256, + 0, + 0, + 0, + 0, + 0, + 0, + 0 +}; + +static int32_t modplugresamplingmode_to_filterlength(int mode) +{ + if(mode<0){ + return 1; + } + switch(mode){ + case MODPLUG_RESAMPLE_NEAREST: return 1; break; + case MODPLUG_RESAMPLE_LINEAR: return 2; break; + case MODPLUG_RESAMPLE_SPLINE: return 4; break; + case MODPLUG_RESAMPLE_FIR: return 8; break; + } + return 8; +} + +LIBOPENMPT_MODPLUG_API ModPlugFile* ModPlug_Load(const void* data, int size) +{ + ModPlugFile* file = malloc(sizeof(ModPlugFile)); + const char* name = NULL; + const char* message = NULL; + if(!file) return NULL; + memset(file,0,sizeof(ModPlugFile)); + memcpy(&file->settings,&globalsettings,sizeof(ModPlug_Settings)); + file->mod = openmpt_module_create_from_memory2(data,size,NULL,NULL,NULL,NULL,NULL,NULL,NULL); + if(!file->mod){ + free(file); + return NULL; + } + file->buf = malloc(BUFFER_COUNT*sizeof(signed short)*4); + if(!file->buf){ + openmpt_module_destroy(file->mod); + free(file); + return NULL; + } + openmpt_module_set_repeat_count(file->mod,file->settings.mLoopCount); + name = openmpt_module_get_metadata(file->mod,"title"); + if(name){ + file->name = malloc(strlen(name)+1); + if(file->name){ + strcpy(file->name,name); + } + openmpt_free_string(name); + name = NULL; + }else{ + file->name = malloc(strlen("")+1); + if(file->name){ + strcpy(file->name,""); + } + } + message = openmpt_module_get_metadata(file->mod,"message"); + if(message){ + file->message = malloc(strlen(message)+1); + if(file->message){ + strcpy(file->message,message); + } + openmpt_free_string(message); + message = NULL; + }else{ + file->message = malloc(strlen("")+1); + if(file->message){ + strcpy(file->message,""); + } + } + openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT,file->settings.mStereoSeparation*100/128); + openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH,modplugresamplingmode_to_filterlength(file->settings.mResamplingMode)); + return file; +} + +LIBOPENMPT_MODPLUG_API void ModPlug_Unload(ModPlugFile* file) +{ + int p; + if(!file) return; + if(file->patterns){ + for(p=0;pmod);p++){ + if(file->patterns[p]){ + free(file->patterns[p]); + file->patterns[p] = NULL; + } + } + free(file->patterns); + file->patterns = NULL; + } + if(file->mixerbuf){ + free(file->mixerbuf); + file->mixerbuf = NULL; + } + openmpt_module_destroy(file->mod); + file->mod = NULL; + free(file->name); + file->name = NULL; + free(file->message); + file->message = NULL; + free(file->buf); + file->buf = NULL; + free(file); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_Read(ModPlugFile* file, void* buffer, int size) +{ + int framesize; + int framecount; + int frames; + int rendered; + int frame; + int channel; + int totalrendered; + signed short* in; + signed int* mixbuf; + unsigned char* buf8; + signed short* buf16; + signed int* buf32; + if(!file) return 0; + framesize = file->settings.mBits/8*file->settings.mChannels; + framecount = size/framesize; + buf8 = buffer; + buf16 = buffer; + buf32 = buffer; + totalrendered = 0; + while(framecount>0){ + frames = framecount; + if(frames>BUFFER_COUNT){ + frames = BUFFER_COUNT; + } + if(file->settings.mChannels==1){ + rendered = (int)openmpt_module_read_mono(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0]); + }else if(file->settings.mChannels==2){ + rendered = (int)openmpt_module_read_stereo(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0],&file->buf[frames*1]); + }else if(file->settings.mChannels==4){ + rendered = (int)openmpt_module_read_quad(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0],&file->buf[frames*1],&file->buf[frames*2],&file->buf[frames*3]); + }else{ + return 0; + } + in = file->buf; + if(file->mixerproc&&file->mixerbuf){ + mixbuf=file->mixerbuf; + for(frame=0;framesettings.mChannels;channel++){ + *mixbuf = in[frames*channel+frame]<<(32-16-1-MIXING_ATTENUATION); + mixbuf++; + } + } + file->mixerproc(file->mixerbuf,file->settings.mChannels*frames,file->settings.mChannels); + mixbuf=file->mixerbuf; + for(frame=0;framesettings.mChannels;channel++){ + in[frames*channel+frame] = *mixbuf>>(32-16-1-MIXING_ATTENUATION); + mixbuf++; + } + } + } + if(file->settings.mBits==8){ + for(frame=0;framesettings.mChannels;channel++){ + *buf8 = in[frames*channel+frame]/256+0x80; + buf8++; + } + } + }else if(file->settings.mBits==16){ + for(frame=0;framesettings.mChannels;channel++){ + *buf16 = in[frames*channel+frame]; + buf16++; + } + } + }else if(file->settings.mBits==32){ + for(frame=0;framesettings.mChannels;channel++){ + *buf32 = in[frames*channel+frame] << (32-16-1-MIXING_ATTENUATION); + buf32++; + } + } + }else{ + return 0; + } + totalrendered += rendered; + framecount -= frames; + if(!rendered) break; + } + memset(((char*)buffer)+totalrendered*framesize,0,size-totalrendered*framesize); + return totalrendered*framesize; +} + +LIBOPENMPT_MODPLUG_API const char* ModPlug_GetName(ModPlugFile* file) +{ + if(!file) return NULL; + return file->name; +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetLength(ModPlugFile* file) +{ + if(!file) return 0; + return (int)(openmpt_module_get_duration_seconds(file->mod)*1000.0); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_Seek(ModPlugFile* file, int millisecond) +{ + if(!file) return; + openmpt_module_set_position_seconds(file->mod,(double)millisecond*0.001); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_GetSettings(ModPlug_Settings* settings) +{ + if(!settings) return; + memcpy(settings,&globalsettings,sizeof(ModPlug_Settings)); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_SetSettings(const ModPlug_Settings* settings) +{ + if(!settings) return; + memcpy(&globalsettings,settings,sizeof(ModPlug_Settings)); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) +{ + int32_t val; + if(!file) return 0; + val = 0; + if(!openmpt_module_get_render_param(file->mod,OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL,&val)) return 128; + return (unsigned int)(128.0*pow(10.0,val*0.0005)); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) +{ + if(!file) return; + openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL,(int32_t)(2000.0*log10(cvol/128.0))); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentSpeed(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_speed(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentTempo(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_tempo(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentOrder(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_order(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentPattern(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_pattern(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentRow(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_row(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetPlayingChannels(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_playing_channels(file->mod); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_SeekOrder(ModPlugFile* file,int order) +{ + if(!file) return; + openmpt_module_set_position_order_row(file->mod,order,0); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetModuleType(ModPlugFile* file) +{ + const char* type; + int retval; + if(!file) return 0; + type = openmpt_module_get_metadata(file->mod,"type"); + retval = MOD_TYPE_NONE; + if(!type){ + return retval; + } + if(!strcmp(type,"mod")){ + retval = MOD_TYPE_MOD; + }else if(!strcmp(type,"s3m")){ + retval = MOD_TYPE_S3M; + }else if(!strcmp(type,"xm")){ + retval = MOD_TYPE_XM; + }else if(!strcmp(type,"med")){ + retval = MOD_TYPE_MED; + }else if(!strcmp(type,"mtm")){ + retval = MOD_TYPE_MTM; + }else if(!strcmp(type,"it")){ + retval = MOD_TYPE_IT; + }else if(!strcmp(type,"669")){ + retval = MOD_TYPE_669; + }else if(!strcmp(type,"ult")){ + retval = MOD_TYPE_ULT; + }else if(!strcmp(type,"stm")){ + retval = MOD_TYPE_STM; + }else if(!strcmp(type,"far")){ + retval = MOD_TYPE_FAR; + }else if(!strcmp(type,"s3m")){ + retval = MOD_TYPE_WAV; + }else if(!strcmp(type,"amf")){ + retval = MOD_TYPE_AMF; + }else if(!strcmp(type,"ams")){ + retval = MOD_TYPE_AMS; + }else if(!strcmp(type,"dsm")){ + retval = MOD_TYPE_DSM; + }else if(!strcmp(type,"mdl")){ + retval = MOD_TYPE_MDL; + }else if(!strcmp(type,"okt")){ + retval = MOD_TYPE_OKT; + }else if(!strcmp(type,"mid")){ + retval = MOD_TYPE_MID; + }else if(!strcmp(type,"dmf")){ + retval = MOD_TYPE_DMF; + }else if(!strcmp(type,"ptm")){ + retval = MOD_TYPE_PTM; + }else if(!strcmp(type,"dbm")){ + retval = MOD_TYPE_DBM; + }else if(!strcmp(type,"mt2")){ + retval = MOD_TYPE_MT2; + }else if(!strcmp(type,"amf0")){ + retval = MOD_TYPE_AMF0; + }else if(!strcmp(type,"psm")){ + retval = MOD_TYPE_PSM; + }else if(!strcmp(type,"j2b")){ + retval = MOD_TYPE_J2B; + }else if(!strcmp(type,"abc")){ + retval = MOD_TYPE_ABC; + }else if(!strcmp(type,"pat")){ + retval = MOD_TYPE_PAT; + }else if(!strcmp(type,"umx")){ + retval = MOD_TYPE_UMX; + }else{ + retval = MOD_TYPE_IT; /* fallback, most complex type */ + } + openmpt_free_string(type); + return retval; +} + +LIBOPENMPT_MODPLUG_API char* ModPlug_GetMessage(ModPlugFile* file) +{ + if(!file) return NULL; + return file->message; +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumInstruments(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_instruments(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumSamples(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_samples(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumPatterns(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_patterns(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumChannels(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_channels(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff) +{ + const char* str; + char buf[32]; + if(!file) return 0; + str = openmpt_module_get_sample_name(file->mod,qual-1); + memset(buf,0,32); + if(str){ + strncpy(buf,str,31); + openmpt_free_string(str); + } + if(buff){ + strncpy(buff,buf,32); + } + return (unsigned int)strlen(buf); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff) +{ + const char* str; + char buf[32]; + if(!file) return 0; + str = openmpt_module_get_instrument_name(file->mod,qual-1); + memset(buf,0,32); + if(str){ + strncpy(buf,str,31); + openmpt_free_string(str); + } + if(buff){ + strncpy(buff,buf,32); + } + return (unsigned int)strlen(buf); +} + +LIBOPENMPT_MODPLUG_API ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows) +{ + int c; + int r; + int numr; + int numc; + ModPlugNote note; + if(!file) return NULL; + if(numrows){ + *numrows = openmpt_module_get_pattern_num_rows(file->mod,pattern); + } + if(pattern<0||pattern>=openmpt_module_get_num_patterns(file->mod)){ + return NULL; + } + if(!file->patterns){ + file->patterns = malloc(sizeof(ModPlugNote*)*openmpt_module_get_pattern_num_rows(file->mod,pattern)); + if(!file->patterns) return NULL; + memset(file->patterns,0,sizeof(ModPlugNote*)*openmpt_module_get_pattern_num_rows(file->mod,pattern)); + } + if(!file->patterns[pattern]){ + file->patterns[pattern] = malloc(sizeof(ModPlugNote)*openmpt_module_get_pattern_num_rows(file->mod,pattern)*openmpt_module_get_num_channels(file->mod)); + if(!file->patterns[pattern]) return NULL; + memset(file->patterns[pattern],0,sizeof(ModPlugNote)*openmpt_module_get_pattern_num_rows(file->mod,pattern)*openmpt_module_get_num_channels(file->mod)); + } + numr = openmpt_module_get_pattern_num_rows(file->mod,pattern); + numc = openmpt_module_get_num_channels(file->mod); + for(r=0;rmod,pattern,r,c,OPENMPT_MODULE_COMMAND_NOTE); + note.Instrument = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_INSTRUMENT); + note.VolumeEffect = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_VOLUMEEFFECT); + note.Effect = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_EFFECT); + note.Volume = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_VOLUME); + note.Parameter = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_PARAMETER); + memcpy(&file->patterns[pattern][r*numc+c],¬e,sizeof(ModPlugNote)); + } + } + return file->patterns[pattern]; +} + +LIBOPENMPT_MODPLUG_API void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) +{ + if(!file) return; + if(!file->mixerbuf){ + file->mixerbuf = malloc(BUFFER_COUNT*sizeof(signed int)*4); + } + file->mixerproc = proc; +} + +LIBOPENMPT_MODPLUG_API void ModPlug_UnloadMixerCallback(ModPlugFile* file) +{ + if(!file) return; + file->mixerproc = NULL; + if(file->mixerbuf){ + free(file->mixerbuf); + file->mixerbuf = NULL; + } +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportS3M(%s) not implemented.\n",filepath); + return 0; +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportXM(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportXM(%s) not implemented.\n",filepath); + return 0; +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportMOD(%s) not implemented.\n",filepath); + return 0; +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportIT(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportIT(%s) not implemented.\n",filepath); + return 0; +} + +#endif /* NO_LIBMODPLUG */ diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.pc.in b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.pc.in new file mode 100644 index 000000000..89c1fee89 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=${prefix}/include + +Name: libopenmpt_modplug +Description: The ModPlug mod file playing library (emulated via libopenmpt). +Version: @PACKAGE_VERSION@ +Requires.private: libopenmpt +Libs: -L${libdir} -lopenmpt_modplug +Libs.private: +Cflags: -I${includedir} diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug_cpp.cpp b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug_cpp.cpp new file mode 100644 index 000000000..fd5375d3b --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug_cpp.cpp @@ -0,0 +1,887 @@ +/* + * libopenmpt_modplug_cpp.cpp + * -------------------------- + * Purpose: libopenmpt emulation of the libmodplug c++ interface + * Notes : WARNING! THIS IS A HACK! + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#ifndef NO_LIBMODPLUG + +/* + +*********************************************************************** +WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +*********************************************************************** + +This is a dirty hack to emulate just so much of the libmodplug c++ +interface so that the current known users (mainly xmms-modplug itself, +gstreamer modplug, audacious, and stuff based on those) work. This is +neither a complete nor a correct implementation. +Metadata and other state is not provided or updated. + +*/ + +#ifdef UNICODE +#undef UNICODE +#endif +#ifdef _UNICODE +#undef _UNICODE +#endif + +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#endif /* _MSC_VER */ + +#include + +#include +#include + +#include +#include +#include +#include + +#define MODPLUG_BUILD +#ifdef _MSC_VER +/* libmodplug C++ header is broken for MSVC DLL builds */ +#define MODPLUG_STATIC +#endif /* _MSC_VER */ +#ifdef _MSC_VER +#define LIBOPENMPT_MODPLUG_API +#else /* !_MSC_VER */ +#define LIBOPENMPT_MODPLUG_API LIBOPENMPT_API_HELPER_EXPORT +#endif /* _MSC_VER */ +class LIBOPENMPT_MODPLUG_API CSoundFile; +#include "libmodplug/stdafx.h" +#include "libmodplug/sndfile.h" + +namespace { +template +void Clear( T & x ) +{ + std::memset( &x, 0, sizeof(T) ); +} +} + +//#define mpcpplog() fprintf(stderr, "%s %i\n", __func__, __LINE__) +#define mpcpplog() do{}while(0) + +#define UNUSED(x) (void)((x)) + +union self_t { + CHAR CompressionTable[16]; + openmpt::module * self_; +}; + +static void set_self( CSoundFile * that, openmpt::module * self_ ) { + self_t self_union; + Clear(self_union); + self_union.self_ = self_; + std::memcpy( that->CompressionTable, self_union.CompressionTable, sizeof( self_union.CompressionTable ) ); +} + +static openmpt::module * get_self( const CSoundFile * that ) { + self_t self_union; + Clear(self_union); + std::memcpy( self_union.CompressionTable, that->CompressionTable, sizeof( self_union.CompressionTable ) ); + return self_union.self_; +} + +#define mod ( get_self( this ) ) + +#define update_state() \ + if ( mod ) m_nCurrentPattern = mod->get_current_order(); \ + if ( mod ) m_nPattern = mod->get_current_pattern(); \ + if ( mod ) m_nMusicSpeed = mod->get_current_speed(); \ + if ( mod ) m_nMusicTempo = mod->get_current_tempo(); \ +/**/ + +UINT CSoundFile::m_nXBassDepth = 0; +UINT CSoundFile::m_nXBassRange = 0; +UINT CSoundFile::m_nReverbDepth = 0; +UINT CSoundFile::m_nReverbDelay = 0; +UINT CSoundFile::gnReverbType = 0; +UINT CSoundFile::m_nProLogicDepth = 0; +UINT CSoundFile::m_nProLogicDelay = 0; +UINT CSoundFile::m_nStereoSeparation = 128; +UINT CSoundFile::m_nMaxMixChannels = 256; +LONG CSoundFile::m_nStreamVolume = 0x8000; +DWORD CSoundFile::gdwSysInfo = 0; +DWORD CSoundFile::gdwSoundSetup = 0; +DWORD CSoundFile::gdwMixingFreq = 44100; +DWORD CSoundFile::gnBitsPerSample = 16; +DWORD CSoundFile::gnChannels = 2; +UINT CSoundFile::gnAGC = 0; +UINT CSoundFile::gnVolumeRampSamples = 0; +UINT CSoundFile::gnVUMeter = 0; +UINT CSoundFile::gnCPUUsage = 0; +LPSNDMIXHOOKPROC CSoundFile::gpSndMixHook = 0; +PMIXPLUGINCREATEPROC CSoundFile::gpMixPluginCreateProc = 0; + +CSoundFile::CSoundFile() { + mpcpplog(); + Clear(Chn); + Clear(ChnMix); + Clear(Ins); + Clear(Headers); + Clear(ChnSettings); + Clear(Patterns); + Clear(PatternSize); + Clear(Order); + Clear(m_MidiCfg); + Clear(m_MixPlugins); + Clear(m_nDefaultSpeed); + Clear(m_nDefaultTempo); + Clear(m_nDefaultGlobalVolume); + Clear(m_dwSongFlags); + Clear(m_nChannels); + Clear(m_nMixChannels); + Clear(m_nMixStat); + Clear(m_nBufferCount); + Clear(m_nType); + Clear(m_nSamples); + Clear(m_nInstruments); + Clear(m_nTickCount); + Clear(m_nTotalCount); + Clear(m_nPatternDelay); + Clear(m_nFrameDelay); + Clear(m_nMusicSpeed); + Clear(m_nMusicTempo); + Clear(m_nNextRow); + Clear(m_nRow); + Clear(m_nPattern); + Clear(m_nCurrentPattern); + Clear(m_nNextPattern); + Clear(m_nRestartPos); + Clear(m_nMasterVolume); + Clear(m_nGlobalVolume); + Clear(m_nSongPreAmp); + Clear(m_nFreqFactor); + Clear(m_nTempoFactor); + Clear(m_nOldGlbVolSlide); + Clear(m_nMinPeriod); + Clear(m_nMaxPeriod); + Clear(m_nRepeatCount); + Clear(m_nInitialRepeatCount); + Clear(m_nGlobalFadeSamples); + Clear(m_nGlobalFadeMaxSamples); + Clear(m_nMaxOrderPosition); + Clear(m_nPatternNames); + Clear(m_lpszSongComments); + Clear(m_lpszPatternNames); + Clear(m_szNames); + Clear(CompressionTable); +} + +CSoundFile::~CSoundFile() { + mpcpplog(); + Destroy(); +} + +BOOL CSoundFile::Create( LPCBYTE lpStream, DWORD dwMemLength ) { + mpcpplog(); + try { + openmpt::module * m = new openmpt::module( lpStream, dwMemLength ); + set_self( this, m ); + std::strncpy( m_szNames[0], mod->get_metadata("title").c_str(), sizeof( m_szNames[0] ) - 1 ); + m_szNames[0][ sizeof( m_szNames[0] ) - 1 ] = '\0'; + std::string type = mod->get_metadata("type"); + m_nType = MOD_TYPE_NONE; + if ( type == "mod" ) { + m_nType = MOD_TYPE_MOD; + } else if ( type == "s3m" ) { + m_nType = MOD_TYPE_S3M; + } else if ( type == "xm" ) { + m_nType = MOD_TYPE_XM; + } else if ( type == "med" ) { + m_nType = MOD_TYPE_MED; + } else if ( type == "mtm" ) { + m_nType = MOD_TYPE_MTM; + } else if ( type == "it" ) { + m_nType = MOD_TYPE_IT; + } else if ( type == "669" ) { + m_nType = MOD_TYPE_669; + } else if ( type == "ult" ) { + m_nType = MOD_TYPE_ULT; + } else if ( type == "stm" ) { + m_nType = MOD_TYPE_STM; + } else if ( type == "far" ) { + m_nType = MOD_TYPE_FAR; + } else if ( type == "s3m" ) { + m_nType = MOD_TYPE_WAV; + } else if ( type == "amf" ) { + m_nType = MOD_TYPE_AMF; + } else if ( type == "ams" ) { + m_nType = MOD_TYPE_AMS; + } else if ( type == "dsm" ) { + m_nType = MOD_TYPE_DSM; + } else if ( type == "mdl" ) { + m_nType = MOD_TYPE_MDL; + } else if ( type == "okt" ) { + m_nType = MOD_TYPE_OKT; + } else if ( type == "mid" ) { + m_nType = MOD_TYPE_MID; + } else if ( type == "dmf" ) { + m_nType = MOD_TYPE_DMF; + } else if ( type == "ptm" ) { + m_nType = MOD_TYPE_PTM; + } else if ( type == "dbm" ) { + m_nType = MOD_TYPE_DBM; + } else if ( type == "mt2" ) { + m_nType = MOD_TYPE_MT2; + } else if ( type == "amf0" ) { + m_nType = MOD_TYPE_AMF0; + } else if ( type == "psm" ) { + m_nType = MOD_TYPE_PSM; + } else if ( type == "j2b" ) { + m_nType = MOD_TYPE_J2B; + } else if ( type == "abc" ) { + m_nType = MOD_TYPE_ABC; + } else if ( type == "pat" ) { + m_nType = MOD_TYPE_PAT; + } else if ( type == "umx" ) { + m_nType = MOD_TYPE_UMX; + } else { + m_nType = MOD_TYPE_IT; // fallback, most complex type + } + m_nChannels = mod->get_num_channels(); + m_nMasterVolume = 128; + m_nSamples = mod->get_num_samples(); + update_state(); + return TRUE; + } catch ( ... ) { + Destroy(); + return FALSE; + } +} + +BOOL CSoundFile::Destroy() { + mpcpplog(); + if ( mod ) { + delete mod; + set_self( this, 0 ); + } + return TRUE; +} + +UINT CSoundFile::GetNumChannels() const { + mpcpplog(); + return mod->get_num_channels(); +} + +static std::int32_t vol128_To_millibel( unsigned int vol ) { + return static_cast( 2000.0 * std::log10( static_cast( vol ) / 128.0 ) ); +} + +BOOL CSoundFile::SetMasterVolume( UINT vol, BOOL bAdjustAGC ) { + UNUSED(bAdjustAGC); + mpcpplog(); + m_nMasterVolume = vol; + mod->set_render_param( openmpt::module::RENDER_MASTERGAIN_MILLIBEL, vol128_To_millibel( m_nMasterVolume ) ); + return TRUE; +} + +UINT CSoundFile::GetNumPatterns() const { + mpcpplog(); + return mod->get_num_patterns(); +} + +UINT CSoundFile::GetNumInstruments() const { + mpcpplog(); + return mod->get_num_instruments(); +} + +void CSoundFile::SetCurrentOrder( UINT nOrder ) { + mpcpplog(); + mod->set_position_order_row( nOrder, 0 ); + update_state(); +} + +UINT CSoundFile::GetSampleName( UINT nSample, LPSTR s ) const { + mpcpplog(); + char buf[32]; + std::memset( buf, 0, 32 ); + if ( mod ) { + std::vector names = mod->get_sample_names(); + if ( 1 <= nSample && nSample <= names.size() ) { + std::strncpy( buf, names[ nSample - 1 ].c_str(), 31 ); + } + } + if ( s ) { + std::strncpy( s, buf, 32 ); + } + return static_cast( std::strlen( buf ) ); +} + +UINT CSoundFile::GetInstrumentName( UINT nInstr, LPSTR s ) const { + mpcpplog(); + char buf[32]; + std::memset( buf, 0, 32 ); + if ( mod ) { + std::vector names = mod->get_instrument_names(); + if ( 1 <= nInstr && nInstr <= names.size() ) { + std::strncpy( buf, names[ nInstr - 1 ].c_str(), 31 ); + } + } + if ( s ) { + std::strncpy( s, buf, 32 ); + } + return static_cast( std::strlen( buf ) ); +} + +void CSoundFile::LoopPattern( int nPat, int nRow ) { + UNUSED(nPat); + UNUSED(nRow); + mpcpplog(); + // todo +} + +void CSoundFile::CheckCPUUsage( UINT nCPU ) { + UNUSED(nCPU); + mpcpplog(); +} + +BOOL CSoundFile::SetPatternName( UINT nPat, LPCSTR lpszName ) { + UNUSED(nPat); + mpcpplog(); + if ( !lpszName ) { + return FALSE; + } + // todo + return TRUE; +} + +BOOL CSoundFile::GetPatternName( UINT nPat, LPSTR lpszName, UINT cbSize ) const { + UNUSED(nPat); + mpcpplog(); + if ( !lpszName || cbSize <= 0 ) { + return FALSE; + } + std::memset( lpszName, 0, cbSize ); + // todo + return TRUE; +} + +BOOL CSoundFile::ReadXM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadS3M(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMod(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMed(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadSTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadIT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::Read669(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadUlt(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadWav(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadDSM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadFAR(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMDL(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadOKT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadDMF(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadPTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadDBM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadAMF(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMT2(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadJ2B(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadUMX(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadABC(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::TestABC(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMID(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::TestMID(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadPAT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::TestPAT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } + +#ifndef MODPLUG_NO_FILESAVE + +UINT CSoundFile::WriteSample( FILE * f, MODINSTRUMENT * pins, UINT nFlags, UINT nMaxLen ) { + UNUSED(f); + UNUSED(pins); + UNUSED(nFlags); + UNUSED(nMaxLen); + mpcpplog(); + return 0; +} + +BOOL CSoundFile::SaveXM( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +BOOL CSoundFile::SaveS3M( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +BOOL CSoundFile::SaveMod( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +BOOL CSoundFile::SaveIT( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +#endif + +UINT CSoundFile::GetBestSaveFormat() const { + mpcpplog(); + return MOD_TYPE_IT; +} + +UINT CSoundFile::GetSaveFormats() const { + mpcpplog(); + return MOD_TYPE_IT; +} + +void CSoundFile::ConvertModCommand( MODCOMMAND * ) const { + mpcpplog(); +} + +void CSoundFile::S3MConvert( MODCOMMAND * m, BOOL bIT ) const { + UNUSED(m); + UNUSED(bIT); + mpcpplog(); +} + +void CSoundFile::S3MSaveConvert( UINT * pcmd, UINT * pprm, BOOL bIT ) const { + UNUSED(pcmd); + UNUSED(pprm); + UNUSED(bIT); + mpcpplog(); +} + +WORD CSoundFile::ModSaveCommand( const MODCOMMAND * m, BOOL bXM ) const { + UNUSED(m); + UNUSED(bXM); + mpcpplog(); + return 0; +} + +VOID CSoundFile::ResetChannels() { + mpcpplog(); +} + +UINT CSoundFile::CreateStereoMix( int count ) { + UNUSED(count); + mpcpplog(); + return 0; +} + +BOOL CSoundFile::FadeSong( UINT msec ) { + UNUSED(msec); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::GlobalFadeSong( UINT msec ) { + UNUSED(msec); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::InitPlayer( BOOL bReset ) { + UNUSED(bReset); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::SetMixConfig( UINT nStereoSeparation, UINT nMaxMixChannels ) { + UNUSED(nMaxMixChannels); + mpcpplog(); + m_nStereoSeparation = nStereoSeparation; + return TRUE; +} + +DWORD CSoundFile::InitSysInfo() { + mpcpplog(); + return 0; +} + +void CSoundFile::SetAGC( BOOL b ) { + UNUSED(b); + mpcpplog(); +} + +void CSoundFile::ResetAGC() { + mpcpplog(); +} + +void CSoundFile::ProcessAGC( int count ) { + UNUSED(count); + mpcpplog(); +} + +BOOL CSoundFile::SetWaveConfig( UINT nRate, UINT nBits, UINT nChannels, BOOL bMMX ) { + UNUSED(bMMX); + mpcpplog(); + gdwMixingFreq = nRate; + gnBitsPerSample = nBits; + gnChannels = nChannels; + return TRUE; +} + +BOOL CSoundFile::SetWaveConfigEx( BOOL bSurround, BOOL bNoOverSampling, BOOL bReverb, BOOL hqido, BOOL bMegaBass, BOOL bNR, BOOL bEQ ) { + UNUSED(bSurround); + UNUSED(bReverb); + UNUSED(hqido); + UNUSED(bMegaBass); + UNUSED(bEQ); + mpcpplog(); + DWORD d = gdwSoundSetup & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + if ( bNoOverSampling ) { + d |= SNDMIX_NORESAMPLING; + } else if ( !hqido ) { + d |= 0; + } else if ( !bNR ) { + d |= SNDMIX_HQRESAMPLER; + } else { + d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + } + gdwSoundSetup = d; + return TRUE; +} + +BOOL CSoundFile::SetResamplingMode( UINT nMode ) { + mpcpplog(); + DWORD d = gdwSoundSetup & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + switch ( nMode ) { + case SRCMODE_NEAREST: + d |= SNDMIX_NORESAMPLING; + break; + case SRCMODE_LINEAR: + break; + case SRCMODE_SPLINE: + d |= SNDMIX_HQRESAMPLER; + break; + case SRCMODE_POLYPHASE: + d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + break; + default: + return FALSE; + break; + } + gdwSoundSetup = d; + return TRUE; +} + +BOOL CSoundFile::SetReverbParameters( UINT nDepth, UINT nDelay ) { + UNUSED(nDepth); + UNUSED(nDelay); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::SetXBassParameters( UINT nDepth, UINT nRange ) { + UNUSED(nDepth); + UNUSED(nRange); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::SetSurroundParameters( UINT nDepth, UINT nDelay ) { + UNUSED(nDepth); + UNUSED(nDelay); + mpcpplog(); + return TRUE; +} + +UINT CSoundFile::GetMaxPosition() const { + mpcpplog(); + // rows in original, just use seconds here + if ( mod ) return static_cast( mod->get_duration_seconds() + 0.5 ); + return 0; +} + +DWORD CSoundFile::GetLength( BOOL bAdjust, BOOL bTotal ) { + UNUSED(bAdjust); + UNUSED(bTotal); + mpcpplog(); + if ( mod ) return static_cast( mod->get_duration_seconds() + 0.5 ); + return 0; +} + +UINT CSoundFile::GetSongComments( LPSTR s, UINT cbsize, UINT linesize ) { + UNUSED(linesize); + mpcpplog(); + if ( !s ) { + return 0; + } + if ( cbsize <= 0 ) { + return 0; + } + if ( !mod ) { + s[0] = '\0'; + return 1; + } + std::strncpy( s, mod->get_metadata("message").c_str(), cbsize ); + s[ cbsize - 1 ] = '\0'; + return static_cast( std::strlen( s ) + 1 ); +} + +UINT CSoundFile::GetRawSongComments( LPSTR s, UINT cbsize, UINT linesize ) { + UNUSED(linesize); + mpcpplog(); + if ( !s ) { + return 0; + } + if ( cbsize <= 0 ) { + return 0; + } + if ( !mod ) { + s[0] = '\0'; + return 1; + } + std::strncpy( s, mod->get_metadata("message_raw").c_str(), cbsize ); + s[ cbsize - 1 ] = '\0'; + return static_cast( std::strlen( s ) + 1 ); +} + +void CSoundFile::SetCurrentPos( UINT nPos ) { + mpcpplog(); + if ( mod ) mod->set_position_seconds( nPos ); + update_state(); +} + +UINT CSoundFile::GetCurrentPos() const { + mpcpplog(); + if ( mod ) return static_cast( mod->get_position_seconds() + 0.5 ); + return 0; +} + +static int get_stereo_separation() { + mpcpplog(); + return CSoundFile::m_nStereoSeparation * 100 / 128; +} + +static int get_filter_length() { + mpcpplog(); + if ( ( CSoundFile::gdwSoundSetup & (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE) ) == (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE) ) { + return 8; + } else if ( ( CSoundFile::gdwSoundSetup & SNDMIX_HQRESAMPLER ) == SNDMIX_HQRESAMPLER ) { + return 4; + } else if ( ( CSoundFile::gdwSoundSetup & SNDMIX_NORESAMPLING ) == SNDMIX_NORESAMPLING ) { + return 1; + } else { + return 2; + } +} + +static std::size_t get_sample_size() { + return (CSoundFile::gnBitsPerSample/8); +} + +static std::size_t get_num_channels() { + return CSoundFile::gnChannels; +} + +static std::size_t get_frame_size() { + return get_sample_size() * get_num_channels(); +} + +static int get_samplerate() { + return CSoundFile::gdwMixingFreq; +} + +UINT CSoundFile::Read( LPVOID lpBuffer, UINT cbBuffer ) { + mpcpplog(); + if ( !mod ) { + return 0; + } + mpcpplog(); + if ( !lpBuffer ) { + return 0; + } + mpcpplog(); + if ( cbBuffer <= 0 ) { + return 0; + } + mpcpplog(); + if ( get_samplerate() <= 0 ) { + return 0; + } + mpcpplog(); + if ( get_sample_size() != 1 && get_sample_size() != 2 && get_sample_size() != 4 ) { + return 0; + } + mpcpplog(); + if ( get_num_channels() != 1 && get_num_channels() != 2 && get_num_channels() != 4 ) { + return 0; + } + mpcpplog(); + std::memset( lpBuffer, 0, cbBuffer ); + const std::size_t frames_torender = cbBuffer / get_frame_size(); + short * out = reinterpret_cast( lpBuffer ); + std::vector tmpbuf; + if ( get_sample_size() == 1 || get_sample_size() == 4 ) { + tmpbuf.resize( frames_torender * get_num_channels() ); + out = &tmpbuf[0]; + } + + mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, get_stereo_separation() ); + mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, get_filter_length() ); + std::size_t frames_rendered = 0; + if ( get_num_channels() == 1 ) { + frames_rendered = mod->read( get_samplerate(), frames_torender, out ); + } else if ( get_num_channels() == 4 ) { + frames_rendered = mod->read_interleaved_quad( get_samplerate(), frames_torender, out ); + } else { + frames_rendered = mod->read_interleaved_stereo( get_samplerate(), frames_torender, out ); + } + + if ( get_sample_size() == 1 ) { + unsigned char * dst = reinterpret_cast( lpBuffer ); + for ( std::size_t sample = 0; sample < frames_rendered * get_num_channels(); ++sample ) { + dst[sample] = ( tmpbuf[sample] / 0x100 ) + 0x80; + } + } else if ( get_sample_size() == 4 ) { + int * dst = reinterpret_cast( lpBuffer ); + for ( std::size_t sample = 0; sample < frames_rendered * get_num_channels(); ++sample ) { + dst[sample] = tmpbuf[sample] << (32-16-1-MIXING_ATTENUATION); + } + } + update_state(); + return static_cast( frames_rendered ); +} + + +/* + +gstreamer modplug calls: + +mSoundFile->Create +mSoundFile->Destroy + +mSoundFile->SetWaveConfig +mSoundFile->SetWaveConfigEx +mSoundFile->SetResamplingMode +mSoundFile->SetSurroundParameters +mSoundFile->SetXBassParameters +mSoundFile->SetReverbParameters + +mSoundFile->GetMaxPosition (inline, -> GetLength) +mSoundFile->GetSongTime + +mSoundFile->GetTitle (inline) +mSoundFile->GetSongComments + +mSoundFile->SetCurrentPos +mSoundFile->Read + +mSoundFile->GetCurrentPos +mSoundFile->GetMusicTempo (inline) + +*/ + + +// really very internal symbols, probably nothing calls these directly + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wunused-parameter" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4100) +#endif + +BOOL CSoundFile::ReadNote() { mpcpplog(); return 0; } +BOOL CSoundFile::ProcessRow() { mpcpplog(); return 0; } +BOOL CSoundFile::ProcessEffects() { mpcpplog(); return 0; } +UINT CSoundFile::GetNNAChannel(UINT nChn) const { mpcpplog(); return 0; } +void CSoundFile::CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut) { mpcpplog(); } +void CSoundFile::NoteChange(UINT nChn, int note, BOOL bPorta, BOOL bResetEnv) { mpcpplog(); } +void CSoundFile::InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta,BOOL bUpdVol,BOOL bResetEnv) { mpcpplog(); } +void CSoundFile::PortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::PortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FinePortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FinePortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::TonePortamento(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::Vibrato(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FineVibrato(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::VolumeSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::PanningSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::ChannelVolSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FineVolumeUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FineVolumeDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::Tremolo(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::Panbrello(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::RetrigNote(UINT nChn, UINT param) { mpcpplog(); } +void CSoundFile::NoteCut(UINT nChn, UINT nTick) { mpcpplog(); } +void CSoundFile::KeyOff(UINT nChn) { mpcpplog(); } +int CSoundFile::PatternLoop(MODCHANNEL *, UINT param) { mpcpplog(); return 0; } +void CSoundFile::ExtendedMODCommands(UINT nChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtendedS3MCommands(UINT nChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtendedChannelEffect(MODCHANNEL *, UINT param) { mpcpplog(); } +void CSoundFile::ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param) { mpcpplog(); } +void CSoundFile::SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier) const { mpcpplog(); } +void CSoundFile::DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide) { mpcpplog(); } +void CSoundFile::SetTempo(UINT param) { mpcpplog(); } +void CSoundFile::SetSpeed(UINT param) { mpcpplog(); } +void CSoundFile::GlobalVolSlide(UINT param) { mpcpplog(); } +DWORD CSoundFile::IsSongFinished(UINT nOrder, UINT nRow) const { mpcpplog(); return 0; } +BOOL CSoundFile::IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const { mpcpplog(); return 0; } +UINT CSoundFile::PackSample(int &sample, int next) { mpcpplog(); return 0; } +BOOL CSoundFile::CanPackSample(LPSTR pSample, UINT nLen, UINT nPacking, BYTE *result) { mpcpplog(); return 0; } +UINT CSoundFile::ReadSample(MODINSTRUMENT *pIns, UINT nFlags, LPCSTR pMemFile, DWORD dwMemLength) { mpcpplog(); return 0; } +BOOL CSoundFile::DestroySample(UINT nSample) { mpcpplog(); return 0; } +BOOL CSoundFile::DestroyInstrument(UINT nInstr) { mpcpplog(); return 0; } +BOOL CSoundFile::IsSampleUsed(UINT nSample) { mpcpplog(); return 0; } +BOOL CSoundFile::IsInstrumentUsed(UINT nInstr) { mpcpplog(); return 0; } +BOOL CSoundFile::RemoveInstrumentSamples(UINT nInstr) { mpcpplog(); return 0; } +UINT CSoundFile::DetectUnusedSamples(BOOL *) { mpcpplog(); return 0; } +BOOL CSoundFile::RemoveSelectedSamples(BOOL *) { mpcpplog(); return 0; } +void CSoundFile::AdjustSampleLoop(MODINSTRUMENT *pIns) { mpcpplog(); } +BOOL CSoundFile::ReadInstrumentFromSong(UINT nInstr, CSoundFile *, UINT nSrcInstrument) { mpcpplog(); return 0; } +BOOL CSoundFile::ReadSampleFromSong(UINT nSample, CSoundFile *, UINT nSrcSample) { mpcpplog(); return 0; } +UINT CSoundFile::GetNoteFromPeriod(UINT period) const { mpcpplog(); return 0; } +UINT CSoundFile::GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const { mpcpplog(); return 0; } +UINT CSoundFile::GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac) const { mpcpplog(); return 0; } +void CSoundFile::ResetMidiCfg() { mpcpplog(); } +UINT CSoundFile::MapMidiInstrument(DWORD dwProgram, UINT nChannel, UINT nNote) { mpcpplog(); return 0; } +BOOL CSoundFile::ITInstrToMPT(const void *p, INSTRUMENTHEADER *penv, UINT trkvers) { mpcpplog(); return 0; } +UINT CSoundFile::SaveMixPlugins(FILE *f, BOOL bUpdate) { mpcpplog(); return 0; } +UINT CSoundFile::LoadMixPlugins(const void *pData, UINT nLen) { mpcpplog(); return 0; } +#ifndef NO_FILTER +DWORD CSoundFile::CutOffToFrequency(UINT nCutOff, int flt_modifier) const { mpcpplog(); return 0; } +#endif +DWORD CSoundFile::TransposeToFrequency(int transp, int ftune) { mpcpplog(); return 0; } +int CSoundFile::FrequencyToTranspose(DWORD freq) { mpcpplog(); return 0; } +void CSoundFile::FrequencyToTranspose(MODINSTRUMENT *psmp) { mpcpplog(); } +MODCOMMAND *CSoundFile::AllocatePattern(UINT rows, UINT nchns) { mpcpplog(); return 0; } +signed char* CSoundFile::AllocateSample(UINT nbytes) { mpcpplog(); return 0; } +void CSoundFile::FreePattern(LPVOID pat) { mpcpplog(); } +void CSoundFile::FreeSample(LPVOID p) { mpcpplog(); } +UINT CSoundFile::Normalize24BitBuffer(LPBYTE pbuffer, UINT cbsizebytes, DWORD lmax24, DWORD dwByteInc) { mpcpplog(); return 0; } + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + + +#endif // NO_LIBMODPLUG diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/m4/ax_cxx_compile_stdcxx.m4 b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 000000000..0b6cb3a7d --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,972 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016, 2018 Krzesimir Nowak +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 9 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus <= 201402L + +#error "This is not a C++17 compiler" + +#else + +#if defined(__clang__) + #define REALLY_CLANG +#else + #if defined(__GNUC__) + #define REALLY_GCC + #endif +#endif + +#include +#include +#include + +namespace cxx17 +{ + +#if !defined(REALLY_CLANG) + namespace test_constexpr_lambdas + { + + // TODO: test it with clang++ from git + + constexpr int foo = [](){return 42;}(); + + } +#endif // !defined(REALLY_CLANG) + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + +#if !defined(REALLY_CLANG) + namespace test_template_argument_deduction_for_class_templates + { + + // TODO: test it with clang++ from git + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } +#endif // !defined(REALLY_CLANG) + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + +#if !defined(REALLY_CLANG) + namespace test_structured_bindings + { + + // TODO: test it with clang++ from git + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } +#endif // !defined(REALLY_CLANG) + +#if !defined(REALLY_CLANG) + namespace test_exception_spec_type_system + { + + // TODO: test it with clang++ from git + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } +#endif // !defined(REALLY_CLANG) + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus <= 201402L + +]]) diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/test.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/test.sh new file mode 100755 index 000000000..7df7ed3fb --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libmodplug-0.8.9.0/test.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +./autogen.sh + +./configure +make +make distcheck +make distclean diff --git a/Frameworks/OpenMPT.old/OpenMPT/contrib/libopenmpt/libopenmpt_bass.c b/Frameworks/OpenMPT.old/OpenMPT/contrib/libopenmpt/libopenmpt_bass.c new file mode 100644 index 000000000..71fc3d9dd --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/contrib/libopenmpt/libopenmpt_bass.c @@ -0,0 +1,109 @@ +/* + * libopenmpt_bass.c + * ----------------- + * Purpose: Example of how to use libopenmpt with the BASS audio library. + * Notes : BASS from un4seen (http://www.un4seen.com/bass.html) is used for audio output. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_bass SOMEMODULE + */ + +#include +#include +#if ( defined( _WIN32 ) || defined( WIN32 ) ) +#include +#define sleep(n) Sleep(n) +#else +#include +#endif + +#include +#include +#include + +#define SAMPLERATE 48000 + +DWORD CALLBACK StreamProc(HSTREAM handle, void *buffer, DWORD length, void *user) +{ + // length is in bytes, but libopenmpt wants samples => divide by number of channels (2) and size of a sample (float = 4) + // same for return value. + size_t count = openmpt_module_read_interleaved_float_stereo( (openmpt_module *)user, SAMPLERATE, length / (2 * sizeof(float)), (float *)buffer ); + count *= (2 * sizeof(float)); + // Reached end of stream? + if(count < length) count |= BASS_STREAMPROC_END; + return (DWORD)count; +} + + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + + FILE * file = 0; + openmpt_module * mod = 0; + HSTREAM stream = 0; + int result = 0; + + if ( argc != 2 ) { + fprintf( stderr, "Usage: %s SOMEMODULE\n", argv[0] ); + return 1; + } + + if ( HIWORD( BASS_GetVersion() ) !=BASSVERSION ) { + fprintf( stderr, "Error: Wrong BASS version\n" ); + return 1; + } + + if ( !BASS_Init( -1, SAMPLERATE, 0, NULL, NULL ) ) { + fprintf( stderr, "Error: Cannot initialize BASS (error %d)\n", BASS_ErrorGetCode() ); + return 1; + } + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + if ( wcslen( argv[1] ) == 0 ) { + fprintf( stderr, "Usage: %s SOMEMODULE\n", argv[0] ); + result = 1; + goto fail; + } + file = _wfopen( argv[1], L"rb" ); +#else + if ( strlen( argv[1] ) == 0 ) { + fprintf( stderr, "Usage: %s SOMEMODULE\n", argv[0] ); + result = 1; + goto fail; + } + file = fopen( argv[1], "rb" ); +#endif + if ( !file ) { + fprintf( stderr, "Error: %s\n", "fopen() failed." ); + result = 1; + goto fail; + } + + mod = openmpt_module_create( openmpt_stream_get_file_callbacks(), file, NULL, NULL, NULL ); + if ( !mod ) { + fprintf( stderr, "Error: %s\n", "openmpt_module_create() failed." ); + goto fail; + } + + // Create a "pull" channel. BASS will call StreamProc whenever the channel needs new data to be decoded. + stream = BASS_StreamCreate(SAMPLERATE, 2, BASS_SAMPLE_FLOAT, StreamProc, mod); + BASS_ChannelPlay(stream, FALSE); + + while ( BASS_ChannelIsActive( stream ) ) { + // Do something useful here + sleep(1); + } + + BASS_StreamFree( stream ); + openmpt_module_destroy( mod ); + +fail: + BASS_Free(); + return result; +} diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/build.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/toolchain-djgpp/build.sh similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/build.sh rename to Frameworks/OpenMPT.old/OpenMPT/contrib/toolchain-djgpp/build.sh diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/setenv.sh b/Frameworks/OpenMPT.old/OpenMPT/contrib/toolchain-djgpp/setenv.sh similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/setenv.sh rename to Frameworks/OpenMPT.old/OpenMPT/contrib/toolchain-djgpp/setenv.sh diff --git a/Frameworks/OpenMPT.old/OpenMPT/doc/contributing.md b/Frameworks/OpenMPT.old/OpenMPT/doc/contributing.md new file mode 100644 index 000000000..e3eaa7331 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/doc/contributing.md @@ -0,0 +1,43 @@ + +Contributing +============ + +OpenMPT, libopenmpt, openmpt123, xmp-openmpt and in_openmpt are developed in +the Subversion repository at +[https://source.openmpt.org/browse/openmpt/trunk/OpenMPT/](https://source.openmpt.org/browse/openmpt/trunk/OpenMPT/). +Patches can be provided either against this Subversion repository or against our +GitHub mirror at +[https://github.com/OpenMPT/openmpt/](https://github.com/OpenMPT/openmpt/). + +We do not have a developer mailing list. Discussions about new features or +problems can happen at: + * [Issue Tracker](https://bugs.openmpt.org/), preferred for specific bug + reports or bug fixes and feature development discussion + * [Forum](https://forum.openmpt.org/), preferred for long-term discussion of + new features or specific questions about development + * [IRC channel (`EsperNET/#modplug`)](irc://irc.esper.net:5555/#modplug), + preferred for shorter questions + * [GitHub pull requests](https://github.com/OpenMPT/openmpt/pulls), please + only use for rather tiny fixes, see below + +For patch submissions, please also see +[OpenMPT style guide](openmpt_styleguide.md) and +[libopenmpt style guide](libopenmpt_styleguide.md). + +### Contributing via GitHub + +As OpenMPT is developed in a Subversion repository and the GitHub repository is +just mirrored from that, we cannot directly take pull requests via GitHub. We +recognize that, especially for tiny bug fixes, the burden to choose a different +way than GitHub for contributing can be too high. Thus, we will of course react, +give feedback, and take patches also via GitHub pull requests. However, as the +GitHub repository is strictly downstream from our Subversion repository (and +this will not change, due to considerable complications when synchronizing this +two-way), we cannot directly merge pull requests on GitHub. We will merge +contributions to our Subversion repository, which will then in turn be mirrored +to GitHub automatically, after which we will close the pull request. Authorship +attribution in git relies on the email address used in the commit header, which +is not how it usually works in Subversion. We will thus add an additional line +to the commit message in the form of `Patch-by: John Doe `. If +you prefer to be attributed with your nickname and/or without your email +address, that would also be fine for us. diff --git a/Frameworks/OpenMPT.old/OpenMPT/doc/libopenmpt_styleguide.md b/Frameworks/OpenMPT.old/OpenMPT/doc/libopenmpt_styleguide.md new file mode 100644 index 000000000..2a0a4fb9f --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/doc/libopenmpt_styleguide.md @@ -0,0 +1,104 @@ + +Coding conventions +------------------ + + +### libopenmpt + +**Note:** +**This applies to `libopenmpt/` and `openmpt123/` directories only.** +**Use OpenMPT style otherwise.** + +The code generally tries to follow these conventions, but they are not +strictly enforced and there are valid reasons to diverge from these +conventions. Using common sense is recommended. + + - In general, the most important thing is to keep style consistent with + directly surrounding code. + - Use C++ std types when possible, prefer `std::size_t` and `std::int32_t` + over `long` or `int`. Do not use C99 std types (e.g. no pure `int32_t`) + - Qualify namespaces explicitly, do not use `using`. + Members of `namespace openmpt` can be named without full namespace + qualification. + - Prefer the C++ version in `namespace std` if the same functionality is + provided by the C standard library as well. Also, include the C++ + version of C standard library headers (e.g. use `` instead of + ``. + - Do not use ANY locale-dependant C functions. For locale-dependant C++ + functionaly (especially iostream), always imbue the + `std::locale::classic()` locale. + - Prefer kernel_style_names over CamelCaseNames. + - If a folder (or one of its parent folders) contains .clang-format, + use clang-format v3.5 for indenting C++ and C files, otherwise: + - `{` are placed at the end of the opening line. + - Enclose even single statements in curly braces. + - Avoid placing single statements on the same line as the `if`. + - Opening parentheses are separated from keywords with a space. + - Opening parentheses are not separated from function names. + - Place spaces around operators and inside parentheses. + - Align `:` and `,` when inheriting or initializing members in a + constructor. + - The pointer `*` is separated from both the type and the variable name. + - Use tabs for identation, spaces for formatting. + Tabs should only appear at the very beginning of a line. + Do not assume any particular width of the TAB character. If width is + important for formatting reasons, use spaces. + - Use empty lines at will. + - API documentation is done with doxygen. + Use general C doxygen for the C API. + Use QT-style doxygen for the C++ API. + +#### libopenmpt indentation example + +~~~~{.cpp} +namespace openmpt { + +// This is totally meaningless code and just illustrates indentation. + +class foo + : public base + , public otherbase +{ + +private: + + std::int32_t x; + std::int16_t y; + +public: + + foo() + : x(0) + , y(-1) + { + return; + } + + int bar() const; + +}; // class foo + +int foo::bar() const { + + for ( int i = 0; i < 23; ++i ) { + switch ( x ) { + case 2: + something( y ); + break; + default: + something( ( y - 1 ) * 2 ); + break; + } + } + if ( x == 12 ) { + return -1; + } else if ( x == 42 ) { + return 1; + } + return 42; + +} + +} // namespace openmpt +~~~~ + diff --git a/Frameworks/OpenMPT.old/OpenMPT/doc/module_formats.md b/Frameworks/OpenMPT.old/OpenMPT/doc/module_formats.md new file mode 100644 index 000000000..eb1e0b2b6 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/doc/module_formats.md @@ -0,0 +1,103 @@ +How to add support for new module formats +========================================= + +This document describes the basics of writing a new module loader and related +work that has to be done. We will not discuss in detail how to write the loader, +have a look at existing loaders to get an idea how they work in general. + +General hints +------------- +* We strive for quality over quantity. The goal is not to support as many module + formats as possible, but to support them as well as possible. +* Write defensive code. Guard against out-of-bound values, division by zero and + similar stuff. libopenmpt is constantly fuzz-tested to catch any crashes, but + of course we want our code to be reliable from the start. +* Every format should have its own `MODTYPE` flag, unless it can be reasonably + represented as a subset of another format (like Ice Tracker ICE files being + a subset of ProTracker MOD). +* When reading binary structs from the file, use our data types with defined + endianness, which can be found in `common/Endianness.h`: + * Big-Endian: (u)int8/16/32/64be, float32be, float64be + * Little-Endian: (u)int8/16/32/64le, float32le, float64le + + Entire structs containing integers with defined endianness can be read in one + go if they are tagged with `MPT_BINARY_STRUCT` (see existing loaders for an + example). +* `m_nChannels` **MUST NOT** be changed after a pattern has been created, as + existing patterns will be interpreted incorrectly. For module formats that + support per-pattern channel amounts, the maximum number of channels must be + determined beforehand. +* Strings can be safely handled using: + * `FileReader::ReadString` and friends for reading them directly from a file + * `mpt::String::ReadBuf` for reading them from a struct or char array + + These functions take care of string padding (zero / space padding) and will + avoid reading past the end of the string if there is no terminating null + character. +* Do not use non-const static variables in your loader. Loaders need to be + thread-safe for libopenmpt. +* `FileReader` instances may be used to treat a portion of another file as its + own independent file (through `FileReader::ReadChunk`). This can be useful + with "embedded files" such as WAV or Ogg samples. Container formats are + another good example for this usage. +* Samples *either* use middle-C frequency *or* finetune + transpose. For the few + weird formats that use both, it may make sense to translate everything into + middle-C frequency. +* Add the new `MODTYPE` to `CSoundFile::UseFinetuneAndTranspose` if applicable, + and see if any effect handlers in `soundlib/Snd_fx.cpp` need to know the new + `MODTYPE`. +* Do not rely on hard-coded magic numbers. For example, when comparing if an + index is valid for a given array, do not hard-code the array size but rather + use `std::size` (or `mpt::array_size` in contexts where `std::size` is not + usable) or, for ensuring that char arrays are null-terminated, + `mpt::String::SetNullTerminator`. Similarly, do not assume any specific + quantities for OpenMPT's constants like MAX_SAMPLES, MAX_PATTERN_ROWS, etc. + These may change at any time. +* Pay attention to off-by-one errors when comparing against MAX_SAMPLES and + MAX_INSTRUMENTS, since sample and instrument numbers are 1-based. +* Placement of the loader function in `CSoundFile::Create` depends on various + factors. In general, module formats that have very bad magic numbers (and thus + might cause other formats to get mis-interpreted) should be placed at the + bottom of the list. Two notable examples are 669 files, where the first two + bytes of the file are "if" (which may e.g. cause a song title starting with + "if ..." in various other formats to be interpreted as a 669 module), and of + course Ultimate SoundTracker modules, which have no magic bytes at all. +* Avoid use of functions tagged with MPT_DEPRECATED. + +Probing +------- +libopenmpt provides fast probing functions that can be used by library users +to quickly check if a file is most likely playable with libopenmpt, even if only +a fraction of the file is available (e.g. when streaming from the internet). + +In order to satisfy these requirements, probing functions should do as little +work as possible (e.g. only parse the header of the file), but as much as +required to tell with some certainty that the file is really of a certain mod +format. However, probing functions should not rely on having access to more than +the first `CSoundFile::ProbeRecommendedSize` bytes of the file. + +* Probing functions **must not** allocate any memory on the heap. +* Probing functions **must not** return ProbeFailure or ProbeWantMoreData for + any file that would normally be accepted by the loader. In particular, this + means that any header checks must not be any more aggressive than they would + be in the real loader (hence it is a good idea to not copy-paste this code but + rather put it in a separate function), and the minimum additional size passed + to `CSoundFile::ProbeAdditionalSize` must not be higher than the biggest size + that would cause a hard failure (i.e. returning `false`) in the module loader. +* Probing functions **may** return ProbeSuccess for files that would be rejected + by a loader after a more thorough inspection. For example, probing functions + do not need to verify that all required chunks of an IFF-like file format are + actually present, if the header makes it obvious enough that the file is + highly likely to be a module. + +Adding loader to the build systems and various other locations +-------------------------------------------------------------- +Apart from writing the module loader itself, there are a couple of other places +that need to be updated: +* Add loader file to `build/android_ndk/Android.mk`. +* Add loader file to `build/autotools/Makefile.am`. +* Run `build/regenerate_vs_projects.sh` / `build/regenerate_vs_projects.cmd` + (depending on your platform) +* Add file extension to `installer/filetypes.iss` (in four places). +* Add file extension to `CTrackApp::OpenModulesDialog` in `mptrack/Mptrack.cpp`. +* Add format information to `soundlib/Tables.cpp`. diff --git a/Frameworks/OpenMPT.old/OpenMPT/examples/.clang-format b/Frameworks/OpenMPT.old/OpenMPT/examples/.clang-format new file mode 100644 index 000000000..1f7e170c0 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/examples/.clang-format @@ -0,0 +1,63 @@ +AccessModifierOffset: -2 +AlignAfterOpenBracket: true +AlignConsecutiveAssignments: false +AlignEscapedNewlinesLeft: true +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: true +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IndentCaseLabels: false +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: true +Language: Cpp +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 3 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 60 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Middle +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: true +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 2 +UseTab: ForIndentation diff --git a/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c.c b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c.c new file mode 100644 index 000000000..2dcd13d33 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c.c @@ -0,0 +1,209 @@ +/* + * libopenmpt_example_c.c + * ---------------------- + * Purpose: libopenmpt C API example + * Notes : PortAudio is used for sound output. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_example_c SOMEMODULE + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define BUFFERSIZE 480 +#define SAMPLERATE 48000 + +static int16_t left[BUFFERSIZE]; +static int16_t right[BUFFERSIZE]; +static int16_t * const buffers[2] = { left, right }; +static int16_t interleaved_buffer[BUFFERSIZE * 2]; +static int is_interleaved = 0; + +static void libopenmpt_example_logfunc( const char * message, void * userdata ) { + (void)userdata; + if ( message ) { + fprintf( stderr, "openmpt: %s\n", message ); + } +} + +static int libopenmpt_example_errfunc( int error, void * userdata ) { + (void)userdata; + (void)error; + return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG; +} + +static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) { + if ( !func_name ) { + func_name = "unknown function"; + } + if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" ); + } else { + fprintf( stderr, "Error: %s\n", mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + } else { + if ( !mod_err_str ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s failed.\n", func_name ); + } else { + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } +} + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + + int result = 0; + FILE * file = 0; + openmpt_module * mod = 0; + int mod_err = OPENMPT_ERROR_OK; + const char * mod_err_str = NULL; + size_t count = 0; + PaError pa_error = paNoError; + int pa_initialized = 0; + PaStream * stream = 0; + + if ( argc != 2 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c SOMEMODULE'." ); + goto fail; + } + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + if ( wcslen( argv[1] ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c SOMEMODULE'." ); + goto fail; + } + file = _wfopen( argv[1], L"rb" ); +#else + if ( strlen( argv[1] ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c SOMEMODULE'." ); + goto fail; + } + file = fopen( argv[1], "rb" ); +#endif + if ( !file ) { + fprintf( stderr, "Error: %s\n", "fopen() failed." ); + goto fail; + } + + mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL ); + if ( !mod ) { + libopenmpt_example_print_error( "openmpt_module_create2()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + goto fail; + } + openmpt_module_set_error_func( mod, NULL, NULL ); + + pa_error = Pa_Initialize(); + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_Initialize() failed." ); + goto fail; + } + pa_initialized = 1; + + pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + if ( pa_error == paSampleFormatNotSupported ) { + is_interleaved = 1; + pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + } + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); + goto fail; + } + if ( !stream ) { + fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); + goto fail; + } + + pa_error = Pa_StartStream( stream ); + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_StartStream() failed." ); + goto fail; + } + + while ( 1 ) { + + openmpt_module_error_clear( mod ); + count = is_interleaved ? openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, interleaved_buffer ) : openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); + mod_err = openmpt_module_error_get_last( mod ); + mod_err_str = openmpt_module_error_get_last_message( mod ); + if ( mod_err != OPENMPT_ERROR_OK ) { + libopenmpt_example_print_error( "openmpt_module_read_stereo()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + if ( count == 0 ) { + break; + } + + pa_error = is_interleaved ? Pa_WriteStream( stream, interleaved_buffer, (unsigned long)count ) : Pa_WriteStream( stream, buffers, (unsigned long)count ); + if ( pa_error == paOutputUnderflowed ) { + pa_error = paNoError; + } + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_WriteStream() failed." ); + goto fail; + } + } + + result = 0; + + goto cleanup; + +fail: + + result = 1; + +cleanup: + + if ( stream ) { + if ( Pa_IsStreamActive( stream ) == 1 ) { + Pa_StopStream( stream ); + } + Pa_CloseStream( stream ); + stream = 0; + } + + if ( pa_initialized ) { + Pa_Terminate(); + pa_initialized = 0; + } + + if ( mod ) { + openmpt_module_destroy( mod ); + mod = 0; + } + + if ( file ) { + fclose( file ); + file = 0; + } + + return result; +} diff --git a/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_mem.c b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_mem.c new file mode 100644 index 000000000..dda6379dc --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_mem.c @@ -0,0 +1,299 @@ +/* + * libopenmpt_example_c_mem.c + * -------------------------- + * Purpose: libopenmpt C API example + * Notes : PortAudio is used for sound output. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_example_c_mem SOMEMODULE + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#define BUFFERSIZE 480 +#define SAMPLERATE 48000 + +static int16_t left[BUFFERSIZE]; +static int16_t right[BUFFERSIZE]; +static int16_t * const buffers[2] = { left, right }; +static int16_t interleaved_buffer[BUFFERSIZE * 2]; +static int is_interleaved = 0; + +static void libopenmpt_example_logfunc( const char * message, void * userdata ) { + (void)userdata; + if ( message ) { + fprintf( stderr, "openmpt: %s\n", message ); + } +} + +static int libopenmpt_example_errfunc( int error, void * userdata ) { + (void)userdata; + (void)error; + return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG; +} + +static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) { + if ( !func_name ) { + func_name = "unknown function"; + } + if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" ); + } else { + fprintf( stderr, "Error: %s\n", mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + } else { + if ( !mod_err_str ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s failed.\n", func_name ); + } else { + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } +} + +typedef struct blob_t { + size_t size; + void * data; +} blob_t; + +static void free_blob( blob_t * blob ) { + if ( blob ) { + if ( blob->data ) { + free( blob->data ); + blob->data = 0; + } + blob->size = 0; + free( blob ); + } +} + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +static blob_t * load_file( const wchar_t * filename ) { +#else +static blob_t * load_file( const char * filename ) { +#endif + blob_t * result = 0; + + blob_t * blob = 0; + FILE * file = 0; + long tell_result = 0; + + blob = malloc( sizeof( blob_t ) ); + if ( !blob ) { + goto fail; + } + memset( blob, 0, sizeof( blob_t ) ); + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + file = _wfopen( filename, L"rb" ); +#else + file = fopen( filename, "rb" ); +#endif + if ( !file ) { + goto fail; + } + + if ( fseek( file, 0, SEEK_END ) != 0 ) { + goto fail; + } + + tell_result = ftell( file ); + if ( tell_result < 0 ) { + goto fail; + } + if ( (unsigned long)tell_result > SIZE_MAX ) { + goto fail; + } + blob->size = (size_t)tell_result; + + if ( fseek( file, 0, SEEK_SET ) != 0 ) { + goto fail; + } + + blob->data = malloc( blob->size ); + if ( !blob->data ) { + goto fail; + } + memset( blob->data, 0, blob->size ); + + if ( fread( blob->data, 1, blob->size, file ) != blob->size ) { + goto fail; + } + + result = blob; + blob = 0; + goto cleanup; + +fail: + + result = 0; + +cleanup: + + if ( blob ) { + free_blob( blob ); + blob = 0; + } + + if ( file ) { + fclose( file ); + file = 0; + } + + return result; +} + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + + int result = 0; + blob_t * blob = 0; + openmpt_module * mod = 0; + int mod_err = OPENMPT_ERROR_OK; + const char * mod_err_str = NULL; + size_t count = 0; + PaError pa_error = paNoError; + int pa_initialized = 0; + PaStream * stream = 0; + + if ( argc != 2 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_mem SOMEMODULE'." ); + goto fail; + } + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + if ( wcslen( argv[1] ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_mem SOMEMODULE'." ); + goto fail; + } +#else + if ( strlen( argv[1] ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_mem SOMEMODULE'." ); + goto fail; + } +#endif + blob = load_file( argv[1] ); + if ( !blob ) { + fprintf( stderr, "Error: %s\n", "load_file() failed." ); + goto fail; + } + + mod = openmpt_module_create_from_memory2( blob->data, blob->size, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL ); + if ( !mod ) { + libopenmpt_example_print_error( "openmpt_module_create_from_memory2()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + goto fail; + } + + pa_error = Pa_Initialize(); + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_Initialize() failed." ); + goto fail; + } + pa_initialized = 1; + + is_interleaved = 0; + pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + if ( pa_error == paSampleFormatNotSupported ) { + is_interleaved = 1; + pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + } + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); + goto fail; + } + if ( !stream ) { + fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); + goto fail; + } + + pa_error = Pa_StartStream( stream ); + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_StartStream() failed." ); + goto fail; + } + + while ( 1 ) { + + openmpt_module_error_clear( mod ); + count = is_interleaved ? openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, interleaved_buffer ) : openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); + mod_err = openmpt_module_error_get_last( mod ); + mod_err_str = openmpt_module_error_get_last_message( mod ); + if ( mod_err != OPENMPT_ERROR_OK ) { + libopenmpt_example_print_error( "openmpt_module_read_stereo()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + if ( count == 0 ) { + break; + } + + pa_error = is_interleaved ? Pa_WriteStream( stream, interleaved_buffer, (unsigned long)count ) : Pa_WriteStream( stream, buffers, (unsigned long)count ); + if ( pa_error == paOutputUnderflowed ) { + pa_error = paNoError; + } + if ( pa_error != paNoError ) { + fprintf( stderr, "Error: %s\n", "Pa_WriteStream() failed." ); + goto fail; + } + } + + result = 0; + + goto cleanup; + +fail: + + result = 1; + +cleanup: + + if ( stream ) { + if ( Pa_IsStreamActive( stream ) == 1 ) { + Pa_StopStream( stream ); + } + Pa_CloseStream( stream ); + stream = 0; + } + + if ( pa_initialized ) { + Pa_Terminate(); + pa_initialized = 0; + } + + if ( mod ) { + openmpt_module_destroy( mod ); + mod = 0; + } + + if ( blob ) { + free_blob( blob ); + blob = 0; + } + + return result; +} diff --git a/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_pipe.c b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_pipe.c new file mode 100644 index 000000000..201a914c9 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_pipe.c @@ -0,0 +1,152 @@ +/* + * libopenmpt_example_c_pipe.c + * --------------------------- + * Purpose: libopenmpt C API simple pipe example + * Notes : This example writes raw 48000Hz / stereo / 16bit native endian PCM data to stdout. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: cat SOMEMODULE | libopenmpt_example_c_pipe | aplay --file-type raw --format=dat + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define BUFFERSIZE 480 +#define SAMPLERATE 48000 + +static void libopenmpt_example_logfunc( const char * message, void * userdata ) { + (void)userdata; + if ( message ) { + fprintf( stderr, "openmpt: %s\n", message ); + } +} + +static int libopenmpt_example_errfunc( int error, void * userdata ) { + (void)userdata; + (void)error; + return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG; +} + +static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) { + if ( !func_name ) { + func_name = "unknown function"; + } + if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" ); + } else { + fprintf( stderr, "Error: %s\n", mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + } else { + if ( !mod_err_str ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s failed.\n", func_name ); + } else { + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } +} + +static ssize_t xwrite( int fd, const void * buffer, size_t size ) { + size_t written = 0; + ssize_t retval = 0; + while ( written < size ) { + retval = write( fd, (const char *)buffer + written, size - written ); + if ( retval < 0 ) { + if ( errno != EINTR ) { + break; + } + retval = 0; + } + written += retval; + } + return written; +} + +static int16_t buffer[BUFFERSIZE * 2]; + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + + int result = 0; + openmpt_module * mod = 0; + int mod_err = OPENMPT_ERROR_OK; + const char * mod_err_str = NULL; + size_t count = 0; + size_t written = 0; + (void)argv; + + if ( argc != 1 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_pipe' and connect stdin and stdout." ); + goto fail; + } + + mod = openmpt_module_create2( openmpt_stream_get_fd_callbacks(), (void*)(uintptr_t)STDIN_FILENO, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL ); + if ( !mod ) { + libopenmpt_example_print_error( "openmpt_module_create2()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + goto fail; + } + + while ( 1 ) { + + openmpt_module_error_clear( mod ); + count = openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, buffer ); + mod_err = openmpt_module_error_get_last( mod ); + mod_err_str = openmpt_module_error_get_last_message( mod ); + if ( mod_err != OPENMPT_ERROR_OK ) { + libopenmpt_example_print_error( "openmpt_module_read_interleaved_stereo()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + if ( count == 0 ) { + break; + } + + written = xwrite( STDOUT_FILENO, buffer, count * 2 * sizeof( int16_t ) ); + if ( written == 0 ) { + fprintf( stderr, "Error: %s\n", "write() failed." ); + goto fail; + } + } + + result = 0; + + goto cleanup; + +fail: + + result = 1; + +cleanup: + + if ( mod ) { + openmpt_module_destroy( mod ); + mod = 0; + } + + return result; +} diff --git a/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_probe.c b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_probe.c new file mode 100644 index 000000000..e4fbabcd3 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_probe.c @@ -0,0 +1,181 @@ +/* + * libopenmpt_example_c_probe.c + * ---------------------------- + * Purpose: libopenmpt C API probing example + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_example_c_probe SOMEMODULE ... + * Returns 0 on successful probing for all files. + * Returns 1 on failed probing for 1 or more files. + * Returns 2 on error. + */ + +#define LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY 1 +#define LIBOPENMPT_EXAMPLE_PROBE_RESULT_FLOAT 2 + +#define LIBOPENMPT_EXAMPLE_PROBE_RESULT LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY + +#include +#include +#include +#include +#include + +#include +#include + +static void libopenmpt_example_logfunc( const char * message, void * userdata ) { + (void)userdata; + + if ( message ) { + fprintf( stderr, "%s\n", message ); + } +} + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +static int probe_file( const wchar_t * filename ) { +#else +static int probe_file( const char * filename ) { +#endif + + int result = 0; + int mod_err = OPENMPT_ERROR_OK; + FILE * file = NULL; + +#if ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY ) + int result_binary = 0; + int probe_file_header_result = OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE; + const char * probe_file_header_result_str = NULL; +#endif +#if ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_FLOAT ) + double probability = 0.0; +#endif + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + if ( wcslen( filename ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_probe SOMEMODULE'." ); + goto fail; + } +#else + if ( strlen( filename ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_probe SOMEMODULE'." ); + goto fail; + } +#endif + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + file = _wfopen( filename, L"rb" ); +#else + file = fopen( filename, "rb" ); +#endif + if ( !file ) { + fprintf( stderr, "Error: %s\n", "fopen() failed." ); + goto fail; + } + + #if ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY ) + probe_file_header_result = openmpt_probe_file_header_from_stream( OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT, openmpt_stream_get_file_callbacks(), file, &libopenmpt_example_logfunc, NULL, &openmpt_error_func_default, NULL, &mod_err, NULL ); + probe_file_header_result_str = NULL; + result_binary = 0; + switch ( probe_file_header_result ) { + case OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS: + probe_file_header_result_str = "Success "; + result_binary = 1; + break; + case OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE: + probe_file_header_result_str = "Failure "; + result_binary = 0; + break; + case OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA: + probe_file_header_result_str = "WantMoreData"; + result_binary = 0; + break; + case OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR: + result_binary = 0; + fprintf( stderr, "Error: %s\n", "openmpt_probe_file_header() failed." ); + goto fail; + break; + default: + result_binary = 0; + fprintf( stderr, "Error: %s\n", "openmpt_probe_file_header() failed." ); + goto fail; + break; + } +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + fprintf( stdout, "%s - %ls\n", probe_file_header_result_str, filename ); +#else + fprintf( stdout, "%s - %s\n", probe_file_header_result_str, filename ); +#endif + if ( result_binary ) { + result = 0; + } else { + result = 1; + } + #elif ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_FLOAT ) + probability = openmpt_could_open_probability2( openmpt_stream_get_file_callbacks(), file, 0.25, &libopenmpt_example_logfunc, NULL, &openmpt_error_func_default, NULL, &mod_err, NULL ); +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + fprintf( stdout, "%s: %f - %ls\n", "Result", probability, filename ); +#else + fprintf( stdout, "%s: %f - %s\n", "Result", probability, filename ); +#endif + if ( probability >= 0.5 ) { + result = 0; + } else { + result = 1; + } + #else + #error "LIBOPENMPT_EXAMPLE_PROBE_RESULT is wrong" + #endif + + goto cleanup; + +fail: + + result = 2; + +cleanup: + + if ( file ) { + fclose( file ); + file = 0; + } + + return result; +} + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + + int global_result = 0; + + if ( argc <= 1 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_probe SOMEMODULE ...'." ); + goto fail; + } + + for ( int i = 1; i < argc; ++i ) { + int result = probe_file( argv[i] ); + if ( result > global_result ) { + global_result = result; + } + } + + goto cleanup; + +fail: + + global_result = 2; + +cleanup: + + return global_result; + +} + diff --git a/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_stdout.c b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_stdout.c new file mode 100644 index 000000000..1488737c3 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_stdout.c @@ -0,0 +1,176 @@ +/* + * libopenmpt_example_c_stdout.c + * ----------------------------- + * Purpose: libopenmpt C API simple example + * Notes : This example writes raw 48000Hz / stereo / 16bit native endian PCM data to stdout. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_example_c_stdout SOMEMODULE | aplay --file-type raw --format=dat + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define BUFFERSIZE 480 +#define SAMPLERATE 48000 + +static void libopenmpt_example_logfunc( const char * message, void * userdata ) { + (void)userdata; + if ( message ) { + fprintf( stderr, "openmpt: %s\n", message ); + } +} + +static int libopenmpt_example_errfunc( int error, void * userdata ) { + (void)userdata; + (void)error; + return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG; +} + +static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) { + if ( !func_name ) { + func_name = "unknown function"; + } + if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" ); + } else { + fprintf( stderr, "Error: %s\n", mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + } else { + if ( !mod_err_str ) { + mod_err_str = openmpt_error_string( mod_err ); + if ( !mod_err_str ) { + fprintf( stderr, "Error: %s failed.\n", func_name ); + } else { + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str ); + } +} + +static ssize_t xwrite( int fd, const void * buffer, size_t size ) { + size_t written = 0; + ssize_t retval = 0; + while ( written < size ) { + retval = write( fd, (const char *)buffer + written, size - written ); + if ( retval < 0 ) { + if ( errno != EINTR ) { + break; + } + retval = 0; + } + written += retval; + } + return written; +} + +static int16_t buffer[BUFFERSIZE * 2]; + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + + int result = 0; + FILE * file = 0; + openmpt_module * mod = 0; + int mod_err = OPENMPT_ERROR_OK; + const char * mod_err_str = NULL; + size_t count = 0; + size_t written = 0; + + if ( argc != 2 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_stdout SOMEMODULE'." ); + goto fail; + } + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + if ( wcslen( argv[1] ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_stdout SOMEMODULE'." ); + goto fail; + } + file = _wfopen( argv[1], L"rb" ); +#else + if ( strlen( argv[1] ) == 0 ) { + fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_stdout SOMEMODULE'." ); + goto fail; + } + file = fopen( argv[1], "rb" ); +#endif + if ( !file ) { + fprintf( stderr, "Error: %s\n", "fopen() failed." ); + goto fail; + } + + mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL ); + if ( !mod ) { + libopenmpt_example_print_error( "openmpt_module_create2()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + goto fail; + } + + while ( 1 ) { + + openmpt_module_error_clear( mod ); + count = openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, buffer ); + mod_err = openmpt_module_error_get_last( mod ); + mod_err_str = openmpt_module_error_get_last_message( mod ); + if ( mod_err != OPENMPT_ERROR_OK ) { + libopenmpt_example_print_error( "openmpt_module_read_interleaved_stereo()", mod_err, mod_err_str ); + openmpt_free_string( mod_err_str ); + mod_err_str = NULL; + } + if ( count == 0 ) { + break; + } + + written = xwrite( STDOUT_FILENO, buffer, count * 2 * sizeof( int16_t ) ); + if ( written == 0 ) { + fprintf( stderr, "Error: %s\n", "write() failed." ); + goto fail; + } + } + + result = 0; + + goto cleanup; + +fail: + + result = 1; + +cleanup: + + if ( mod ) { + openmpt_module_destroy( mod ); + mod = 0; + } + + if ( file ) { + fclose( file ); + file = 0; + } + + return result; +} diff --git a/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_unsafe.c b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_unsafe.c new file mode 100644 index 000000000..0419ac3b0 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_c_unsafe.c @@ -0,0 +1,65 @@ +/* + * libopenmpt_example_c_unsafe.c + * ----------------------------- + * Purpose: libopenmpt C API simplified example + * Notes : PortAudio is used for sound output. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_example_c_unsafe SOMEMODULE + * CAUTION: This simple example does no error cheking at all. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define BUFFERSIZE 480 +#define SAMPLERATE 48000 + +static int16_t left[BUFFERSIZE]; +static int16_t right[BUFFERSIZE]; +static int16_t * const buffers[2] = { left, right }; + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +int wmain( int argc, wchar_t * argv[] ) { +#else +int main( int argc, char * argv[] ) { +#endif + FILE * file = 0; + openmpt_module * mod = 0; + size_t count = 0; + PaStream * stream = 0; + (void)argc; +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) + file = _wfopen( argv[1], L"rb" ); +#else + file = fopen( argv[1], "rb" ); +#endif + mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, NULL, NULL, NULL, NULL, NULL, NULL, NULL ); + fclose( file ); + Pa_Initialize(); + Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + Pa_StartStream( stream ); + while ( 1 ) { + count = openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); + if ( count == 0 ) { + break; + } + Pa_WriteStream( stream, buffers, (unsigned long)count ); + } + Pa_StopStream( stream ); + Pa_CloseStream( stream ); + Pa_Terminate(); + openmpt_module_destroy( mod ); + return 0; +} diff --git a/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_cxx.cpp b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_cxx.cpp new file mode 100644 index 000000000..199308248 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/examples/libopenmpt_example_cxx.cpp @@ -0,0 +1,112 @@ +/* + * libopenmpt_example_cxx.cpp + * -------------------------- + * Purpose: libopenmpt C++ API example + * Notes : PortAudio C++ is used for sound output. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +/* + * Usage: libopenmpt_example_cxx SOMEMODULE + */ + +#include +#include +#include +#include +#include +#include + +#include + +#if defined(__clang__) +#if ((__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) >= 40000) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" +#endif +#endif +#include +#if defined(__clang__) +#if ((__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) >= 40000) +#pragma clang diagnostic pop +#endif +#endif + +#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) +#if defined( __GNUC__ ) +// mingw-w64 g++ does only default to special C linkage for "main", but not for "wmain" (see ). +extern "C" int wmain( int argc, wchar_t * argv[] ) { +#else +int wmain( int argc, wchar_t * argv[] ) { +#endif +#else +int main( int argc, char * argv[] ) { +#endif + try { + if ( argc != 2 ) { + throw std::runtime_error( "Usage: libopenmpt_example_cxx SOMEMODULE" ); + } + constexpr std::size_t buffersize = 480; + constexpr std::int32_t samplerate = 48000; + std::ifstream file( argv[1], std::ios::binary ); + openmpt::module mod( file ); + portaudio::AutoSystem portaudio_initializer; + portaudio::System & portaudio = portaudio::System::instance(); + std::vector left( buffersize ); + std::vector right( buffersize ); + std::vector interleaved_buffer( buffersize * 2 ); + bool is_interleaved = false; +#if defined(_MSC_VER) && defined(_PREFAST_) + // work-around bug in VS2019 MSVC 16.5.5 static analyzer + is_interleaved = false; + portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, false, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 ); + portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag ); + portaudio::BlockingStream stream( stream_parameters ); +#else + portaudio::BlockingStream stream = [&]() { + try { + is_interleaved = false; + portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, false, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 ); + portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag ); + return portaudio::BlockingStream( stream_parameters ); + } catch ( const portaudio::PaException & e ) { + if ( e.paError() != paSampleFormatNotSupported ) { + throw; + } + is_interleaved = true; + portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, true, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 ); + portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag ); + return portaudio::BlockingStream( stream_parameters ); + } + }(); +#endif + stream.start(); + while ( true ) { + std::size_t count = is_interleaved ? mod.read_interleaved_stereo( samplerate, buffersize, interleaved_buffer.data() ) : mod.read( samplerate, buffersize, left.data(), right.data() ); + if ( count == 0 ) { + break; + } + try { + if ( is_interleaved ) { + stream.write( interleaved_buffer.data(), static_cast( count ) ); + } else { + const float * const buffers[2] = { left.data(), right.data() }; + stream.write( buffers, static_cast( count ) ); + } + } catch ( const portaudio::PaException & pa_exception ) { + if ( pa_exception.paError() != paOutputUnderflowed ) { + throw; + } + } + } + stream.stop(); + } catch ( const std::bad_alloc & ) { + std::cerr << "Error: " << std::string( "out of memory" ) << std::endl; + return 1; + } catch ( const std::exception & e ) { + std::cerr << "Error: " << std::string( e.what() ? e.what() : "unknown error" ) << std::endl; + return 1; + } + return 0; +} diff --git a/Frameworks/OpenMPT.old/OpenMPT/include/minimp3/LICENSE b/Frameworks/OpenMPT.old/OpenMPT/include/minimp3/LICENSE new file mode 100644 index 000000000..2c4afabdb --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/include/minimp3/LICENSE @@ -0,0 +1,117 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + + diff --git a/Frameworks/OpenMPT.old/OpenMPT/include/minimp3/OpenMPT.txt b/Frameworks/OpenMPT.old/OpenMPT/include/minimp3/OpenMPT.txt new file mode 100644 index 000000000..357607664 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/include/minimp3/OpenMPT.txt @@ -0,0 +1,4 @@ +minimp3 library from https://github.com/lieff/minimp3 +commit 50d2aaf360a53653b718fead8e258d654c3a7e41 (2021-11-27) +The following changes have been made: + * minimp3.c has been added diff --git a/Frameworks/OpenMPT.old/OpenMPT/include/minimp3/minimp3.c b/Frameworks/OpenMPT.old/OpenMPT/include/minimp3/minimp3.c new file mode 100644 index 000000000..08c2661d0 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/include/minimp3/minimp3.c @@ -0,0 +1,3 @@ +/* #define MINIMP3_NO_SIMD */ +#define MINIMP3_IMPLEMENTATION +#include "minimp3.h" diff --git a/Frameworks/OpenMPT.old/OpenMPT/include/minimp3/minimp3.h b/Frameworks/OpenMPT.old/OpenMPT/include/minimp3/minimp3.h new file mode 100644 index 000000000..3220ae1a8 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/include/minimp3/minimp3.h @@ -0,0 +1,1865 @@ +#ifndef MINIMP3_H +#define MINIMP3_H +/* + https://github.com/lieff/minimp3 + To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. + This software is distributed without any warranty. + See . +*/ +#include + +#define MINIMP3_MAX_SAMPLES_PER_FRAME (1152*2) + +typedef struct +{ + int frame_bytes, frame_offset, channels, hz, layer, bitrate_kbps; +} mp3dec_frame_info_t; + +typedef struct +{ + float mdct_overlap[2][9*32], qmf_state[15*2*32]; + int reserv, free_format_bytes; + unsigned char header[4], reserv_buf[511]; +} mp3dec_t; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +void mp3dec_init(mp3dec_t *dec); +#ifndef MINIMP3_FLOAT_OUTPUT +typedef int16_t mp3d_sample_t; +#else /* MINIMP3_FLOAT_OUTPUT */ +typedef float mp3d_sample_t; +void mp3dec_f32_to_s16(const float *in, int16_t *out, int num_samples); +#endif /* MINIMP3_FLOAT_OUTPUT */ +int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_sample_t *pcm, mp3dec_frame_info_t *info); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MINIMP3_H */ +#if defined(MINIMP3_IMPLEMENTATION) && !defined(_MINIMP3_IMPLEMENTATION_GUARD) +#define _MINIMP3_IMPLEMENTATION_GUARD + +#include +#include + +#define MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ +#ifndef MAX_FRAME_SYNC_MATCHES +#define MAX_FRAME_SYNC_MATCHES 10 +#endif /* MAX_FRAME_SYNC_MATCHES */ + +#define MAX_L3_FRAME_PAYLOAD_BYTES MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ + +#define MAX_BITRESERVOIR_BYTES 511 +#define SHORT_BLOCK_TYPE 2 +#define STOP_BLOCK_TYPE 3 +#define MODE_MONO 3 +#define MODE_JOINT_STEREO 1 +#define HDR_SIZE 4 +#define HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) +#define HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) +#define HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) +#define HDR_IS_CRC(h) (!((h[1]) & 1)) +#define HDR_TEST_PADDING(h) ((h[2]) & 0x2) +#define HDR_TEST_MPEG1(h) ((h[1]) & 0x8) +#define HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) +#define HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) +#define HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) +#define HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) +#define HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) +#define HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) +#define HDR_GET_BITRATE(h) ((h[2]) >> 4) +#define HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) +#define HDR_GET_MY_SAMPLE_RATE(h) (HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) +#define HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) +#define HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) + +#define BITS_DEQUANTIZER_OUT -1 +#define MAX_SCF (255 + BITS_DEQUANTIZER_OUT*4 - 210) +#define MAX_SCFI ((MAX_SCF + 3) & ~3) + +#define MINIMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) +#define MINIMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) + +#if !defined(MINIMP3_NO_SIMD) + +#if !defined(MINIMP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) +/* x64 always have SSE2, arm64 always have neon, no need for generic code */ +#define MINIMP3_ONLY_SIMD +#endif /* SIMD checks... */ + +#if (defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__)) +#if defined(_MSC_VER) +#include +#endif /* defined(_MSC_VER) */ +#include +#define HAVE_SSE 1 +#define HAVE_SIMD 1 +#define VSTORE _mm_storeu_ps +#define VLD _mm_loadu_ps +#define VSET _mm_set1_ps +#define VADD _mm_add_ps +#define VSUB _mm_sub_ps +#define VMUL _mm_mul_ps +#define VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) +#define VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) +#define VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) +#define VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) +typedef __m128 f4; +#if defined(_MSC_VER) || defined(MINIMP3_ONLY_SIMD) +#define minimp3_cpuid __cpuid +#else /* defined(_MSC_VER) || defined(MINIMP3_ONLY_SIMD) */ +static __inline__ __attribute__((always_inline)) void minimp3_cpuid(int CPUInfo[], const int InfoType) +{ +#if defined(__PIC__) + __asm__ __volatile__( +#if defined(__x86_64__) + "push %%rbx\n" + "cpuid\n" + "xchgl %%ebx, %1\n" + "pop %%rbx\n" +#else /* defined(__x86_64__) */ + "xchgl %%ebx, %1\n" + "cpuid\n" + "xchgl %%ebx, %1\n" +#endif /* defined(__x86_64__) */ + : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#else /* defined(__PIC__) */ + __asm__ __volatile__( + "cpuid" + : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#endif /* defined(__PIC__)*/ +} +#endif /* defined(_MSC_VER) || defined(MINIMP3_ONLY_SIMD) */ +static int have_simd(void) +{ +#ifdef MINIMP3_ONLY_SIMD + return 1; +#else /* MINIMP3_ONLY_SIMD */ + static int g_have_simd; + int CPUInfo[4]; +#ifdef MINIMP3_TEST + static int g_counter; + if (g_counter++ > 100) + return 0; +#endif /* MINIMP3_TEST */ + if (g_have_simd) + goto end; + minimp3_cpuid(CPUInfo, 0); + g_have_simd = 1; + if (CPUInfo[0] > 0) + { + minimp3_cpuid(CPUInfo, 1); + g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; /* SSE2 */ + } +end: + return g_have_simd - 1; +#endif /* MINIMP3_ONLY_SIMD */ +} +#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) +#include +#define HAVE_SSE 0 +#define HAVE_SIMD 1 +#define VSTORE vst1q_f32 +#define VLD vld1q_f32 +#define VSET vmovq_n_f32 +#define VADD vaddq_f32 +#define VSUB vsubq_f32 +#define VMUL vmulq_f32 +#define VMAC(a, x, y) vmlaq_f32(a, x, y) +#define VMSB(a, x, y) vmlsq_f32(a, x, y) +#define VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) +#define VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) +typedef float32x4_t f4; +static int have_simd() +{ /* TODO: detect neon for !MINIMP3_ONLY_SIMD */ + return 1; +} +#else /* SIMD checks... */ +#define HAVE_SSE 0 +#define HAVE_SIMD 0 +#ifdef MINIMP3_ONLY_SIMD +#error MINIMP3_ONLY_SIMD used, but SSE/NEON not enabled +#endif /* MINIMP3_ONLY_SIMD */ +#endif /* SIMD checks... */ +#else /* !defined(MINIMP3_NO_SIMD) */ +#define HAVE_SIMD 0 +#endif /* !defined(MINIMP3_NO_SIMD) */ + +#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) +#define HAVE_ARMV6 1 +static __inline__ __attribute__((always_inline)) int32_t minimp3_clip_int16_arm(int32_t a) +{ + int32_t x = 0; + __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); + return x; +} +#else +#define HAVE_ARMV6 0 +#endif + +typedef struct +{ + const uint8_t *buf; + int pos, limit; +} bs_t; + +typedef struct +{ + float scf[3*64]; + uint8_t total_bands, stereo_bands, bitalloc[64], scfcod[64]; +} L12_scale_info; + +typedef struct +{ + uint8_t tab_offset, code_tab_width, band_count; +} L12_subband_alloc_t; + +typedef struct +{ + const uint8_t *sfbtab; + uint16_t part_23_length, big_values, scalefac_compress; + uint8_t global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + uint8_t table_select[3], region_count[3], subblock_gain[3]; + uint8_t preflag, scalefac_scale, count1_table, scfsi; +} L3_gr_info_t; + +typedef struct +{ + bs_t bs; + uint8_t maindata[MAX_BITRESERVOIR_BYTES + MAX_L3_FRAME_PAYLOAD_BYTES]; + L3_gr_info_t gr_info[4]; + float grbuf[2][576], scf[40], syn[18 + 15][2*32]; + uint8_t ist_pos[2][39]; +} mp3dec_scratch_t; + +static void bs_init(bs_t *bs, const uint8_t *data, int bytes) +{ + bs->buf = data; + bs->pos = 0; + bs->limit = bytes*8; +} + +static uint32_t get_bits(bs_t *bs, int n) +{ + uint32_t next, cache = 0, s = bs->pos & 7; + int shl = n + s; + const uint8_t *p = bs->buf + (bs->pos >> 3); + if ((bs->pos += n) > bs->limit) + return 0; + next = *p++ & (255 >> s); + while ((shl -= 8) > 0) + { + cache |= next << shl; + next = *p++; + } + return cache | (next >> -shl); +} + +static int hdr_valid(const uint8_t *h) +{ + return h[0] == 0xff && + ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && + (HDR_GET_LAYER(h) != 0) && + (HDR_GET_BITRATE(h) != 15) && + (HDR_GET_SAMPLE_RATE(h) != 3); +} + +static int hdr_compare(const uint8_t *h1, const uint8_t *h2) +{ + return hdr_valid(h2) && + ((h1[1] ^ h2[1]) & 0xFE) == 0 && + ((h1[2] ^ h2[2]) & 0x0C) == 0 && + !(HDR_IS_FREE_FORMAT(h1) ^ HDR_IS_FREE_FORMAT(h2)); +} + +static unsigned hdr_bitrate_kbps(const uint8_t *h) +{ + static const uint8_t halfrate[2][3][15] = { + { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, + { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, + }; + return 2*halfrate[!!HDR_TEST_MPEG1(h)][HDR_GET_LAYER(h) - 1][HDR_GET_BITRATE(h)]; +} + +static unsigned hdr_sample_rate_hz(const uint8_t *h) +{ + static const unsigned g_hz[3] = { 44100, 48000, 32000 }; + return g_hz[HDR_GET_SAMPLE_RATE(h)] >> (int)!HDR_TEST_MPEG1(h) >> (int)!HDR_TEST_NOT_MPEG25(h); +} + +static unsigned hdr_frame_samples(const uint8_t *h) +{ + return HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)HDR_IS_FRAME_576(h)); +} + +static int hdr_frame_bytes(const uint8_t *h, int free_format_size) +{ + int frame_bytes = hdr_frame_samples(h)*hdr_bitrate_kbps(h)*125/hdr_sample_rate_hz(h); + if (HDR_IS_LAYER_1(h)) + { + frame_bytes &= ~3; /* slot align */ + } + return frame_bytes ? frame_bytes : free_format_size; +} + +static int hdr_padding(const uint8_t *h) +{ + return HDR_TEST_PADDING(h) ? (HDR_IS_LAYER_1(h) ? 4 : 1) : 0; +} + +#ifndef MINIMP3_ONLY_MP3 +static const L12_subband_alloc_t *L12_subband_alloc_table(const uint8_t *hdr, L12_scale_info *sci) +{ + const L12_subband_alloc_t *alloc; + int mode = HDR_GET_STEREO_MODE(hdr); + int nbands, stereo_bands = (mode == MODE_MONO) ? 0 : (mode == MODE_JOINT_STEREO) ? (HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; + + if (HDR_IS_LAYER_1(hdr)) + { + static const L12_subband_alloc_t g_alloc_L1[] = { { 76, 4, 32 } }; + alloc = g_alloc_L1; + nbands = 32; + } else if (!HDR_TEST_MPEG1(hdr)) + { + static const L12_subband_alloc_t g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; + alloc = g_alloc_L2M2; + nbands = 30; + } else + { + static const L12_subband_alloc_t g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; + int sample_rate_idx = HDR_GET_SAMPLE_RATE(hdr); + unsigned kbps = hdr_bitrate_kbps(hdr) >> (int)(mode != MODE_MONO); + if (!kbps) /* free-format */ + { + kbps = 192; + } + + alloc = g_alloc_L2M1; + nbands = 27; + if (kbps < 56) + { + static const L12_subband_alloc_t g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; + alloc = g_alloc_L2M1_lowrate; + nbands = sample_rate_idx == 2 ? 12 : 8; + } else if (kbps >= 96 && sample_rate_idx != 1) + { + nbands = 30; + } + } + + sci->total_bands = (uint8_t)nbands; + sci->stereo_bands = (uint8_t)MINIMP3_MIN(stereo_bands, nbands); + + return alloc; +} + +static void L12_read_scalefactors(bs_t *bs, uint8_t *pba, uint8_t *scfcod, int bands, float *scf) +{ + static const float g_deq_L12[18*3] = { +#define DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x + DQ(3),DQ(7),DQ(15),DQ(31),DQ(63),DQ(127),DQ(255),DQ(511),DQ(1023),DQ(2047),DQ(4095),DQ(8191),DQ(16383),DQ(32767),DQ(65535),DQ(3),DQ(5),DQ(9) + }; + int i, m; + for (i = 0; i < bands; i++) + { + float s = 0; + int ba = *pba++; + int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; + for (m = 4; m; m >>= 1) + { + if (mask & m) + { + int b = get_bits(bs, 6); + s = g_deq_L12[ba*3 - 6 + b % 3]*(1 << 21 >> b/3); + } + *scf++ = s; + } + } +} + +static void L12_read_scale_info(const uint8_t *hdr, bs_t *bs, L12_scale_info *sci) +{ + static const uint8_t g_bitalloc_code_tab[] = { + 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, + 0,17,18, 3,19,4,5,16, + 0,17,18,16, + 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, + 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 + }; + const L12_subband_alloc_t *subband_alloc = L12_subband_alloc_table(hdr, sci); + + int i, k = 0, ba_bits = 0; + const uint8_t *ba_code_tab = g_bitalloc_code_tab; + + for (i = 0; i < sci->total_bands; i++) + { + uint8_t ba; + if (i == k) + { + k += subband_alloc->band_count; + ba_bits = subband_alloc->code_tab_width; + ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; + subband_alloc++; + } + ba = ba_code_tab[get_bits(bs, ba_bits)]; + sci->bitalloc[2*i] = ba; + if (i < sci->stereo_bands) + { + ba = ba_code_tab[get_bits(bs, ba_bits)]; + } + sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; + } + + for (i = 0; i < 2*sci->total_bands; i++) + { + sci->scfcod[i] = sci->bitalloc[i] ? HDR_IS_LAYER_1(hdr) ? 2 : get_bits(bs, 2) : 6; + } + + L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); + + for (i = sci->stereo_bands; i < sci->total_bands; i++) + { + sci->bitalloc[2*i + 1] = 0; + } +} + +static int L12_dequantize_granule(float *grbuf, bs_t *bs, L12_scale_info *sci, int group_size) +{ + int i, j, k, choff = 576; + for (j = 0; j < 4; j++) + { + float *dst = grbuf + group_size*j; + for (i = 0; i < 2*sci->total_bands; i++) + { + int ba = sci->bitalloc[i]; + if (ba != 0) + { + if (ba < 17) + { + int half = (1 << (ba - 1)) - 1; + for (k = 0; k < group_size; k++) + { + dst[k] = (float)((int)get_bits(bs, ba) - half); + } + } else + { + unsigned mod = (2 << (ba - 17)) + 1; /* 3, 5, 9 */ + unsigned code = get_bits(bs, mod + 2 - (mod >> 3)); /* 5, 7, 10 */ + for (k = 0; k < group_size; k++, code /= mod) + { + dst[k] = (float)((int)(code % mod - mod/2)); + } + } + } + dst += choff; + choff = 18 - choff; + } + } + return group_size*4; +} + +static void L12_apply_scf_384(L12_scale_info *sci, const float *scf, float *dst) +{ + int i, k; + memcpy(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); + for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) + { + for (k = 0; k < 12; k++) + { + dst[k + 0] *= scf[0]; + dst[k + 576] *= scf[3]; + } + } +} +#endif /* MINIMP3_ONLY_MP3 */ + +static int L3_read_side_info(bs_t *bs, L3_gr_info_t *gr, const uint8_t *hdr) +{ + static const uint8_t g_scf_long[8][23] = { + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, + { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, + { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } + }; + static const uint8_t g_scf_short[8][40] = { + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + static const uint8_t g_scf_mixed[8][40] = { + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + + unsigned tables, scfsi = 0; + int main_data_begin, part_23_sum = 0; + int sr_idx = HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); + int gr_count = HDR_IS_MONO(hdr) ? 1 : 2; + + if (HDR_TEST_MPEG1(hdr)) + { + gr_count *= 2; + main_data_begin = get_bits(bs, 9); + scfsi = get_bits(bs, 7 + gr_count); + } else + { + main_data_begin = get_bits(bs, 8 + gr_count) >> gr_count; + } + + do + { + if (HDR_IS_MONO(hdr)) + { + scfsi <<= 4; + } + gr->part_23_length = (uint16_t)get_bits(bs, 12); + part_23_sum += gr->part_23_length; + gr->big_values = (uint16_t)get_bits(bs, 9); + if (gr->big_values > 288) + { + return -1; + } + gr->global_gain = (uint8_t)get_bits(bs, 8); + gr->scalefac_compress = (uint16_t)get_bits(bs, HDR_TEST_MPEG1(hdr) ? 4 : 9); + gr->sfbtab = g_scf_long[sr_idx]; + gr->n_long_sfb = 22; + gr->n_short_sfb = 0; + if (get_bits(bs, 1)) + { + gr->block_type = (uint8_t)get_bits(bs, 2); + if (!gr->block_type) + { + return -1; + } + gr->mixed_block_flag = (uint8_t)get_bits(bs, 1); + gr->region_count[0] = 7; + gr->region_count[1] = 255; + if (gr->block_type == SHORT_BLOCK_TYPE) + { + scfsi &= 0x0F0F; + if (!gr->mixed_block_flag) + { + gr->region_count[0] = 8; + gr->sfbtab = g_scf_short[sr_idx]; + gr->n_long_sfb = 0; + gr->n_short_sfb = 39; + } else + { + gr->sfbtab = g_scf_mixed[sr_idx]; + gr->n_long_sfb = HDR_TEST_MPEG1(hdr) ? 8 : 6; + gr->n_short_sfb = 30; + } + } + tables = get_bits(bs, 10); + tables <<= 5; + gr->subblock_gain[0] = (uint8_t)get_bits(bs, 3); + gr->subblock_gain[1] = (uint8_t)get_bits(bs, 3); + gr->subblock_gain[2] = (uint8_t)get_bits(bs, 3); + } else + { + gr->block_type = 0; + gr->mixed_block_flag = 0; + tables = get_bits(bs, 15); + gr->region_count[0] = (uint8_t)get_bits(bs, 4); + gr->region_count[1] = (uint8_t)get_bits(bs, 3); + gr->region_count[2] = 255; + } + gr->table_select[0] = (uint8_t)(tables >> 10); + gr->table_select[1] = (uint8_t)((tables >> 5) & 31); + gr->table_select[2] = (uint8_t)((tables) & 31); + gr->preflag = HDR_TEST_MPEG1(hdr) ? get_bits(bs, 1) : (gr->scalefac_compress >= 500); + gr->scalefac_scale = (uint8_t)get_bits(bs, 1); + gr->count1_table = (uint8_t)get_bits(bs, 1); + gr->scfsi = (uint8_t)((scfsi >> 12) & 15); + scfsi <<= 4; + gr++; + } while(--gr_count); + + if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) + { + return -1; + } + + return main_data_begin; +} + +static void L3_read_scalefactors(uint8_t *scf, uint8_t *ist_pos, const uint8_t *scf_size, const uint8_t *scf_count, bs_t *bitbuf, int scfsi) +{ + int i, k; + for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) + { + int cnt = scf_count[i]; + if (scfsi & 8) + { + memcpy(scf, ist_pos, cnt); + } else + { + int bits = scf_size[i]; + if (!bits) + { + memset(scf, 0, cnt); + memset(ist_pos, 0, cnt); + } else + { + int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; + for (k = 0; k < cnt; k++) + { + int s = get_bits(bitbuf, bits); + ist_pos[k] = (s == max_scf ? -1 : s); + scf[k] = s; + } + } + } + ist_pos += cnt; + scf += cnt; + } + scf[0] = scf[1] = scf[2] = 0; +} + +static float L3_ldexp_q2(float y, int exp_q2) +{ + static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; + int e; + do + { + e = MINIMP3_MIN(30*4, exp_q2); + y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); + } while ((exp_q2 -= e) > 0); + return y; +} + +static void L3_decode_scalefactors(const uint8_t *hdr, uint8_t *ist_pos, bs_t *bs, const L3_gr_info_t *gr, float *scf, int ch) +{ + static const uint8_t g_scf_partitions[3][28] = { + { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, + { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, + { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } + }; + const uint8_t *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; + uint8_t scf_size[4], iscf[40]; + int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; + float gain; + + if (HDR_TEST_MPEG1(hdr)) + { + static const uint8_t g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; + int part = g_scfc_decode[gr->scalefac_compress]; + scf_size[1] = scf_size[0] = (uint8_t)(part >> 2); + scf_size[3] = scf_size[2] = (uint8_t)(part & 3); + } else + { + static const uint8_t g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; + int k, modprod, sfc, ist = HDR_TEST_I_STEREO(hdr) && ch; + sfc = gr->scalefac_compress >> ist; + for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) + { + for (modprod = 1, i = 3; i >= 0; i--) + { + scf_size[i] = (uint8_t)(sfc / modprod % g_mod[k + i]); + modprod *= g_mod[k + i]; + } + } + scf_partition += k; + scfsi = -16; + } + L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); + + if (gr->n_short_sfb) + { + int sh = 3 - scf_shift; + for (i = 0; i < gr->n_short_sfb; i += 3) + { + iscf[gr->n_long_sfb + i + 0] += gr->subblock_gain[0] << sh; + iscf[gr->n_long_sfb + i + 1] += gr->subblock_gain[1] << sh; + iscf[gr->n_long_sfb + i + 2] += gr->subblock_gain[2] << sh; + } + } else if (gr->preflag) + { + static const uint8_t g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; + for (i = 0; i < 10; i++) + { + iscf[11 + i] += g_preamp[i]; + } + } + + gain_exp = gr->global_gain + BITS_DEQUANTIZER_OUT*4 - 210 - (HDR_IS_MS_STEREO(hdr) ? 2 : 0); + gain = L3_ldexp_q2(1 << (MAX_SCFI/4), MAX_SCFI - gain_exp); + for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) + { + scf[i] = L3_ldexp_q2(gain, iscf[i] << scf_shift); + } +} + +static const float g_pow43[129 + 16] = { + 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, + 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f +}; + +static float L3_pow_43(int x) +{ + float frac; + int sign, mult = 256; + + if (x < 129) + { + return g_pow43[16 + x]; + } + + if (x < 1024) + { + mult = 16; + x <<= 3; + } + + sign = 2*x & 64; + frac = (float)((x & 63) - sign) / ((x & ~63) + sign); + return g_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; +} + +static void L3_huffman(float *dst, bs_t *bs, const L3_gr_info_t *gr_info, const float *scf, int layer3gr_limit) +{ + static const int16_t tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, + -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, + -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, + -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, + -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, + -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, + -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, + -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, + -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, + -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, + -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, + -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, + -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, + -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, + -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; + static const uint8_t tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205 }; + static const uint8_t tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; + static const int16_t tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; + static const uint8_t g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; + +#define PEEK_BITS(n) (bs_cache >> (32 - n)) +#define FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } +#define CHECK_BITS while (bs_sh >= 0) { bs_cache |= (uint32_t)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } +#define BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) + + float one = 0.0f; + int ireg = 0, big_val_cnt = gr_info->big_values; + const uint8_t *sfb = gr_info->sfbtab; + const uint8_t *bs_next_ptr = bs->buf + bs->pos/8; + uint32_t bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); + int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; + bs_next_ptr += 4; + + while (big_val_cnt > 0) + { + int tab_num = gr_info->table_select[ireg]; + int sfb_cnt = gr_info->region_count[ireg++]; + const int16_t *codebook = tabs + tabindex[tab_num]; + int linbits = g_linbits[tab_num]; + if (linbits) + { + do + { + np = *sfb++ / 2; + pairs_to_decode = MINIMP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[PEEK_BITS(w)]; + while (leaf < 0) + { + FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[PEEK_BITS(w) - (leaf >> 3)]; + } + FLUSH_BITS(leaf >> 8); + + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + if (lsb == 15) + { + lsb += PEEK_BITS(linbits); + FLUSH_BITS(linbits); + CHECK_BITS; + *dst = one*L3_pow_43(lsb)*((int32_t)bs_cache < 0 ? -1: 1); + } else + { + *dst = g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + } + FLUSH_BITS(lsb ? 1 : 0); + } + CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } else + { + do + { + np = *sfb++ / 2; + pairs_to_decode = MINIMP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[PEEK_BITS(w)]; + while (leaf < 0) + { + FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[PEEK_BITS(w) - (leaf >> 3)]; + } + FLUSH_BITS(leaf >> 8); + + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + *dst = g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + FLUSH_BITS(lsb ? 1 : 0); + } + CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } + } + + for (np = 1 - big_val_cnt;; dst += 4) + { + const uint8_t *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; + int leaf = codebook_count1[PEEK_BITS(4)]; + if (!(leaf & 8)) + { + leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; + } + FLUSH_BITS(leaf & 7); + if (BSPOS > layer3gr_limit) + { + break; + } +#define RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } +#define DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((int32_t)bs_cache < 0) ? -one : one; FLUSH_BITS(1) } + RELOAD_SCALEFACTOR; + DEQ_COUNT1(0); + DEQ_COUNT1(1); + RELOAD_SCALEFACTOR; + DEQ_COUNT1(2); + DEQ_COUNT1(3); + CHECK_BITS; + } + + bs->pos = layer3gr_limit; +} + +static void L3_midside_stereo(float *left, int n) +{ + int i = 0; + float *right = left + 576; +#if HAVE_SIMD + if (have_simd()) + { + for (; i < n - 3; i += 4) + { + f4 vl = VLD(left + i); + f4 vr = VLD(right + i); + VSTORE(left + i, VADD(vl, vr)); + VSTORE(right + i, VSUB(vl, vr)); + } +#ifdef __GNUC__ + /* Workaround for spurious -Waggressive-loop-optimizations warning from gcc. + * For more info see: https://github.com/lieff/minimp3/issues/88 + */ + if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) + return; +#endif + } +#endif /* HAVE_SIMD */ + for (; i < n; i++) + { + float a = left[i]; + float b = right[i]; + left[i] = a + b; + right[i] = a - b; + } +} + +static void L3_intensity_stereo_band(float *left, int n, float kl, float kr) +{ + int i; + for (i = 0; i < n; i++) + { + left[i + 576] = left[i]*kr; + left[i] = left[i]*kl; + } +} + +static void L3_stereo_top_band(const float *right, const uint8_t *sfb, int nbands, int max_band[3]) +{ + int i, k; + + max_band[0] = max_band[1] = max_band[2] = -1; + + for (i = 0; i < nbands; i++) + { + for (k = 0; k < sfb[i]; k += 2) + { + if (right[k] != 0 || right[k + 1] != 0) + { + max_band[i % 3] = i; + break; + } + } + right += sfb[i]; + } +} + +static void L3_stereo_process(float *left, const uint8_t *ist_pos, const uint8_t *sfb, const uint8_t *hdr, int max_band[3], int mpeg2_sh) +{ + static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; + unsigned i, max_pos = HDR_TEST_MPEG1(hdr) ? 7 : 64; + + for (i = 0; sfb[i]; i++) + { + unsigned ipos = ist_pos[i]; + if ((int)i > max_band[i % 3] && ipos < max_pos) + { + float kl, kr, s = HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; + if (HDR_TEST_MPEG1(hdr)) + { + kl = g_pan[2*ipos]; + kr = g_pan[2*ipos + 1]; + } else + { + kl = 1; + kr = L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); + if (ipos & 1) + { + kl = kr; + kr = 1; + } + } + L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); + } else if (HDR_TEST_MS_STEREO(hdr)) + { + L3_midside_stereo(left, sfb[i]); + } + left += sfb[i]; + } +} + +static void L3_intensity_stereo(float *left, uint8_t *ist_pos, const L3_gr_info_t *gr, const uint8_t *hdr) +{ + int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; + int i, max_blocks = gr->n_short_sfb ? 3 : 1; + + L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); + if (gr->n_long_sfb) + { + max_band[0] = max_band[1] = max_band[2] = MINIMP3_MAX(MINIMP3_MAX(max_band[0], max_band[1]), max_band[2]); + } + for (i = 0; i < max_blocks; i++) + { + int default_pos = HDR_TEST_MPEG1(hdr) ? 3 : 0; + int itop = n_sfb - max_blocks + i; + int prev = itop - max_blocks; + ist_pos[itop] = max_band[i] >= prev ? default_pos : ist_pos[prev]; + } + L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); +} + +static void L3_reorder(float *grbuf, float *scratch, const uint8_t *sfb) +{ + int i, len; + float *src = grbuf, *dst = scratch; + + for (;0 != (len = *sfb); sfb += 3, src += 2*len) + { + for (i = 0; i < len; i++, src++) + { + *dst++ = src[0*len]; + *dst++ = src[1*len]; + *dst++ = src[2*len]; + } + } + memcpy(grbuf, scratch, (dst - scratch)*sizeof(float)); +} + +static void L3_antialias(float *grbuf, int nbands) +{ + static const float g_aa[2][8] = { + {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, + {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} + }; + + for (; nbands > 0; nbands--, grbuf += 18) + { + int i = 0; +#if HAVE_SIMD + if (have_simd()) for (; i < 8; i += 4) + { + f4 vu = VLD(grbuf + 18 + i); + f4 vd = VLD(grbuf + 14 - i); + f4 vc0 = VLD(g_aa[0] + i); + f4 vc1 = VLD(g_aa[1] + i); + vd = VREV(vd); + VSTORE(grbuf + 18 + i, VSUB(VMUL(vu, vc0), VMUL(vd, vc1))); + vd = VADD(VMUL(vu, vc1), VMUL(vd, vc0)); + VSTORE(grbuf + 14 - i, VREV(vd)); + } +#endif /* HAVE_SIMD */ +#ifndef MINIMP3_ONLY_SIMD + for(; i < 8; i++) + { + float u = grbuf[18 + i]; + float d = grbuf[17 - i]; + grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; + grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; + } +#endif /* MINIMP3_ONLY_SIMD */ + } +} + +static void L3_dct3_9(float *y) +{ + float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; + + s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; + t0 = s0 + s6*0.5f; + s0 -= s6; + t4 = (s4 + s2)*0.93969262f; + t2 = (s8 + s2)*0.76604444f; + s6 = (s4 - s8)*0.17364818f; + s4 += s8 - s2; + + s2 = s0 - s4*0.5f; + y[4] = s4 + s0; + s8 = t0 - t2 + s6; + s0 = t0 - t4 + t2; + s4 = t0 + t4 - s6; + + s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; + + s3 *= 0.86602540f; + t0 = (s5 + s1)*0.98480775f; + t4 = (s5 - s7)*0.34202014f; + t2 = (s1 + s7)*0.64278761f; + s1 = (s1 - s5 - s7)*0.86602540f; + + s5 = t0 - s3 - t2; + s7 = t4 - s3 - t0; + s3 = t4 + s3 - t2; + + y[0] = s4 - s7; + y[1] = s2 + s1; + y[2] = s0 - s3; + y[3] = s8 + s5; + y[5] = s8 - s5; + y[6] = s0 + s3; + y[7] = s2 - s1; + y[8] = s4 + s7; +} + +static void L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) +{ + int i, j; + static const float g_twid9[18] = { + 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f + }; + + for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) + { + float co[9], si[9]; + co[0] = -grbuf[0]; + si[0] = grbuf[17]; + for (i = 0; i < 4; i++) + { + si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; + co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; + si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; + co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); + } + L3_dct3_9(co); + L3_dct3_9(si); + + si[1] = -si[1]; + si[3] = -si[3]; + si[5] = -si[5]; + si[7] = -si[7]; + + i = 0; + +#if HAVE_SIMD + if (have_simd()) for (; i < 8; i += 4) + { + f4 vovl = VLD(overlap + i); + f4 vc = VLD(co + i); + f4 vs = VLD(si + i); + f4 vr0 = VLD(g_twid9 + i); + f4 vr1 = VLD(g_twid9 + 9 + i); + f4 vw0 = VLD(window + i); + f4 vw1 = VLD(window + 9 + i); + f4 vsum = VADD(VMUL(vc, vr1), VMUL(vs, vr0)); + VSTORE(overlap + i, VSUB(VMUL(vc, vr0), VMUL(vs, vr1))); + VSTORE(grbuf + i, VSUB(VMUL(vovl, vw0), VMUL(vsum, vw1))); + vsum = VADD(VMUL(vovl, vw1), VMUL(vsum, vw0)); + VSTORE(grbuf + 14 - i, VREV(vsum)); + } +#endif /* HAVE_SIMD */ + for (; i < 9; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; + overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; + grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; + grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; + } + } +} + +static void L3_idct3(float x0, float x1, float x2, float *dst) +{ + float m1 = x1*0.86602540f; + float a1 = x0 - x2*0.5f; + dst[1] = x0 + x2; + dst[0] = a1 + m1; + dst[2] = a1 - m1; +} + +static void L3_imdct12(float *x, float *dst, float *overlap) +{ + static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; + float co[3], si[3]; + int i; + + L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); + L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); + si[1] = -si[1]; + + for (i = 0; i < 3; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; + overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; + dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; + dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; + } +} + +static void L3_imdct_short(float *grbuf, float *overlap, int nbands) +{ + for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) + { + float tmp[18]; + memcpy(tmp, grbuf, sizeof(tmp)); + memcpy(grbuf, overlap, 6*sizeof(float)); + L3_imdct12(tmp, grbuf + 6, overlap + 6); + L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); + L3_imdct12(tmp + 2, overlap, overlap + 6); + } +} + +static void L3_change_sign(float *grbuf) +{ + int b, i; + for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) + for (i = 1; i < 18; i += 2) + grbuf[i] = -grbuf[i]; +} + +static void L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) +{ + static const float g_mdct_window[2][18] = { + { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, + { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } + }; + if (n_long_bands) + { + L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); + grbuf += 18*n_long_bands; + overlap += 9*n_long_bands; + } + if (block_type == SHORT_BLOCK_TYPE) + L3_imdct_short(grbuf, overlap, 32 - n_long_bands); + else + L3_imdct36(grbuf, overlap, g_mdct_window[block_type == STOP_BLOCK_TYPE], 32 - n_long_bands); +} + +static void L3_save_reservoir(mp3dec_t *h, mp3dec_scratch_t *s) +{ + int pos = (s->bs.pos + 7)/8u; + int remains = s->bs.limit/8u - pos; + if (remains > MAX_BITRESERVOIR_BYTES) + { + pos += remains - MAX_BITRESERVOIR_BYTES; + remains = MAX_BITRESERVOIR_BYTES; + } + if (remains > 0) + { + memmove(h->reserv_buf, s->maindata + pos, remains); + } + h->reserv = remains; +} + +static int L3_restore_reservoir(mp3dec_t *h, bs_t *bs, mp3dec_scratch_t *s, int main_data_begin) +{ + int frame_bytes = (bs->limit - bs->pos)/8; + int bytes_have = MINIMP3_MIN(h->reserv, main_data_begin); + memcpy(s->maindata, h->reserv_buf + MINIMP3_MAX(0, h->reserv - main_data_begin), MINIMP3_MIN(h->reserv, main_data_begin)); + memcpy(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); + bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); + return h->reserv >= main_data_begin; +} + +static void L3_decode(mp3dec_t *h, mp3dec_scratch_t *s, L3_gr_info_t *gr_info, int nch) +{ + int ch; + + for (ch = 0; ch < nch; ch++) + { + int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; + L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); + L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); + } + + if (HDR_TEST_I_STEREO(h->header)) + { + L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); + } else if (HDR_IS_MS_STEREO(h->header)) + { + L3_midside_stereo(s->grbuf[0], 576); + } + + for (ch = 0; ch < nch; ch++, gr_info++) + { + int aa_bands = 31; + int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(HDR_GET_MY_SAMPLE_RATE(h->header) == 2); + + if (gr_info->n_short_sfb) + { + aa_bands = n_long_bands - 1; + L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); + } + + L3_antialias(s->grbuf[ch], aa_bands); + L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); + L3_change_sign(s->grbuf[ch]); + } +} + +static void mp3d_DCT_II(float *grbuf, int n) +{ + static const float g_sec[24] = { + 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f + }; + int i, k = 0; +#if HAVE_SIMD + if (have_simd()) for (; k < n; k += 4) + { + f4 t[4][8], *x; + float *y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + f4 x0 = VLD(&y[i*18]); + f4 x1 = VLD(&y[(15 - i)*18]); + f4 x2 = VLD(&y[(16 + i)*18]); + f4 x3 = VLD(&y[(31 - i)*18]); + f4 t0 = VADD(x0, x3); + f4 t1 = VADD(x1, x2); + f4 t2 = VMUL_S(VSUB(x1, x2), g_sec[3*i + 0]); + f4 t3 = VMUL_S(VSUB(x0, x3), g_sec[3*i + 1]); + x[0] = VADD(t0, t1); + x[8] = VMUL_S(VSUB(t0, t1), g_sec[3*i + 2]); + x[16] = VADD(t3, t2); + x[24] = VMUL_S(VSUB(t3, t2), g_sec[3*i + 2]); + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = VSUB(x0, x7); x0 = VADD(x0, x7); + x7 = VSUB(x1, x6); x1 = VADD(x1, x6); + x6 = VSUB(x2, x5); x2 = VADD(x2, x5); + x5 = VSUB(x3, x4); x3 = VADD(x3, x4); + x4 = VSUB(x0, x3); x0 = VADD(x0, x3); + x3 = VSUB(x1, x2); x1 = VADD(x1, x2); + x[0] = VADD(x0, x1); + x[4] = VMUL_S(VSUB(x0, x1), 0.70710677f); + x5 = VADD(x5, x6); + x6 = VMUL_S(VADD(x6, x7), 0.70710677f); + x7 = VADD(x7, xt); + x3 = VMUL_S(VADD(x3, x4), 0.70710677f); + x5 = VSUB(x5, VMUL_S(x7, 0.198912367f)); /* rotate by PI/8 */ + x7 = VADD(x7, VMUL_S(x5, 0.382683432f)); + x5 = VSUB(x5, VMUL_S(x7, 0.198912367f)); + x0 = VSUB(xt, x6); xt = VADD(xt, x6); + x[1] = VMUL_S(VADD(xt, x7), 0.50979561f); + x[2] = VMUL_S(VADD(x4, x3), 0.54119611f); + x[3] = VMUL_S(VSUB(x0, x5), 0.60134488f); + x[5] = VMUL_S(VADD(x0, x5), 0.89997619f); + x[6] = VMUL_S(VSUB(x4, x3), 1.30656302f); + x[7] = VMUL_S(VSUB(xt, x7), 2.56291556f); + } + + if (k > n - 3) + { +#if HAVE_SSE +#define VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) +#else /* HAVE_SSE */ +#define VSAVE2(i, v) vst1_f32((float32_t *)&y[i*18], vget_low_f32(v)) +#endif /* HAVE_SSE */ + for (i = 0; i < 7; i++, y += 4*18) + { + f4 s = VADD(t[3][i], t[3][i + 1]); + VSAVE2(0, t[0][i]); + VSAVE2(1, VADD(t[2][i], s)); + VSAVE2(2, VADD(t[1][i], t[1][i + 1])); + VSAVE2(3, VADD(t[2][1 + i], s)); + } + VSAVE2(0, t[0][7]); + VSAVE2(1, VADD(t[2][7], t[3][7])); + VSAVE2(2, t[1][7]); + VSAVE2(3, t[3][7]); + } else + { +#define VSAVE4(i, v) VSTORE(&y[i*18], v) + for (i = 0; i < 7; i++, y += 4*18) + { + f4 s = VADD(t[3][i], t[3][i + 1]); + VSAVE4(0, t[0][i]); + VSAVE4(1, VADD(t[2][i], s)); + VSAVE4(2, VADD(t[1][i], t[1][i + 1])); + VSAVE4(3, VADD(t[2][1 + i], s)); + } + VSAVE4(0, t[0][7]); + VSAVE4(1, VADD(t[2][7], t[3][7])); + VSAVE4(2, t[1][7]); + VSAVE4(3, t[3][7]); + } + } else +#endif /* HAVE_SIMD */ +#ifdef MINIMP3_ONLY_SIMD + {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */ +#else /* MINIMP3_ONLY_SIMD */ + for (; k < n; k++) + { + float t[4][8], *x, *y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + float x0 = y[i*18]; + float x1 = y[(15 - i)*18]; + float x2 = y[(16 + i)*18]; + float x3 = y[(31 - i)*18]; + float t0 = x0 + x3; + float t1 = x1 + x2; + float t2 = (x1 - x2)*g_sec[3*i + 0]; + float t3 = (x0 - x3)*g_sec[3*i + 1]; + x[0] = t0 + t1; + x[8] = (t0 - t1)*g_sec[3*i + 2]; + x[16] = t3 + t2; + x[24] = (t3 - t2)*g_sec[3*i + 2]; + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = x0 - x7; x0 += x7; + x7 = x1 - x6; x1 += x6; + x6 = x2 - x5; x2 += x5; + x5 = x3 - x4; x3 += x4; + x4 = x0 - x3; x0 += x3; + x3 = x1 - x2; x1 += x2; + x[0] = x0 + x1; + x[4] = (x0 - x1)*0.70710677f; + x5 = x5 + x6; + x6 = (x6 + x7)*0.70710677f; + x7 = x7 + xt; + x3 = (x3 + x4)*0.70710677f; + x5 -= x7*0.198912367f; /* rotate by PI/8 */ + x7 += x5*0.382683432f; + x5 -= x7*0.198912367f; + x0 = xt - x6; xt += x6; + x[1] = (xt + x7)*0.50979561f; + x[2] = (x4 + x3)*0.54119611f; + x[3] = (x0 - x5)*0.60134488f; + x[5] = (x0 + x5)*0.89997619f; + x[6] = (x4 - x3)*1.30656302f; + x[7] = (xt - x7)*2.56291556f; + + } + for (i = 0; i < 7; i++, y += 4*18) + { + y[0*18] = t[0][i]; + y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; + y[2*18] = t[1][i] + t[1][i + 1]; + y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; + } + y[0*18] = t[0][7]; + y[1*18] = t[2][7] + t[3][7]; + y[2*18] = t[1][7]; + y[3*18] = t[3][7]; + } +#endif /* MINIMP3_ONLY_SIMD */ +} + +#ifndef MINIMP3_FLOAT_OUTPUT +static int16_t mp3d_scale_pcm(float sample) +{ +#if HAVE_ARMV6 + int32_t s32 = (int32_t)(sample + .5f); + s32 -= (s32 < 0); + int16_t s = (int16_t)minimp3_clip_int16_arm(s32); +#else + if (sample >= 32766.5) return (int16_t) 32767; + if (sample <= -32767.5) return (int16_t)-32768; + int16_t s = (int16_t)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ +#endif + return s; +} +#else /* MINIMP3_FLOAT_OUTPUT */ +static float mp3d_scale_pcm(float sample) +{ + return sample*(1.f/32768.f); +} +#endif /* MINIMP3_FLOAT_OUTPUT */ + +static void mp3d_synth_pair(mp3d_sample_t *pcm, int nch, const float *z) +{ + float a; + a = (z[14*64] - z[ 0]) * 29; + a += (z[ 1*64] + z[13*64]) * 213; + a += (z[12*64] - z[ 2*64]) * 459; + a += (z[ 3*64] + z[11*64]) * 2037; + a += (z[10*64] - z[ 4*64]) * 5153; + a += (z[ 5*64] + z[ 9*64]) * 6574; + a += (z[ 8*64] - z[ 6*64]) * 37489; + a += z[ 7*64] * 75038; + pcm[0] = mp3d_scale_pcm(a); + + z += 2; + a = z[14*64] * 104; + a += z[12*64] * 1567; + a += z[10*64] * 9727; + a += z[ 8*64] * 64019; + a += z[ 6*64] * -9975; + a += z[ 4*64] * -45; + a += z[ 2*64] * 146; + a += z[ 0*64] * -5; + pcm[16*nch] = mp3d_scale_pcm(a); +} + +static void mp3d_synth(float *xl, mp3d_sample_t *dstl, int nch, float *lins) +{ + int i; + float *xr = xl + 576*(nch - 1); + mp3d_sample_t *dstr = dstl + (nch - 1); + + static const float g_win[] = { + -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, + -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, + -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, + -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, + -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, + -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, + -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, + -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, + -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, + -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, + -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, + -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, + -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, + -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, + -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 + }; + float *zlin = lins + 15*64; + const float *w = g_win; + + zlin[4*15] = xl[18*16]; + zlin[4*15 + 1] = xr[18*16]; + zlin[4*15 + 2] = xl[0]; + zlin[4*15 + 3] = xr[0]; + + zlin[4*31] = xl[1 + 18*16]; + zlin[4*31 + 1] = xr[1 + 18*16]; + zlin[4*31 + 2] = xl[1]; + zlin[4*31 + 3] = xr[1]; + + mp3d_synth_pair(dstr, nch, lins + 4*15 + 1); + mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); + mp3d_synth_pair(dstl, nch, lins + 4*15); + mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); + +#if HAVE_SIMD + if (have_simd()) for (i = 14; i >= 0; i--) + { +#define VLOAD(k) f4 w0 = VSET(*w++); f4 w1 = VSET(*w++); f4 vz = VLD(&zlin[4*i - 64*k]); f4 vy = VLD(&zlin[4*i - 64*(15 - k)]); +#define V0(k) { VLOAD(k) b = VADD(VMUL(vz, w1), VMUL(vy, w0)) ; a = VSUB(VMUL(vz, w0), VMUL(vy, w1)); } +#define V1(k) { VLOAD(k) b = VADD(b, VADD(VMUL(vz, w1), VMUL(vy, w0))); a = VADD(a, VSUB(VMUL(vz, w0), VMUL(vy, w1))); } +#define V2(k) { VLOAD(k) b = VADD(b, VADD(VMUL(vz, w1), VMUL(vy, w0))); a = VADD(a, VSUB(VMUL(vy, w1), VMUL(vz, w0))); } + f4 a, b; + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*i + 64] = xl[1 + 18*(1 + i)]; + zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; + zlin[4*i - 64 + 2] = xl[18*(1 + i)]; + zlin[4*i - 64 + 3] = xr[18*(1 + i)]; + + V0(0) V2(1) V1(2) V2(3) V1(4) V2(5) V1(6) V2(7) + + { +#ifndef MINIMP3_FLOAT_OUTPUT +#if HAVE_SSE + static const f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); + dstr[(15 - i)*nch] = _mm_extract_epi16(pcm8, 1); + dstr[(17 + i)*nch] = _mm_extract_epi16(pcm8, 5); + dstl[(15 - i)*nch] = _mm_extract_epi16(pcm8, 0); + dstl[(17 + i)*nch] = _mm_extract_epi16(pcm8, 4); + dstr[(47 - i)*nch] = _mm_extract_epi16(pcm8, 3); + dstr[(49 + i)*nch] = _mm_extract_epi16(pcm8, 7); + dstl[(47 - i)*nch] = _mm_extract_epi16(pcm8, 2); + dstl[(49 + i)*nch] = _mm_extract_epi16(pcm8, 6); +#else /* HAVE_SSE */ + int16x4_t pcma, pcmb; + a = VADD(a, VSET(0.5f)); + b = VADD(b, VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, VSET(0))))); + vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); + vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); + vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); + vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); + vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); + vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); + vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); + vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); +#endif /* HAVE_SSE */ + +#else /* MINIMP3_FLOAT_OUTPUT */ + + static const f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; + a = VMUL(a, g_scale); + b = VMUL(b, g_scale); +#if HAVE_SSE + _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); +#else /* HAVE_SSE */ + vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); + vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); + vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); + vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); + vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); + vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); + vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); + vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); +#endif /* HAVE_SSE */ +#endif /* MINIMP3_FLOAT_OUTPUT */ + } + } else +#endif /* HAVE_SIMD */ +#ifdef MINIMP3_ONLY_SIMD + {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */ +#else /* MINIMP3_ONLY_SIMD */ + for (i = 14; i >= 0; i--) + { +#define LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; +#define S0(k) { int j; LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } +#define S1(k) { int j; LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } +#define S2(k) { int j; LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } + float a[4], b[4]; + + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; + zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; + zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; + zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; + + S0(0) S2(1) S1(2) S2(3) S1(4) S2(5) S1(6) S2(7) + + dstr[(15 - i)*nch] = mp3d_scale_pcm(a[1]); + dstr[(17 + i)*nch] = mp3d_scale_pcm(b[1]); + dstl[(15 - i)*nch] = mp3d_scale_pcm(a[0]); + dstl[(17 + i)*nch] = mp3d_scale_pcm(b[0]); + dstr[(47 - i)*nch] = mp3d_scale_pcm(a[3]); + dstr[(49 + i)*nch] = mp3d_scale_pcm(b[3]); + dstl[(47 - i)*nch] = mp3d_scale_pcm(a[2]); + dstl[(49 + i)*nch] = mp3d_scale_pcm(b[2]); + } +#endif /* MINIMP3_ONLY_SIMD */ +} + +static void mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, mp3d_sample_t *pcm, float *lins) +{ + int i; + for (i = 0; i < nch; i++) + { + mp3d_DCT_II(grbuf + 576*i, nbands); + } + + memcpy(lins, qmf_state, sizeof(float)*15*64); + + for (i = 0; i < nbands; i += 2) + { + mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); + } +#ifndef MINIMP3_NONSTANDARD_BUT_LOGICAL + if (nch == 1) + { + for (i = 0; i < 15*64; i += 2) + { + qmf_state[i] = lins[nbands*64 + i]; + } + } else +#endif /* MINIMP3_NONSTANDARD_BUT_LOGICAL */ + { + memcpy(qmf_state, lins + nbands*64, sizeof(float)*15*64); + } +} + +static int mp3d_match_frame(const uint8_t *hdr, int mp3_bytes, int frame_bytes) +{ + int i, nmatch; + for (i = 0, nmatch = 0; nmatch < MAX_FRAME_SYNC_MATCHES; nmatch++) + { + i += hdr_frame_bytes(hdr + i, frame_bytes) + hdr_padding(hdr + i); + if (i + HDR_SIZE > mp3_bytes) + return nmatch > 0; + if (!hdr_compare(hdr, hdr + i)) + return 0; + } + return 1; +} + +static int mp3d_find_frame(const uint8_t *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) +{ + int i, k; + for (i = 0; i < mp3_bytes - HDR_SIZE; i++, mp3++) + { + if (hdr_valid(mp3)) + { + int frame_bytes = hdr_frame_bytes(mp3, *free_format_bytes); + int frame_and_padding = frame_bytes + hdr_padding(mp3); + + for (k = HDR_SIZE; !frame_bytes && k < MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - HDR_SIZE; k++) + { + if (hdr_compare(mp3, mp3 + k)) + { + int fb = k - hdr_padding(mp3); + int nextfb = fb + hdr_padding(mp3 + k); + if (i + k + nextfb + HDR_SIZE > mp3_bytes || !hdr_compare(mp3, mp3 + k + nextfb)) + continue; + frame_and_padding = k; + frame_bytes = fb; + *free_format_bytes = fb; + } + } + if ((frame_bytes && i + frame_and_padding <= mp3_bytes && + mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || + (!i && frame_and_padding == mp3_bytes)) + { + *ptr_frame_bytes = frame_and_padding; + return i; + } + *free_format_bytes = 0; + } + } + *ptr_frame_bytes = 0; + return mp3_bytes; +} + +void mp3dec_init(mp3dec_t *dec) +{ + dec->header[0] = 0; +} + +int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_sample_t *pcm, mp3dec_frame_info_t *info) +{ + int i = 0, igr, frame_size = 0, success = 1; + const uint8_t *hdr; + bs_t bs_frame[1]; + mp3dec_scratch_t scratch; + + if (mp3_bytes > 4 && dec->header[0] == 0xff && hdr_compare(dec->header, mp3)) + { + frame_size = hdr_frame_bytes(mp3, dec->free_format_bytes) + hdr_padding(mp3); + if (frame_size != mp3_bytes && (frame_size + HDR_SIZE > mp3_bytes || !hdr_compare(mp3, mp3 + frame_size))) + { + frame_size = 0; + } + } + if (!frame_size) + { + memset(dec, 0, sizeof(mp3dec_t)); + i = mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); + if (!frame_size || i + frame_size > mp3_bytes) + { + info->frame_bytes = i; + return 0; + } + } + + hdr = mp3 + i; + memcpy(dec->header, hdr, HDR_SIZE); + info->frame_bytes = i + frame_size; + info->frame_offset = i; + info->channels = HDR_IS_MONO(hdr) ? 1 : 2; + info->hz = hdr_sample_rate_hz(hdr); + info->layer = 4 - HDR_GET_LAYER(hdr); + info->bitrate_kbps = hdr_bitrate_kbps(hdr); + + if (!pcm) + { + return hdr_frame_samples(hdr); + } + + bs_init(bs_frame, hdr + HDR_SIZE, frame_size - HDR_SIZE); + if (HDR_IS_CRC(hdr)) + { + get_bits(bs_frame, 16); + } + + if (info->layer == 3) + { + int main_data_begin = L3_read_side_info(bs_frame, scratch.gr_info, hdr); + if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) + { + mp3dec_init(dec); + return 0; + } + success = L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); + if (success) + { + for (igr = 0; igr < (HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm += 576*info->channels) + { + memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); + L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); + mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, pcm, scratch.syn[0]); + } + } + L3_save_reservoir(dec, &scratch); + } else + { +#ifdef MINIMP3_ONLY_MP3 + return 0; +#else /* MINIMP3_ONLY_MP3 */ + L12_scale_info sci[1]; + L12_read_scale_info(hdr, bs_frame, sci); + + memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); + for (i = 0, igr = 0; igr < 3; igr++) + { + if (12 == (i += L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) + { + i = 0; + L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); + mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, pcm, scratch.syn[0]); + memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); + pcm += 384*info->channels; + } + if (bs_frame->pos > bs_frame->limit) + { + mp3dec_init(dec); + return 0; + } + } +#endif /* MINIMP3_ONLY_MP3 */ + } + return success*hdr_frame_samples(dec->header); +} + +#ifdef MINIMP3_FLOAT_OUTPUT +void mp3dec_f32_to_s16(const float *in, int16_t *out, int num_samples) +{ + int i = 0; +#if HAVE_SIMD + int aligned_count = num_samples & ~7; + for(; i < aligned_count; i += 8) + { + static const f4 g_scale = { 32768.0f, 32768.0f, 32768.0f, 32768.0f }; + f4 a = VMUL(VLD(&in[i ]), g_scale); + f4 b = VMUL(VLD(&in[i+4]), g_scale); +#if HAVE_SSE + static const f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); + out[i ] = _mm_extract_epi16(pcm8, 0); + out[i+1] = _mm_extract_epi16(pcm8, 1); + out[i+2] = _mm_extract_epi16(pcm8, 2); + out[i+3] = _mm_extract_epi16(pcm8, 3); + out[i+4] = _mm_extract_epi16(pcm8, 4); + out[i+5] = _mm_extract_epi16(pcm8, 5); + out[i+6] = _mm_extract_epi16(pcm8, 6); + out[i+7] = _mm_extract_epi16(pcm8, 7); +#else /* HAVE_SSE */ + int16x4_t pcma, pcmb; + a = VADD(a, VSET(0.5f)); + b = VADD(b, VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, VSET(0))))); + vst1_lane_s16(out+i , pcma, 0); + vst1_lane_s16(out+i+1, pcma, 1); + vst1_lane_s16(out+i+2, pcma, 2); + vst1_lane_s16(out+i+3, pcma, 3); + vst1_lane_s16(out+i+4, pcmb, 0); + vst1_lane_s16(out+i+5, pcmb, 1); + vst1_lane_s16(out+i+6, pcmb, 2); + vst1_lane_s16(out+i+7, pcmb, 3); +#endif /* HAVE_SSE */ + } +#endif /* HAVE_SIMD */ + for(; i < num_samples; i++) + { + float sample = in[i] * 32768.0f; + if (sample >= 32766.5) + out[i] = (int16_t) 32767; + else if (sample <= -32767.5) + out[i] = (int16_t)-32768; + else + { + int16_t s = (int16_t)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + out[i] = s; + } + } +} +#endif /* MINIMP3_FLOAT_OUTPUT */ +#endif /* MINIMP3_IMPLEMENTATION && !_MINIMP3_IMPLEMENTATION_GUARD */ diff --git a/Frameworks/OpenMPT.old/OpenMPT/include/miniz/LICENSE b/Frameworks/OpenMPT.old/OpenMPT/include/miniz/LICENSE new file mode 100644 index 000000000..1982f4bb8 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/include/miniz/LICENSE @@ -0,0 +1,22 @@ +Copyright 2013-2014 RAD Game Tools and Valve Software +Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Frameworks/OpenMPT.old/OpenMPT/include/miniz/OpenMPT.txt b/Frameworks/OpenMPT.old/OpenMPT/include/miniz/OpenMPT.txt new file mode 100644 index 000000000..1a6b2f195 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/include/miniz/OpenMPT.txt @@ -0,0 +1,18 @@ +miniz DEFLATE implementation. +https://github.com/richgel999/miniz +2.2.0 +Modifications for OpenMPT: + * #define MINIZ_NO_STDIO has been set because OpenMPT does not need stdio + functionality and miniz relies on secure-CRT file i/o functions in windows + builds which are not available on all mingw64 versions. + * #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 is used unconditionally, + because unaligned access is undefined behaviour. + * Various warnings in platform detection logic using undefined macros have + been fixed. + * Warning `warning: cast from 'const mz_uint8 *' (aka 'const unsigned char *') + to 'const mz_uint32 *' (aka 'const unsigned int *') increases required + alignment from 1 to 4 [-Wcast-align]` has been fixed. + * Definitions of `tdefl_compressor_alloc` and `tinfl_decompressor_alloc` + have beeen fixed + * Missing #ifndef MINIZ_NO_STDIO has been added to miniz.h. +No further changes have been made. diff --git a/Frameworks/OpenMPT.old/OpenMPT/include/miniz/miniz.c b/Frameworks/OpenMPT.old/OpenMPT/include/miniz/miniz.c new file mode 100644 index 000000000..c66a11be0 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/include/miniz/miniz.c @@ -0,0 +1,7755 @@ +#include "miniz.h" +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API's */ + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) + return MZ_ADLER32_INIT; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + return (s2 << 16) + s1; +} + +/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ +#if 0 + mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) + { + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) + return MZ_CRC32_INIT; + crcu32 = ~crcu32; + while (buf_len--) + { + mz_uint8 b = *ptr++; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; + } + return ~crcu32; + } +#elif defined(USE_EXTERNAL_MZCRC) +/* If USE_EXTERNAL_CRC is defined, an external module will export the + * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version. + * Depending on the impl, it may be necessary to ~ the input/output crc values. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len); +#else +/* Faster, but larger CPU cache footprint. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, + 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, + 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, + 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, + 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, + 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, + 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, + 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, + 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, + 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, + 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; + const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; + + while (buf_len >= 4) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; + pByte_buf += 4; + buf_len -= 4; + } + + while (buf_len) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + ++pByte_buf; + --buf_len; + } + + return ~crc32; +} +#endif + +void mz_free(void *p) +{ + MZ_FREE(p); +} + +MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) +{ + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); +} +MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address) +{ + (void)opaque, (void)address; + MZ_FREE(address); +} +MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) +{ + (void)opaque, (void)address, (void)items, (void)size; + return MZ_REALLOC(address, items * size); +} + +const char *mz_version(void) +{ + return MZ_VERSION; +} + +#ifndef MINIZ_NO_ZLIB_APIS + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) + return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; /* Can't make forward progress without some input. + */ + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + (void)pStream; + /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) + return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflateReset(mz_streamp pStream) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + + pDecomp = (inflate_state *)pStream->state; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + /* pDecomp->m_window_bits = window_bits */; + + return MZ_OK; +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state *pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) + return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + + pState = (inflate_state *)pStream->state; + if (pState->m_window_bits > 0) + decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) + return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + /* flush != MZ_FINISH then we must assume there's more input. */ + if (flush != MZ_FINISH) + decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for (;;) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ + else if (flush == MZ_FINISH) + { + /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} +int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((*pSource_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)*pSource_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + *pSource_len = *pSource_len - stream.avail_in; + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_uncompress2(pDest, pDest_len, pSource, &source_len); +} + +const char *mz_error(int err) +{ + static struct + { + int m_err; + const char *m_pDesc; + } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; + for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) + if (s_error_descs[i].m_err == err) + return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif /*MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Compression (independent from all decompression API's) */ + +/* Purposely making these tables static for faster init and thread safety. */ +static const mz_uint16 s_tdefl_len_sym[256] = + { + 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 + }; + +static const mz_uint8 s_tdefl_len_extra[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 + }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = + { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 + }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = + { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = + { + 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = + { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 + }; + +/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ +typedef struct +{ + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) + { + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; + } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) + total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32 *pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) + { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { + tdefl_sym_freq *t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } + } + return pCur_syms; +} + +/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) + { + A[0].m_key = 1; + return; + } + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) + { + if (leaf >= n || A[root].m_key < A[leaf].m_key) + { + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) + { + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) + A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) + { + while (root >= 0 && (int)A[root].m_key == dpth) + { + used++; + root--; + } + while (avbl > used) + { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; + } +} + +/* Limits canonical Huffman code table's max code size. */ +enum +{ + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 +}; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) + return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) + total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) + { + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; + } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_OBJ(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) + num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) + { + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; + } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) + next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) + continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) + rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) \ + do \ + { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) \ + { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) \ + { \ + if (rle_repeat_count < 3) \ + { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) \ + packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } \ + else \ + { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } + +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) \ + { \ + if (rle_z_count < 3) \ + { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) \ + packed_code_sizes[num_packed_code_sizes++] = 0; \ + } \ + else if (rle_z_count <= 10) \ + { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } \ + else \ + { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } + +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) + break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) + break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) + break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ + } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64 *)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + TDEFL_PUT_BITS(0x78, 8); + TDEFL_PUT_BITS(0x01, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ + if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) + { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) + { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) + { + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; + } + } + } + else + { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, z ^= 0xFFFF) + { + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) +#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) +#endif +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + q = (const mz_uint16 *)(d->m_dict + probe_pos); + if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) + continue; + p = s; + probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + if (!probe_len) + { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); + break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) + break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) + break; + if (probe_len > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) + return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8* p) +{ + mz_uint32 ret; + memcpy(&ret, p, sizeof(mz_uint32)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p) +#endif +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) + break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist)); +#else + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; +#endif + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + /* Simple lazy/greedy parsing state machine. */ + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) + { + if (d->m_dict[cur_pos + cur_match_len] != c) + break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + /* Move the lookahead forward by len_to_move bytes. */ + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); + /* Check if it's time to flush the current LZ codes to the internal output buffer. */ + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) + { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) + { + MZ_CLEAR_OBJ(d->m_hash); + MZ_CLEAR_OBJ(d->m_next); + d->m_dict_size = 0; + } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); + return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + *d->m_pLZ_flags = 0; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_dict); + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + tdefl_compressor *pComp; + mz_bool succeeded; + if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) + return MZ_FALSE; + pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + if (!pComp) + return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); + return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; + mz_uint8 *pNew_buf; + if (!p->m_expandable) + return MZ_FALSE; + do + { + new_capacity = MZ_MAX(128U, new_capacity << 1U); + } while (new_size > new_capacity); + pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); + if (!pNew_buf) + return MZ_FALSE; + p->m_pBuf = pNew_buf; + p->m_capacity = new_capacity; + } + memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); + p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) + return MZ_FALSE; + else + *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return NULL; + *pOut_len = out_buf.m_size; + return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) + return 0; + out_buf.m_pBuf = (mz_uint8 *)pOut_buf; + out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return 0; + return out_buf.m_size; +} + +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ +#endif + +/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at + http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. + This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +{ + /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ + static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + tdefl_output_buffer out_buf; + int i, bpl = w * num_chans, y, z; + mz_uint32 c; + *pLen_out = 0; + if (!pComp) + return NULL; + MZ_CLEAR_OBJ(out_buf); + out_buf.m_expandable = MZ_TRUE; + out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); + if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) + { + MZ_FREE(pComp); + return NULL; + } + /* write dummy header */ + for (z = 41; z; --z) + tdefl_output_buffer_putter(&z, 1, &out_buf); + /* compress image data */ + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) + { + tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); + } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) + { + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + /* write real header */ + *pLen_out = out_buf.m_size - 41; + { + static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; + mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, + 0x0a, 0x1a, 0x0a, 0x00, 0x00, + 0x00, 0x0d, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x44, 0x41, + 0x54 }; + pnghdr[18] = (mz_uint8)(w >> 8); + pnghdr[19] = (mz_uint8)w; + pnghdr[22] = (mz_uint8)(h >> 8); + pnghdr[23] = (mz_uint8)h; + pnghdr[25] = chans[num_chans]; + pnghdr[33] = (mz_uint8)(*pLen_out >> 24); + pnghdr[34] = (mz_uint8)(*pLen_out >> 16); + pnghdr[35] = (mz_uint8)(*pLen_out >> 8); + pnghdr[36] = (mz_uint8)*pLen_out; + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); + for (i = 0; i < 4; ++i, c <<= 8) + ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + /* write footer (IDAT CRC-32, followed by IEND chunk) */ + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) + { + *pLen_out = 0; + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); + for (i = 0; i < 4; ++i, c <<= 8) + (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); + /* compute final size of file, grab compressed data buffer and return */ + *pLen_out += 57; + MZ_FREE(pComp); + return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +} + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ +/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +#if 0 // OpenMPT +tdefl_compressor *tdefl_compressor_alloc() +#else // OpenMPT +tdefl_compressor *tdefl_compressor_alloc(void) // OpenMPT +#endif // OpenMPT +{ + return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +} + +void tdefl_compressor_free(tdefl_compressor *pComp) +{ + MZ_FREE(pComp); +} +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __cplusplus +} +#endif + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Decompression (completely independent from all compression API's) */ + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN \ + switch (r->m_state) \ + { \ + case 0: +#define TINFL_CR_RETURN(state_index, result) \ + do \ + { \ + status = result; \ + r->m_state = state_index; \ + goto common_exit; \ + case state_index:; \ + } \ + MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) \ + do \ + { \ + for (;;) \ + { \ + TINFL_CR_RETURN(state_index, result); \ + } \ + } \ + MZ_MACRO_END +#define TINFL_CR_FINISH } + +#define TINFL_GET_BYTE(state_index, c) \ + do \ + { \ + while (pIn_buf_cur >= pIn_buf_end) \ + { \ + TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ + } \ + c = *pIn_buf_cur++; \ + } \ + MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) \ + do \ + { \ + mz_uint c; \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + b = bit_buf & ((1 << (n)) - 1); \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END + +/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ +/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ +/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ +/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do \ + { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) \ + { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } \ + else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) \ + break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < 15); + +/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ +/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ +/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ +/* The slow path is only executed at the very end of the input buffer. */ +/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ +/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ + do \ + { \ + int temp; \ + mz_uint code_len, c; \ + if (num_bits < 15) \ + { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) \ + { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } \ + else \ + { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ + pIn_buf_cur += 2; \ + num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while (temp < 0); \ + } \ + sym = temp; \ + bit_buf >>= code_len; \ + num_bits -= code_len; \ + } \ + MZ_MACRO_END + +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; + tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) + { + *pIn_buf_size = *pOut_buf_size = 0; + return TINFL_STATUS_BAD_PARAM; + } + + num_bits = r->m_num_bits; + bit_buf = r->m_bit_buf; + dist = r->m_dist; + counter = r->m_counter; + num_extra = r->m_num_extra; + dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; + r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); + TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) + { + TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); + } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); + r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) + { + if (num_bits) + TINFL_GET_BITS(6, r->m_raw_header[counter], 8); + else + TINFL_GET_BYTE(7, r->m_raw_header[counter]); + } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) + { + TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); + } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); + } + while (pIn_buf_cur >= pIn_buf_end) + { + TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); + pIn_buf_cur += n; + pOut_buf_cur += n; + counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_tables[0].m_code_size; + mz_uint i; + r->m_table_sizes[0] = 288; + r->m_table_sizes[1] = 32; + TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) + { + TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); + r->m_table_sizes[counter] += s_min_table_sizes[counter]; + } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); + for (counter = 0; counter < r->m_table_sizes[2]; counter++) + { + mz_uint s; + TINFL_GET_BITS(14, s, 3); + r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; + } + r->m_table_sizes[2] = 19; + } + for (; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; + tinfl_huff_table *pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; + pTable = &r->m_tables[r->m_type]; + MZ_CLEAR_OBJ(total_syms); + MZ_CLEAR_OBJ(pTable->m_look_up); + MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) + total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; + next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) + { + used_syms += total_syms[i]; + next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); + } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; + if (!code_size) + continue; + cur_code = next_code[code_size]++; + for (l = code_size; l > 0; l--, cur_code >>= 1) + rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) + { + mz_int16 k = (mz_int16)((code_size << 9) | sym_index); + while (rev_code < TINFL_FAST_LOOKUP_SIZE) + { + pTable->m_look_up[rev_code] = k; + rev_code += (1 << code_size); + } + continue; + } + if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) + { + pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) + { + pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + else + tree_cur = pTable->m_tree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); + pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) + { + mz_uint s; + TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); + if (dist < 16) + { + r->m_len_codes[counter++] = (mz_uint8)dist; + continue; + } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; + TINFL_GET_BITS(18, s, num_extra); + s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); + counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for (;;) + { + mz_uint8 *pSrc; + for (;;) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; + mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 4; + num_bits += 32; + } +#else + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + counter = sym2; + bit_buf >>= code_len; + num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + bit_buf >>= code_len; + num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) + break; + + num_extra = s_length_extra[counter - 257]; + counter = s_length_base[counter - 257]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(25, extra_bits, num_extra); + counter += extra_bits; + } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; + dist = s_dist_base[dist]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(27, extra_bits, num_extra); + dist += extra_bits; + } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2); +#else + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; +#endif + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + while(counter>2) + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; + pSrc += 3; + counter -= 3; + } + if (counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + + /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ + TINFL_SKIP_BITS(32, num_bits & 7); + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ + + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + for (counter = 0; counter < 4; ++counter) + { + mz_uint s; + if (num_bits) + TINFL_GET_BITS(41, s, 8); + else + TINFL_GET_BYTE(42, s); + r->m_z_adler32 = (r->m_z_adler32 << 8) | s; + } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + + TINFL_CR_FINISH + +common_exit: + /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ + /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ + if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) + { + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + } + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + r->m_dist = dist; + r->m_counter = counter; + r->m_num_extra = num_extra; + r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; + *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; + size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; + size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; + if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) + status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +/* Higher level helper functions. */ +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; + void *pBuf = NULL, *pNew_buf; + size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for (;;) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) + break; + new_out_buf_capacity = out_buf_capacity * 2; + if (new_out_buf_capacity < 128) + new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + pBuf = pNew_buf; + out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tinfl_decompressor decomp; + tinfl_status status; + tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); + size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for (;;) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +#ifndef MINIZ_NO_MALLOC +#if 0 // OpenMPT +tinfl_decompressor *tinfl_decompressor_alloc() +#else // OpenMPT +tinfl_decompressor *tinfl_decompressor_alloc(void) // OpenMPT +#endif // OpenMPT +{ + tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); + if (pDecomp) + tinfl_init(pDecomp); + return pDecomp; +} + +void tinfl_decompressor_free(tinfl_decompressor *pDecomp) +{ + MZ_FREE(pDecomp); +} +#endif + +#ifdef __cplusplus +} +#endif + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * Copyright 2016 Martin Raiber + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- .ZIP archive reading */ + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include + +#if defined(_MSC_VER) || defined(__MINGW64__) +static FILE *mz_fopen(const char *pFilename, const char *pMode) +{ + FILE *pFile = NULL; + fopen_s(&pFile, pFilename, pMode); + return pFile; +} +static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) +{ + FILE *pFile = NULL; + if (freopen_s(&pFile, pPath, pMode, pStream)) + return NULL; + return pFile; +} +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT _stat64 +#define MZ_FILE_STAT _stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN mz_freopen +#define MZ_DELETE_FILE remove +#elif defined(__MINGW32__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__TINYC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__USE_LARGEFILE64) /* gcc, clang */ +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen64(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT stat64 +#define MZ_FILE_STAT stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__APPLE__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen(p, m, s) +#define MZ_DELETE_FILE remove + +#else +#pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#ifdef __STRICT_ANSI__ +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#else +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#endif +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#endif /* #ifdef _MSC_VER */ +#endif /* #ifdef MINIZ_NO_STDIO */ + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ +enum +{ + /* ZIP archive identifiers and record sizes */ + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, + MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + + /* Central directory header record offsets */ + MZ_ZIP_CDH_SIG_OFS = 0, + MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, + MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, + MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, + MZ_ZIP_CDH_FILE_TIME_OFS = 12, + MZ_ZIP_CDH_FILE_DATE_OFS = 14, + MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, + MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, + MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, + MZ_ZIP_CDH_DISK_START_OFS = 34, + MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, + MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + + /* Local directory header offsets */ + MZ_ZIP_LDH_SIG_OFS = 0, + MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, + MZ_ZIP_LDH_BIT_FLAG_OFS = 6, + MZ_ZIP_LDH_METHOD_OFS = 8, + MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, + MZ_ZIP_LDH_CRC32_OFS = 14, + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, + MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, + + /* End of central directory offsets */ + MZ_ZIP_ECDH_SIG_OFS = 0, + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, + MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, + MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, + MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + + /* The flags passed in when the archive is initially opened. */ + uint32_t m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ + MZ_FILE *m_pFile; + mz_uint64 m_file_archive_start_ofs; + + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size + +#if defined(DEBUG) || defined(_DEBUG) +static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) +{ + MZ_ASSERT(index < pArray->m_size); + return index; +} +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] +#else +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] +#endif + +static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) +{ + memset(pArray, 0, sizeof(mz_zip_array)); + pArray->m_element_size = element_size; +} + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; + size_t new_capacity = min_new_capacity; + MZ_ASSERT(pArray->m_element_size); + if (pArray->m_capacity >= min_new_capacity) + return MZ_TRUE; + if (growing) + { + new_capacity = MZ_MAX(1, pArray->m_capacity); + while (new_capacity < min_new_capacity) + new_capacity *= 2; + } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) + return MZ_FALSE; + pArray->m_p = pNew_p; + pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) + return MZ_FALSE; + } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) + return MZ_FALSE; + } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; + if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) + return MZ_FALSE; + if (n > 0) + memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dos_date >> 5) & 15) - 1; + tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; + tm.tm_min = (dos_time >> 5) & 63; + tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) + { + *pDOS_date = 0; + *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) +{ + struct MZ_FILE_STAT_STRUCT file_stat; + + /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + + *pTime = file_stat.st_mtime; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) +{ + struct utimbuf t; + + memset(&t, 0, sizeof(t)); + t.actime = access_time; + t.modtime = modified_time; + + return !utime(pFilename, &t); +} +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ + +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + if (pZip) + pZip->m_last_error = err_num; + return MZ_FALSE; +} + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + pZip->m_last_error = MZ_ZIP_NO_ERROR; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + pZip->m_pState->m_init_flags = flags; + pZip->m_pState->m_zip64 = MZ_FALSE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) \ + do \ + { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END + +/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices; + mz_uint32 start, end; + const mz_uint32 size = pZip->m_total_files; + + if (size <= 1U) + return; + + pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + + start = (size - 2U) >> 1U; + for (;;) + { + mz_uint64 child, root = start; + for (;;) + { + if ((child = (root << 1U) + 1U) >= size) + break; + child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + if (!start) + break; + start--; + } + + end = size - 1; + while (end > 0) + { + mz_uint64 child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for (;;) + { + if ((child = (root << 1U) + 1U) >= end) + break; + child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) +{ + mz_int64 cur_file_ofs; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) + return MZ_FALSE; + + /* Find the record by scanning the file from the end towards the beginning. */ + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for (;;) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + + for (i = n - 4; i >= 0; --i) + { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) + { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) + break; + } + } + + if (i >= 0) + { + cur_file_ofs += i; + break; + } + + /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) + return MZ_FALSE; + + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + + *pOfs = cur_file_ofs; + return MZ_TRUE; +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) +{ + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8 *p; + + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + + mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + { + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) + { + zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) + { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } + } + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) + { + mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (sort_central_dir) + { + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + /* Now create an index into the central directory file records, do some basic sanity checking on each record */ + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && + (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data; + void* buf = NULL; + + if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > n) + { + buf = MZ_MALLOC(ext_data_size); + if(buf==NULL) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (mz_uint8*)buf; + } + else + { + pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + } + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + + MZ_FREE(buf); + } + } + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) + { + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) + { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + n -= total_header_size; + p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +void mz_zip_zero_struct(mz_zip_archive *pZip) +{ + if (pZip) + MZ_CLEAR_OBJ(*pZip); +} + +static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_bool status = MZ_TRUE; + + if (!pZip) + return MZ_FALSE; + + if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; + + return MZ_FALSE; + } + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; + pZip->m_pState = NULL; + + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; + status = MZ_FALSE; + } + } + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return status; +} + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + return mz_zip_reader_end_internal(pZip, MZ_TRUE); +} +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_archive_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) +{ + if (!pMem) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pNeeds_keepalive = NULL; + +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + + pZip->m_pState->m_mem_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); +} + +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) +{ + mz_uint64 file_size; + MZ_FILE *pFile; + + if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + file_size = archive_size; + if (!file_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + } + + file_size = MZ_FTELL64(pFile); + } + + /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + + if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) +{ + mz_uint64 cur_file_ofs; + + if ((!pZip) || (!pFile)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + cur_file_ofs = MZ_FTELL64(pFile); + + if (!archive_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + + archive_size = MZ_FTELL64(pFile) - cur_file_ofs; + + if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = archive_size; + pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; +} + +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint bit_flag; + mz_uint method; + + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); + bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + + if ((method != 0) && (method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + return MZ_FALSE; + } + + if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + return MZ_FALSE; + } + + if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, attribute_mapping_id, external_attr; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ + /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ + /* FIXME: Remove this check? Is it necessary - we already check the filename. */ + attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; + (void)attribute_mapping_id; + + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) + { + return MZ_TRUE; + } + + return MZ_FALSE; +} + +static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) +{ + mz_uint n; + const mz_uint8 *p = pCentral_dir_header; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_FALSE; + + if ((!p) || (!pStat)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Extract fields from the central directory record. */ + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + /* Copy as much of the filename and comment as possible. */ + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); + pStat->m_comment[n] = '\0'; + + /* Set some flags for convienance */ + pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); + pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); + pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); + + /* See if we need to read any zip64 extended information fields. */ + /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ + if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; + mz_uint32 field_data_remaining = field_data_size; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_TRUE; + + if (pStat->m_uncomp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_uncomp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_comp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_comp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_local_header_ofs == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + } + } + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const uint32_t size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + + if (pIndex) + *pIndex = 0; + + if (size) + { + /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ + /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ + mz_int64 l = 0, h = (mz_int64)size - 1; + + while (l <= h) + { + mz_int64 m = l + ((h - l) >> 1); + uint32_t file_index = pIndices[(uint32_t)m]; + + int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint32 index; + if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) + return -1; + else + return (int)index; +} + +mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) +{ + mz_uint file_index; + size_t name_len, comment_len; + + if (pIndex) + *pIndex = 0; + + if ((!pZip) || (!pZip->m_pState) || (!pName)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* See if we can use a binary search */ + if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && + (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && + ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + { + return mz_zip_locate_file_binary_search(pZip, pName, pIndex); + } + + /* Locate the entry by scanning the entire central directory */ + name_len = strlen(pName); + if (name_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + comment_len = pComment ? strlen(pComment) : 0; + if (comment_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; + filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Ensure supplied output buffer is large enough. */ + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); + + /* Read and parse the local directory entry. */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) + { + if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + } +#endif + + return MZ_TRUE; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + /* Read directly from the archive in memory. */ + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + /* Use a user provided read buffer. */ + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + else + { + /* Temporarily allocate a read buffer. */ + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return NULL; + } + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + return NULL; + } + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) + *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + { + if (pSize) + *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32 = MZ_CRC32_INIT; +#endif + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; + void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pState->m_pMem) + { + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + } + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); +#endif + } + + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + } +#endif + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + status = TINFL_STATUS_FAILED; + } + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); +#endif + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (file_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_reader_extract_iter_state *pState; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + /* Argument sanity check */ + if ((!pZip) || (!pZip->m_pState)) + return NULL; + + /* Allocate an iterator status structure */ + pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); + if (!pState) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + /* Fetch file details */ + if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Encryption and patch files are not supported. */ + if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Init state - save args */ + pState->pZip = pZip; + pState->flags = flags; + + /* Init state - reset variables to defaults */ + pState->status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + pState->file_crc32 = MZ_CRC32_INIT; +#endif + pState->read_buf_ofs = 0; + pState->out_buf_ofs = 0; + pState->pRead_buf = NULL; + pState->pWrite_buf = NULL; + pState->out_blk_remain = 0; + + /* Read and parse the local directory entry. */ + pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; + pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + else + { + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, therefore intermediate read buffer required */ + pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + else + { + /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ + pState->read_buf_size = 0; + } + pState->read_buf_avail = 0; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, init decompressor */ + tinfl_init( &pState->inflator ); + + /* Allocate write buffer */ + if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + if (pState->pRead_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + + return pState; +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_uint32 file_index; + + /* Locate file index by name */ + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return NULL; + + /* Construct iterator */ + return mz_zip_reader_extract_iter_new(pZip, file_index, flags); +} + +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) +{ + size_t copied_to_caller = 0; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) + return 0; + + if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data, calc amount to return. */ + copied_to_caller = (size_t)MZ_MIN( buf_size, pState->comp_remaining ); + + /* Zip is in memory....or requires reading from a file? */ + if (pState->pZip->m_pState->m_pMem) + { + /* Copy data to caller's buffer */ + memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); + pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; + } + else + { + /* Read directly into caller's buffer */ + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) + { + /* Failed to read all that was asked for, flag failure and alert user */ + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + copied_to_caller = 0; + } + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Compute CRC if not returning compressed data only */ + if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); +#endif + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += copied_to_caller; + pState->out_buf_ofs += copied_to_caller; + pState->comp_remaining -= copied_to_caller; + } + else + { + do + { + /* Calc ptr to write buffer - given current output pos and block size */ + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + /* Calc max output size - given current output pos and block size */ + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + if (!pState->out_blk_remain) + { + /* Read more data from file if none available (and reading from file) */ + if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) + { + /* Calc read size */ + pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += pState->read_buf_avail; + pState->comp_remaining -= pState->read_buf_avail; + pState->read_buf_ofs = 0; + } + + /* Perform decompression */ + in_buf_size = (size_t)pState->read_buf_avail; + pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + pState->read_buf_avail -= in_buf_size; + pState->read_buf_ofs += in_buf_size; + + /* Update current output block size remaining */ + pState->out_blk_remain = out_buf_size; + } + + if (pState->out_blk_remain) + { + /* Calc amount to return. */ + size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); + + /* Copy data to caller's buffer */ + memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Perform CRC */ + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); +#endif + + /* Decrement data consumed from block */ + pState->out_blk_remain -= to_copy; + + /* Inc output offset, while performing sanity check */ + if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Increment counter of data copied to caller */ + copied_to_caller += to_copy; + } + } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); + } + + /* Return how many bytes were copied into user buffer */ + return copied_to_caller; +} + +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) +{ + int status; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) + return MZ_FALSE; + + /* Was decompression completed and requested? */ + if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + pState->status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (pState->file_crc32 != pState->file_stat.m_crc32) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + } +#endif + } + + /* Free buffers */ + if (!pState->pZip->m_pState->m_pMem) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); + if (pState->pWrite_buf) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); + + /* Save status */ + status = pState->status; + + /* Free context */ + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); + + return status == TINFL_STATUS_DONE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; + + return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + + if (MZ_FCLOSE(pFile) == EOF) + { + if (status) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + + status = MZ_FALSE; + } + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + + return status; +} + +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} + +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); +} + +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_uint32 *p = (mz_uint32 *)pOpaque; + (void)file_ofs; + *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); + return n; +} + +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_zip_internal_state *pState; + const mz_uint8 *pCentral_dir_header; + mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint64 local_header_ofs = 0; + mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; + mz_bool has_data_descriptor; + mz_uint32 local_header_bit_flags; + + mz_zip_array file_data_array; + mz_zip_array_init(&file_data_array, 1); + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (file_index > pZip->m_total_files) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); + + if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_is_encrypted) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports stored and deflate. */ + if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + if (!file_stat.m_is_supported) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + /* Read and parse the local directory entry. */ + local_header_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); + local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + has_data_descriptor = (local_header_bit_flags & 8) != 0; + + if (local_header_filename_len != strlen(file_stat.m_filename)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + goto handle_failure; + } + + if (local_header_filename_len) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ + if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_uint32 extra_size_remaining = local_header_extra_len; + const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ + /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ + if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) + { + mz_uint8 descriptor_buf[32]; + mz_bool has_id; + const mz_uint8 *pSrc; + mz_uint32 file_crc32; + mz_uint64 comp_size = 0, uncomp_size = 0; + + mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; + + file_crc32 = MZ_READ_LE32(pSrc); + + if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); + } + else + { + comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); + } + + if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + else + { + if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + mz_zip_array_clear(pZip, &file_data_array); + + if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) + { + if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) + return MZ_FALSE; + + /* 1 more check to be sure, although the extract checks too. */ + if (uncomp_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + return MZ_FALSE; + } + } + + return MZ_TRUE; + +handle_failure: + mz_zip_array_clear(pZip, &file_data_array); + return MZ_FALSE; +} + +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) +{ + mz_zip_internal_state *pState; + uint32_t i; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Basic sanity checks */ + if (!pState->m_zip64) + { + if (pZip->m_total_files > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pZip->m_archive_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + else + { + if (pZip->m_total_files >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + for (i = 0; i < pZip->m_total_files; i++) + { + if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) + { + mz_uint32 found_index; + mz_zip_archive_file_stat stat; + + if (!mz_zip_reader_file_stat(pZip, i, &stat)) + return MZ_FALSE; + + if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) + return MZ_FALSE; + + /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ + if (found_index != i) + return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + } + + if (!mz_zip_validate_file(pZip, i, flags)) + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if ((!pMem) || (!size)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if (!pFilename) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +/* ------------------- .ZIP archive writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); +} +static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); + p[2] = (mz_uint8)(v >> 16); + p[3] = (mz_uint8)(v >> 24); +} +static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) +{ + mz_write_le32(p, (mz_uint32)v); + mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); +} + +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) +#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + + if (!n) + return 0; + + /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ + if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + return 0; + } + + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + + while (new_capacity < new_size) + new_capacity *= 2; + + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return 0; + } + + pState->m_pMem = pNew_block; + pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + status = MZ_FALSE; + } + } + + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) +{ + mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; + + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + { + if (!pZip->m_pRead) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (pZip->m_file_offset_alignment) + { + /* Ensure user specified file offset alignment is a power of 2. */ + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + + pZip->m_pState->m_zip64 = zip64; + pZip->m_pState->m_zip64_has_extended_info_fields = zip64; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + return mz_zip_writer_init_v2(pZip, existing_size, 0); +} + +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_mem_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; + + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + return 0; + } + + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); +} + +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) +{ + MZ_FILE *pFile; + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + + pZip->m_pState->m_pFile = pFile; + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; + char buf[4096]; + + MZ_CLEAR_OBJ(buf); + + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_ofs += n; + size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, 0, flags)) + return MZ_FALSE; + + pZip->m_pState->m_pFile = pFile; + pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_zip_internal_state *pState; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) + { + /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ + if (!pZip->m_pState->m_zip64) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* No sense in trying to write to an archive that's already at the support max size */ + if (pZip->m_pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + (void)pFilename; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +#else + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (!pFilename) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + } + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; +#endif /* #ifdef MINIZ_NO_STDIO */ + } + else if (pState->m_pMem) + { + /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + } + /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ + else if (!pZip->m_pWrite) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Start writing new files at the archive's current central directory location. */ + /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_central_directory_file_ofs = 0; + + /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ + /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ + /* TODO: We could easily maintain the sorted central directory offsets. */ + mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); +} + +/* TODO: pArchive_name is a terrible name here! */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) +#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) +static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) +{ + mz_uint8 *pDst = pBuf; + mz_uint32 field_size = 0; + + MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + MZ_WRITE_LE16(pDst + 2, 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + MZ_WRITE_LE64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pComp_size) + { + MZ_WRITE_LE64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + MZ_WRITE_LE64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + MZ_WRITE_LE16(pBuf + 2, field_size); + + return (mz_uint32)(pDst - pBuf); +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, + mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, + const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes, + const char *user_extra_data, mz_uint user_extra_data_len) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + if (!pZip->m_pState->m_zip64) + { + if (local_header_ofs > 0xFFFFFFFF) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, (mz_uint16)(extra_size + user_extra_data_len), comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + /* Try to resize the central directory array back into its original state. */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ + if (*pArchive_name == '/') + return MZ_FALSE; + + /* Making sure the name does not contain drive letters or DOS style backward slashes is the responsibility of the program using miniz*/ + + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_file_ofs += s; + n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); +} + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_uint16 bit_flags = 0; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +#ifndef MINIZ_NO_TIME + if (last_modified != NULL) + { + mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); + } + else + { + MZ_TIME_T cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif /* #ifndef MINIZ_NO_TIME */ + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + /* Set DOS Subdirectory attribute bit. */ + ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; + + /* Subdirectories cannot contain data. */ + if ((buf_size) || (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + cur_archive_file_ofs += num_alignment_padding_bytes; + + MZ_CLEAR_OBJ(local_dir_header); + + if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + method = MZ_DEFLATED; + } + + if (pState->m_zip64) + { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + + if (pExtra_data != NULL) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + if (uncomp_size) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_zip_internal_state *pState; + mz_uint64 file_ofs = 0, cur_archive_header_file_ofs; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX)) + { + /* Source file is too large for non-zip64 */ + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + pState->m_zip64 = MZ_TRUE; + } + + /* We could support this, but why? */ + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + +#ifndef MINIZ_NO_TIME + if (pFile_time) + { + mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); + } +#endif + + if (max_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_archive_file_ofs; + + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + if (max_size && level) + { + method = MZ_DEFLATED; + } + + MZ_CLEAR_OBJ(local_dir_header); + if (pState->m_zip64) + { + if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + else + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, NULL, + NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (max_size) + { + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!level) + { + while (1) + { + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if (n == 0) + break; + + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + cur_archive_file_ofs += n; + } + uncomp_size = file_ofs; + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + } + + for (;;) + { + tdefl_status status; + tdefl_flush flush = TDEFL_NO_FLUSH; + + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + break; + } + + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + + if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) + flush = TDEFL_FULL_FLUSH; + + if (n == 0) + flush = TDEFL_FINISH; + + status = tdefl_compress_buffer(pComp, pRead_buf, n, flush); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + { + mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + break; + } + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return MZ_FALSE; + } + + uncomp_size = file_ofs; + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + if (!(level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + { + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, + (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size, + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size, + uncomp_crc32, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + cur_archive_header_file_ofs = local_dir_header_ofs; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + if (pExtra_data != NULL) + { + cur_archive_header_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_header_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_header_file_ofs += extra_size; + } + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO + +static size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + MZ_FILE *pSrc_file = (MZ_FILE *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pSrc_file); + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pSrc_file, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pSrc_file); +} + +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + return mz_zip_writer_add_read_buf_callback(pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, max_size, pFile_time, pComment, comment_size, level_and_flags, + user_extra_data, user_extra_data_len, user_extra_data_central, user_extra_data_central_len); +} + +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + MZ_FILE *pSrc_file = NULL; + mz_uint64 uncomp_size = 0; + MZ_TIME_T file_modified_time; + MZ_TIME_T *pFile_time = NULL; + mz_bool status; + + memset(&file_modified_time, 0, sizeof(file_modified_time)); + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + pFile_time = &file_modified_time; + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); +#endif + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); + + MZ_FCLOSE(pSrc_file); + + return status; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) +{ + /* + 64 should be enough for any new zip64 data */ + if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); + + if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) + { + mz_uint8 new_ext_block[64]; + mz_uint8 *pDst = new_ext_block; + mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + mz_write_le16(pDst + sizeof(mz_uint16), 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + mz_write_le64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + } + + if (pComp_size) + { + mz_write_le64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + mz_write_le64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + } + + if (pDisk_start) + { + mz_write_le32(pDst, *pDisk_start); + pDst += sizeof(mz_uint32); + } + + mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); + + if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if ((pExt) && (ext_len)) + { + mz_uint32 extra_size_remaining = ext_len; + const mz_uint8 *pExtra_data = pExt; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + return MZ_TRUE; +} + +static mz_uint32 read_32ne(const mz_uint8 *p) // OpenMPT +{ // OpenMPT + mz_uint32 result = 0; // OpenMPT + memcpy(&result, p, sizeof(mz_uint32)); // OpenMPT + return result; // OpenMPT +} // OpenMPT + +/* TODO: This func is now pretty freakin complex due to zip64, split it up? */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; + mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; + const mz_uint8 *pSrc_central_header; + mz_zip_archive_file_stat src_file_stat; + mz_uint32 src_filename_len, src_comment_len, src_ext_len; + mz_uint32 local_header_filename_size, local_header_extra_len; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ + if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Get pointer to the source central dir header and crack it */ + if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); + src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); + src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; + + /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ + if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + if (!pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) + return MZ_FALSE; + + cur_src_file_ofs = src_file_stat.m_local_header_ofs; + cur_dst_file_ofs = pZip->m_archive_size; + + /* Read the source archive's local dir header */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Compute the total size we need to copy (filename+extra data+compressed data) */ + local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; + + /* Try to find a zip64 extended information field */ + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_zip_array file_data_array; + const mz_uint8 *pExtra_data; + mz_uint32 extra_size_remaining = local_header_extra_len; + + mz_zip_array_init(&file_data_array, 1); + if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + + mz_zip_array_clear(pZip, &file_data_array); + } + + if (!pState->m_zip64) + { + /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ + /* We also check when the archive is finalized so this doesn't need to be perfect. */ + mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; + + if (approx_new_archive_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + /* Write dest archive padding */ + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + + cur_dst_file_ofs += num_alignment_padding_bytes; + + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + while (src_archive_bytes_remaining) + { + n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_dst_file_ofs += n; + + src_archive_bytes_remaining -= n; + } + + /* Now deal with the optional data descriptor */ + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + /* Copy data descriptor */ + if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + /* src is zip64, dest must be zip64 */ + + /* name uint32_t's */ + /* id 1 (optional in zip64?) */ + /* crc 1 */ + /* comp_size 2 */ + /* uncomp_size 2 */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); + } + else + { + /* src is NOT zip64 */ + mz_bool has_id; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + + if (pZip->m_pState->m_zip64) + { + /* dest is zip64, so upgrade the data descriptor */ +#if 0 // OpenMPT + const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0)); + const mz_uint32 src_crc32 = pSrc_descriptor[0]; + const mz_uint64 src_comp_size = pSrc_descriptor[1]; + const mz_uint64 src_uncomp_size = pSrc_descriptor[2]; +#else // OpenMPT + const mz_uint8 *pSrc_descriptor = (const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0); // OpenMPT + const mz_uint32 src_crc32 = read_32ne(pSrc_descriptor + (0 * sizeof(mz_uint32))); // OpenMPT + const mz_uint64 src_comp_size = read_32ne(pSrc_descriptor + (1 * sizeof(mz_uint32)));; // OpenMPT + const mz_uint64 src_uncomp_size = read_32ne(pSrc_descriptor + (2 * sizeof(mz_uint32))); // OpenMPT +#endif // OpenMPT + + mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); + mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); + + n = sizeof(mz_uint32) * 6; + } + else + { + /* dest is NOT zip64, just copy it as-is */ + n = sizeof(mz_uint32) * (has_id ? 4 : 3); + } + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + /* Finally, add the new central dir header */ + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + + if (pState->m_zip64) + { + /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ + const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; + mz_zip_array new_ext_block; + + mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); + + if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return MZ_FALSE; + } + + MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + mz_zip_array_clear(pZip, &new_ext_block); + } + else + { + /* sanity checks */ + if (cur_dst_file_ofs > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (local_dir_header_ofs >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + } + + /* This shouldn't trigger unless we screwed up during the initial sanity checks */ + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + { + /* TODO: Support central dirs >= 32-bits in size */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + } + + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[256]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + /* Write central directory */ + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += central_dir_size; + } + + if (pState->m_zip64) + { + /* Write zip64 end of central directory header */ + mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; + + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; + + /* Write zip64 end of central directory locator */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; + } + + /* Write end of central directory record */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) +{ + if ((!ppBuf) || (!pSize)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + *ppBuf = NULL; + *pSize = 0; + + if ((!pZip) || (!pZip->m_pState)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_pWrite != mz_zip_heap_write_func) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *ppBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + return mz_zip_writer_end_internal(pZip, MZ_TRUE); +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); +} + +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + mz_zip_zero_struct(&zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_FILENAME; + return MZ_FALSE; + } + + /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ + /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + /* Create a new archive. */ + if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + created_new_archive = MZ_TRUE; + } + else + { + /* Append to an existing archive. */ + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); + + return MZ_FALSE; + } + } + + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + actual_err = zip_archive.m_last_error; + + /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ + if (!mz_zip_writer_finalize_archive(&zip_archive)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if (!mz_zip_writer_end_internal(&zip_archive, status)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if ((!status) && (created_new_archive)) + { + /* It's a new archive and something went wrong, so just delete it. */ + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + + if (pErr) + *pErr = actual_err; + + return status; +} + +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) +{ + mz_uint32 file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + + return NULL; + } + + mz_zip_zero_struct(&zip_archive); + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + return NULL; + } + + if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) + { + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + } + + mz_zip_reader_end_internal(&zip_archive, p != NULL); + + if (pErr) + *pErr = zip_archive.m_last_error; + + return p; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* ------------------- Misc utils */ + +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; +} + +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; +} + +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = err_num; + return prev_err; +} + +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + return pZip->m_last_error; +} + +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) +{ + return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); +} + +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = MZ_ZIP_NO_ERROR; + return prev_err; +} + +const char *mz_zip_get_error_string(mz_zip_error mz_err) +{ + switch (mz_err) + { + case MZ_ZIP_NO_ERROR: + return "no error"; + case MZ_ZIP_UNDEFINED_ERROR: + return "undefined error"; + case MZ_ZIP_TOO_MANY_FILES: + return "too many files"; + case MZ_ZIP_FILE_TOO_LARGE: + return "file too large"; + case MZ_ZIP_UNSUPPORTED_METHOD: + return "unsupported method"; + case MZ_ZIP_UNSUPPORTED_ENCRYPTION: + return "unsupported encryption"; + case MZ_ZIP_UNSUPPORTED_FEATURE: + return "unsupported feature"; + case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: + return "failed finding central directory"; + case MZ_ZIP_NOT_AN_ARCHIVE: + return "not a ZIP archive"; + case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: + return "invalid header or archive is corrupted"; + case MZ_ZIP_UNSUPPORTED_MULTIDISK: + return "unsupported multidisk archive"; + case MZ_ZIP_DECOMPRESSION_FAILED: + return "decompression failed or archive is corrupted"; + case MZ_ZIP_COMPRESSION_FAILED: + return "compression failed"; + case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: + return "unexpected decompressed size"; + case MZ_ZIP_CRC_CHECK_FAILED: + return "CRC-32 check failed"; + case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: + return "unsupported central directory size"; + case MZ_ZIP_ALLOC_FAILED: + return "allocation failed"; + case MZ_ZIP_FILE_OPEN_FAILED: + return "file open failed"; + case MZ_ZIP_FILE_CREATE_FAILED: + return "file create failed"; + case MZ_ZIP_FILE_WRITE_FAILED: + return "file write failed"; + case MZ_ZIP_FILE_READ_FAILED: + return "file read failed"; + case MZ_ZIP_FILE_CLOSE_FAILED: + return "file close failed"; + case MZ_ZIP_FILE_SEEK_FAILED: + return "file seek failed"; + case MZ_ZIP_FILE_STAT_FAILED: + return "file stat failed"; + case MZ_ZIP_INVALID_PARAMETER: + return "invalid parameter"; + case MZ_ZIP_INVALID_FILENAME: + return "invalid filename"; + case MZ_ZIP_BUF_TOO_SMALL: + return "buffer too small"; + case MZ_ZIP_INTERNAL_ERROR: + return "internal error"; + case MZ_ZIP_FILE_NOT_FOUND: + return "file not found"; + case MZ_ZIP_ARCHIVE_TOO_LARGE: + return "archive is too large"; + case MZ_ZIP_VALIDATION_FAILED: + return "validation failed"; + case MZ_ZIP_WRITE_CALLBACK_FAILED: + return "write calledback failed"; + default: + break; + } + + return "unknown error"; +} + +/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return MZ_FALSE; + + return pZip->m_pState->m_zip64; +} + +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + + return pZip->m_pState->m_central_dir.m_size; +} + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) +{ + if (!pZip) + return 0; + return pZip->m_archive_size; +} + +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_file_archive_start_ofs; +} + +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_pFile; +} + +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + if (filename_buf_size) + pFilename[0] = '\0'; + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return 0; + } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); +} + +mz_bool mz_zip_end(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_FALSE; + + if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) + return mz_zip_reader_end(pZip); +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) + return mz_zip_writer_end(pZip); +#endif + + return MZ_FALSE; +} + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ diff --git a/Frameworks/OpenMPT.old/OpenMPT/include/miniz/miniz.h b/Frameworks/OpenMPT.old/OpenMPT/include/miniz/miniz.h new file mode 100644 index 000000000..5877797ea --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/include/miniz/miniz.h @@ -0,0 +1,1367 @@ +#define MINIZ_EXPORT +/* miniz.c 2.2.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateReset/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ +#pragma once + + + +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ + +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ +#define MINIZ_NO_STDIO // OpenMPT + +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +/*#define MINIZ_NO_TIME */ + +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ + +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME +#endif + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ +#define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 +#endif + +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) // OpenMPT +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif +#else // OpenMPT +#if MINIZ_X86_OR_X64_CPU // OpenMPT +#define MINIZ_LITTLE_ENDIAN 1 // OpenMPT +#else // OpenMPT +#define MINIZ_LITTLE_ENDIAN 0 // OpenMPT +#endif // OpenMPT +#endif // OpenMPT + +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ +#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +#if 0 // OpenMPT +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#define MINIZ_UNALIGNED_USE_MEMCPY +#else // OpenMPT +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 // OpenMPT +#endif // OpenMPT +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ +#define MINIZ_HAS_64BIT_REGISTERS 1 +#else +#define MINIZ_HAS_64BIT_REGISTERS 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API Definitions. */ + +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ +typedef unsigned long mz_ulong; + +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ +MINIZ_EXPORT void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +/* Compression strategies. */ +enum +{ + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; + +/* Method */ +#define MZ_DEFLATED 8 + +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum +{ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +#define MZ_VERSION "10.2.0" +#define MZ_VERNUM 0xA100 +#define MZ_VER_MAJOR 10 +#define MZ_VER_MINOR 2 +#define MZ_VER_REVISION 0 +#define MZ_VER_SUBREVISION 0 + +#ifndef MINIZ_NO_ZLIB_APIS + +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +enum +{ + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; + +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum +{ + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +/* Window bits */ +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +/* Compression/decompression stream struct. */ +typedef struct mz_stream_s +{ + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ + + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ + + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ + + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ + + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ +} mz_stream; + +typedef mz_stream *mz_streamp; + +/* Returns the version string of miniz.c. */ +MINIZ_EXPORT const char *mz_version(void); + +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ +MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); + +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ +MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); + +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ +MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); + +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); + +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); + +/* Initializes a decompressor. */ +MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); + +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ +MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ +MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); + +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ +MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); + +/* Deinitializes a decompressor. */ +MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); + +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ +MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); + +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +MINIZ_EXPORT const char *mz_error(int err); + +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflateReset mz_inflateReset +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define uncompress2 mz_uncompress2 +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + + + + + +#pragma once +#include +#include +#include +#include + + + +/* ------------------- Types and macros */ +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef int64_t mz_int64; +typedef uint64_t mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ + +#ifdef MINIZ_NO_TIME +typedef struct mz_dummy_time_t_tag +{ + int m_dummy; +} mz_dummy_time_t; +#define MZ_TIME_T mz_dummy_time_t +#else +#define MZ_TIME_T time_t +#endif + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); +extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif + #pragma once + + +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Compression API Definitions */ + +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 + +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; + +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif + +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; + +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +/* tdefl's compression state structure. */ +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); +MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); +#endif + +#ifdef __cplusplus +} +#endif + #pragma once + +/* ------------------- Low-level Decompression API Definitions */ + +#ifdef __cplusplus +extern "C" { +#endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); +MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); +#endif + +/* Max size of LZ dictionary. */ +#define TINFL_LZ_DICT_SIZE 32768 + +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) \ + do \ + { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ +MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +/* Internal/private bits follow. */ +enum +{ + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 +#else +#define TINFL_USE_64BIT_BITBUF 0 +#endif + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) +#else +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +#ifdef __cplusplus +} +#endif + +#pragma once + + +/* ------------------- ZIP archive reading/writing */ + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +}; + +typedef struct +{ + /* Central directory file index. */ + mz_uint32 m_file_index; + + /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ + mz_uint64 m_central_dir_ofs; + + /* These fields are copied directly from the zip's central dir. */ + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; + +#ifndef MINIZ_NO_TIME + MZ_TIME_T m_time; +#endif + + /* CRC-32 of uncompressed data. */ + mz_uint32 m_crc32; + + /* File's compressed size. */ + mz_uint64 m_comp_size; + + /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ + mz_uint64 m_uncomp_size; + + /* Zip internal and external file attributes. */ + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + + /* Entry's local header file offset in bytes. */ + mz_uint64 m_local_header_ofs; + + /* Size of comment in bytes. */ + mz_uint32 m_comment_size; + + /* MZ_TRUE if the entry appears to be a directory. */ + mz_bool m_is_directory; + + /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ + mz_bool m_is_encrypted; + + /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ + mz_bool m_is_supported; + + /* Filename. If string ends in '/' it's a subdirectory entry. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + + /* Comment field. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; + +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ + MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, + MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000, + /*After adding a compressed file, seek back + to local file header and set the correct sizes*/ + MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000 +} mz_zip_flags; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef struct +{ + mz_zip_archive *pZip; + mz_uint flags; + + int status; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32; +#endif + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + void *pWrite_buf; + + size_t out_blk_remain; + + tinfl_decompressor inflator; + +} mz_zip_reader_extract_iter_state; + +/* -------- ZIP reading */ + +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +#endif + +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +/* -------- ZIP reading or writing */ + +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + +/* Returns the total number of files in the archive. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + +/* Returns detailed information about an archive file entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +/* Extracts a archive file to a memory buffer. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +/* Extracts a archive file using a callback function to output the file's data. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +/* Extract a file iteratively */ +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +#ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +#endif + +#if 0 +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); +#endif + +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); + +/* Misc utils/helpers, valid for ZIP reading or writing */ +MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +#ifndef MINIZ_NO_STDIO // OpenMPT +MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); +#endif // OpenMPT + +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); + +/* -------- ZIP writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); +#endif + +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); + +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +/* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */ +/* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/ +MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + + +#ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); +#endif + +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); + +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + +/* Finalizes a heap archive, returning a poiner to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +/* -------- Misc. high-level helper functions: */ + +#ifndef MINIZ_NO_STDIO // OpenMPT +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); +#endif // OpenMPT + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifdef __cplusplus +} +#endif + +#endif /* MINIZ_NO_ARCHIVE_APIS */ diff --git a/Frameworks/OpenMPT.old/OpenMPT/include/stb_vorbis/OpenMPT.txt b/Frameworks/OpenMPT.old/OpenMPT/include/stb_vorbis/OpenMPT.txt new file mode 100644 index 000000000..e723225a3 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/include/stb_vorbis/OpenMPT.txt @@ -0,0 +1,13 @@ +This folder contains the stb_vorbis library from +https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.22 +commit 5a0bb8b1c1b1ca3f4e2485f4114c1c8ea021b781 (2021-07-12) + +Modifications: + * Use of alloca has been replaced with malloc, as alloca is not in C99 and + fails to compile. + * Macro redefinition of alloca with mingw-w64 has been fixed. + * Macro redefinition of STB_VORBIS_NO_STDIO has been fixed. + +For building, premake is used to generate Visual Studio project files. +See ../build/premake/ for details. + diff --git a/Frameworks/OpenMPT.old/OpenMPT/include/stb_vorbis/stb_vorbis.c b/Frameworks/OpenMPT.old/OpenMPT/include/stb_vorbis/stb_vorbis.c new file mode 100644 index 000000000..1d4deecb8 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/include/stb_vorbis/stb_vorbis.c @@ -0,0 +1,5593 @@ +// Ogg Vorbis audio decoder - v1.22 - public domain +// http://nothings.org/stb_vorbis/ +// +// Original version written by Sean Barrett in 2007. +// +// Originally sponsored by RAD Game Tools. Seeking implementation +// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker, +// Elias Software, Aras Pranckevicius, and Sean Barrett. +// +// LICENSE +// +// See end of file for license information. +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Feature contributors: +// Dougall Johnson (sample-exact seeking) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster github:alxprd +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// Phillip Bennefall Rohit Thiago Goulart +// github:manxorist Saga Musix github:infatum +// Timur Gagiev Maxwell Koo Peter Waller +// github:audinowho Dougall Johnson David Reid +// github:Clownacy Pedro J. Estebanez Remi Verschelde +// AnthoFoxo github:morlat Gabriel Ravier +// +// Partial history: +// 1.22 - 2021-07-11 - various small fixes +// 1.21 - 2021-07-02 - fix bug for files with no comments +// 1.20 - 2020-07-11 - several small fixes +// 1.19 - 2020-02-05 - warnings +// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. +// 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure) +// 1.16 - 2019-03-04 - fix warnings +// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found +// 1.14 - 2018-02-11 - delete bogus dealloca usage +// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) +// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files +// 1.11 - 2017-07-23 - fix MinGW compilation +// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory +// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version +// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame +// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const +// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) +// some crash fixes when out of memory or with corrupt files +// fix some inappropriately signed shifts +// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant +// 1.04 - 2014-08-27 - fix missing const-correct case in API +// 1.03 - 2014-08-07 - warning fixes +// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// +// See end of file for full version history. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +typedef struct +{ + char *vendor; + + int comment_list_length; + char **comment_list; +} stb_vorbis_comment; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get ogg comments +extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char * datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + const stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, + const unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. +// +// *output points into stb_vorbis's internal output buffer storage; these +// buffers are owned by stb_vorbis and application code should not free +// them or modify their contents. They are transient and will be overwritten +// once you ask for more data to get decoded, so be sure to grab any data +// you need before then. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Moreover, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern int stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0) + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of *samples* per channel. +// Note that for interleaved data, you pass in the number of shorts (the +// size of your array), but the return value is the number of samples per +// channel, not the total number of samples. +// +// The data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed, + VORBIS_ogg_skeleton_not_supported +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef STB_VORBIS_HEADER_ONLY + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +#ifdef STB_VORBIS_CODEBOOK_SHORTS +#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #ifndef STB_VORBIS_NO_STDIO // OpenMPT + #define STB_VORBIS_NO_STDIO + #endif // OpenMPT +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT + #include + #include + #include + #include + + // find definition of alloca if it's not in stdlib.h: + #if defined(_MSC_VER) || defined(__MINGW32__) + #include + #endif + #if defined(__linux__) || defined(__linux) || defined(__sun__) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) + #include + #endif +#else // STB_VORBIS_NO_CRT + #define NULL 0 + #define malloc(s) 0 + #define free(s) ((void) 0) + #define realloc(s) 0 +#endif // STB_VORBIS_NO_CRT + +#include + +#ifdef __MINGW32__ + // eff you mingw: + // "fixed": + // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/ + // "no that broke the build, reverted, who cares about C": + // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/ + #ifdef __forceinline + #undef __forceinline + #endif + #define __forceinline +#if 0 // OpenMPT + #ifndef alloca + #define alloca __builtin_alloca + #endif +#endif // OpenMPT +#elif !defined(_MSC_VER) + #if __GNUC__ + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#if 0 +#include +#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) +#else +#define CHECK(f) ((void) 0) +#endif + +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) + + +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +typedef float codetype; + +#ifdef _MSC_VER +#define STBV_NOTUSED(v) (void)(v) +#else +#define STBV_NOTUSED(v) (void)sizeof(v) +#endif + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #else + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #endif + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} Codebook; + +typedef struct +{ + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; + +typedef struct +{ + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; +} Floor1; + +typedef union +{ + Floor0 floor0; + Floor1 floor1; +} Floor; + +typedef struct +{ + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; + +typedef struct +{ + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; + +typedef struct +{ + uint16 coupling_steps; + MappingChannel *chan; + uint8 submaps; + uint8 submap_floor[15]; // varies + uint8 submap_residue[15]; // varies +} Mapping; + +typedef struct +{ + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; + +typedef struct +{ + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page +} CRCscan; + +typedef struct +{ + uint32 page_start, page_end; + uint32 last_decoded_sample; +} ProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + + char *vendor; + int comment_list_length; + char **comment_list; + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + uint32 f_start; + int close_on_free; +#endif + + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; + + uint32 stream_len; + + uint8 push_mode; + + // the page to seek to when seeking to start, may be zero + uint32 first_audio_page_offset; + + // p_first is the page on which the first audio packet ends + // (but not necessarily the page on which it starts) + ProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies + + uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + uint16 *bit_reverse[2]; + + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; +}; + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define IS_PUSH_MODE(f) TRUE +#else + #define IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis vorb; + +static int error(vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#if 0 // OpenMPT +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) +#define temp_free(f,p) (void)0 +#else // OpenMPT +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : malloc(size)) // OpenMPT +#define temp_free(f,p) (f->alloc.alloc_buffer ? (void)0 : free(p)) // OpenMPT +#endif // OpenMPT +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *setup_malloc(vorb *f, int sz) +{ + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; +} + +static void setup_free(vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack + free(p); +} + +static void *setup_temp_malloc(vorb *f, int sz) +{ + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); +} + +static void setup_temp_free(vorb *f, void *p, int sz) +{ + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+7)&~7; + return; + } + free(p); +} + +#define CRC32_POLY 0x04c11db7 // from spec + +static uint32 crc_table[256]; +static void crc32_init(void) +{ + int i,j; + uint32 s; + for(i=0; i < 256; i++) { + for (s=(uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); + crc_table[i] = s; + } +} + +static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) +{ + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int ilog(int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + if (n < 0) return 0; // signed n returns 0 + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else return 30 + log2_4[n >> 30]; +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float float32_unpack(uint32 x) +{ + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, (int)exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) +{ + int i,k,m=0; + uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + assert(len[k] < 32); // no error return required, code reading lens checks this + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1U << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + assert(z < 32); // no error return required, code reading lens checks this + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { return FALSE; } + res = available[z]; + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propagate availability up the tree + if (z != len[i]) { + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void compute_accelerated_huffman(Codebook *c) +{ + int i, len; + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif + +static int STBV_CDECL uint32_compare(const void *p, const void *q) +{ + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; + return x < y ? -1 : x > y; +} + +static int include_in_sort(Codebook *c, uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int vorbis_validate(uint8 *data) +{ + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +static int lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + if (pow((float) r+1, dim) <= entries) + return -1; + if ((int) floor(pow((float) r, dim)) > entries) + return -1; + return r; +} + +// called twice per file +static void compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void compute_bitreverse(int n, uint16 *rev) +{ + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int init_blocksize(vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; +} + +static void neighbors(uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + uint16 x,id; +} stbv__floor_ordering; + +static int STBV_CDECL point_compare(const void *p, const void *q) +{ + stbv__floor_ordering *a = (stbv__floor_ordering *) p; + stbv__floor_ordering *b = (stbv__floor_ordering *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define USE_MEMORY(z) TRUE +#else + #define USE_MEMORY(z) ((z)->stream) +#endif + +static uint8 get8(vorb *z) +{ + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif +} + +static uint32 get32(vorb *f) +{ + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += (uint32) get8(f) << 24; + return x; +} + +static int getn(vorb *z, uint8 *data, int n) +{ + if (USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif +} + +static void skip(vorb *z, int n) +{ + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif +} + + +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int capture_pattern(vorb *f) +{ + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; + return TRUE; +} + +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 + +static int start_page_no_capturepattern(vorb *f) +{ + uint32 loc0,loc1,n; + if (f->first_decode && !IS_PUSH_MODE(f)) { + f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; + } + // stream structure version + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + f->p_first.page_end = f->p_first.page_start + len; + f->p_first.last_decoded_sample = loc0; + } + f->next_seg = 0; + return TRUE; +} + +static int start_page(vorb *f) +{ + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); +} + +static int start_packet(vorb *f) +{ + while (f->next_seg == -1) { + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int maybe_start_packet(vorb *f) +{ + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); +} + +static int next_segment(vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define EOP (-1) +#define INVALID_BITS (-1) + +static int get8_packet_raw(vorb *f) +{ + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); +} + +static int get8_packet(vorb *f) +{ + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +static int get32_packet(vorb *f) +{ + uint32 x; + x = get8_packet(f); + x += get8_packet(f) << 8; + x += get8_packet(f) << 16; + x += (uint32) get8_packet(f) << 24; + return x; +} + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static uint32 get_bits(vorb *f, int n) +{ + uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + + assert(f->valid_bits >= n); + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +static __forceinline void prep_huffman(vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = get8_packet_raw(f); + if (z == EOP) return; + f->acc += (unsigned) z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5 +}; + +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) +{ + int i; + prep_huffman(f); + + if (c->codewords == NULL && c->sorted_codewords == NULL) + return -1; + + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = codebook_decode_scalar_raw(f,c); \ + } + +#else + +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); + +#endif + +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) DECODE(var,f,c) +#endif + + + + + + +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_BASE(c) (0) + +static int codebook_decode_start(vorb *f, Codebook *c) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) +{ + int i,z = codebook_decode_start(f,c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) +{ + int i,z = codebook_decode_start(f,c); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +static int predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define LINE_OP(a,b) a *= b +#else +#define LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +#endif + +static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + if (x < x1) { + LINE_OP(output[x], inverse_db_table[y&255]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y&255]); + } + } +} + +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +// n is 1/2 of the blocksize -- +// specification: "Correct per-vector decode length is [n]/2" +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) +{ + int i,j,pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + unsigned int actual_size = rtype == 2 ? n*2 : n; + unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); + unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + CHECK(f); + + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch > 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + CHECK(f); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks+r->classbook; + int temp; + DECODE(temp,f,c); + if (temp == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + CHECK(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + temp_free(f,part_classdata); + #else + temp_free(f,classifications); + #endif + temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#elif 0 +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + buffer[i] = acc; + } +} + +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +static __forceinline void iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + float l00,l11; + + k00 = z[-0] - z[ -8]; + k11 = z[-1] - z[ -9]; + l00 = z[-2] - z[-10]; + l11 = z[-3] - z[-11]; + z[ -0] = z[-0] + z[ -8]; + z[ -1] = z[-1] + z[ -9]; + z[ -2] = z[-2] + z[-10]; + z[ -3] = z[-3] + z[-11]; + z[ -8] = k00; + z[ -9] = k11; + z[-10] = (l00+l11) * A2; + z[-11] = (l11-l00) * A2; + + k00 = z[ -4] - z[-12]; + k11 = z[ -5] - z[-13]; + l00 = z[ -6] - z[-14]; + l11 = z[ -7] - z[-15]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-12] = k11; + z[-13] = -k00; + z[-14] = (l11-l00) * A2; + z[-15] = (l00+l11) * -A2; + + iter_54(z); + iter_54(z-8); + z -= 16; + } +} + +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propagates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + temp_free(f,buf2); + temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *get_window(vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef int16 YTYPE; +#else +typedef int YTYPE; +#endif +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + STBV_NOTUSED(step2_flag); + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + draw_line(target, lx,ly, hx,hy, n2); + CHECK(f); + lx = hx, ly = hy; + } + } + if (lx < n2) { + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); + CHECK(f); + } + } + return TRUE; +} + +// The meaning of "left" and "right" +// +// For a given frame: +// we compute samples from 0..n +// window_center is n/2 +// we'll window and mix the samples from left_start to left_end with data from the previous frame +// all of the samples from left_end to right_start can be output without mixing; however, +// this interval is 0-length except when transitioning between short and long frames +// all of the samples from right_start to right_end need to be mixed with the next frame, +// which we don't have, so those get saved in a buffer +// frame N's right_end-right_start, the number of samples to mix with the next frame, +// has to be the same as frame N+1's left_end-left_start (which they are by +// construction) + +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f,1); + next = get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + + return TRUE; +} + +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + Mapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + +// WINDOWING + + STBV_NOTUSED(left_end); + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + CHECK(f); + + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + CHECK(f); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + + CHECK(f); +// RESIDUE DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + CHECK(f); + +// INVERSE COUPLING + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + CHECK(f); + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + CHECK(f); + for (i=0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + CHECK(f); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around) + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet; + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + (right_end-left_start)) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; // this doesn't seem right, but has no ill effect on my test files + if (*len > right_end) *len = right_end; // this should never happen + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + CHECK(f); + + return TRUE; +} + +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = get_window(f, n); + if (w == NULL) return 0; + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static int vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left, res; + res = vorbis_decode_packet(f, &len, &left, &right); + if (res) + vorbis_finish_frame(f, len, left, right); + return res; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int is_whole_packet_present(stb_vorbis *f) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int start_decoder(vorb *f) +{ + uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + f->first_decode = TRUE; + + if (!start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) { + // check for the Ogg skeleton fishead identifying header to refine our error + if (f->segments[0] == 64 && + getn(f, header, 6) && + header[0] == 'f' && + header[1] == 'i' && + header[2] == 's' && + header[3] == 'h' && + header[4] == 'e' && + header[5] == 'a' && + get8(f) == 'd' && + get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported); + else + return error(f, VORBIS_invalid_first_page); + } + + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { + int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + + if (!next_segment(f)) return FALSE; + + if (get8_packet(f) != VORBIS_packet_comment) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + //file vendor + len = get32_packet(f); + f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->vendor == NULL) return error(f, VORBIS_outofmem); + for(i=0; i < len; ++i) { + f->vendor[i] = get8_packet(f); + } + f->vendor[len] = (char)'\0'; + //user comments + f->comment_list_length = get32_packet(f); + f->comment_list = NULL; + if (f->comment_list_length > 0) + { + f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length)); + if (f->comment_list == NULL) return error(f, VORBIS_outofmem); + } + + for(i=0; i < f->comment_list_length; ++i) { + len = get32_packet(f); + f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem); + + for(j=0; j < len; ++j) { + f->comment_list[i][j] = get8_packet(f); + } + f->comment_list[i][len] = (char)'\0'; + } + + // framing_flag + x = get8_packet(f); + if (!(x & 1)) return error(f, VORBIS_invalid_setup); + + + skip(f, f->bytes_in_seg); + f->bytes_in_seg = 0; + + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f,8) + 1; + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total=0; + uint8 *lengths; + Codebook *c = f->codebooks+i; + CHECK(f); + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); + + if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); + + if (c->sparse) + lengths = (uint8 *) setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + + if (!lengths) return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (current_length >= 32) return error(f, VORBIS_invalid_setup); + if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f,1) : 1; + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + if (lengths[j] == 32) + return error(f, VORBIS_invalid_setup); + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + CHECK(f); + if (!c->sparse) { + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) setup_temp_free(f, values, 0); + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + CHECK(f); + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); + if (c->lookup_type == 1) { + int values = lookup1_values(c->entries, c->dimensions); + if (values < 0) return error(f, VORBIS_invalid_setup); + c->lookup_values = (uint32) values; + } else { + c->lookup_values = c->entries * c->dimensions; + } + if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + float last=0; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + unsigned int z = sparse ? c->sorted_values[j] : j; + unsigned int div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + float val = mults[off]*c->delta_value + c->minimum_value + last; + c->multiplicands[j*c->dimensions + k] = val; + if (c->sequence_p) + last = val; + if (k+1 < c->dimensions) { + if (div > UINT_MAX / (unsigned int) c->lookup_values) { + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + return error(f, VORBIS_invalid_setup); + } + div *= c->lookup_values; + } + } + } + c->lookup_type = 2; + } + else +#endif + { + float last=0; + CHECK(f); + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + for (j=0; j < (int) c->lookup_values; ++j) { + float val = mults[j] * c->delta_value + c->minimum_value + last; + c->multiplicands[j] = val; + if (c->sequence_p) + last = val; + } + } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + skip:; +#endif + setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); + + CHECK(f); + } + CHECK(f); + } + + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6)+1; + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return error(f, VORBIS_outofmem); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); + } else { + stbv__floor_ordering p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = (int16)get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].id = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values-1; ++j) + if (p[j].x == p[j+1].x) + return error(f, VORBIS_invalid_setup); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (uint8) p[j].id; + // precompute the neighbors + for (j=2; j < g->values; ++j) { + int low = 0,hi = 0; + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6)+1; + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i=0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + if (r->end < r->begin) return error(f, VORBIS_invalid_setup); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); + for (j=0; j < r->classifications; ++j) { + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = get_bits(f,6)+1; + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return error(f, VORBIS_outofmem); + if (get_bits(f,1)) + m->submaps = get_bits(f,4)+1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup); + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); + m->chan[k].angle = get_bits(f, ilog(f->channels-1)); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); + } + + flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); + memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); + #endif + } + + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + unsigned int actual_size = f->blocksize_1 / 2; + unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; + unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + // maximum reasonable partition size is f->blocksize_1 + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return error(f, VORBIS_outofmem); + } + + // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page + // without PAGEFLAG_continued_packet, so this either points to the first page, or + // the page after the end of the headers. It might be cleaner to point to a page + // in the middle of the headers, when that's the page where the first audio packet + // starts, but we'd have to also correctly skip the end of any continued packet in + // stb_vorbis_seek_start. + if (f->next_seg == -1) { + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + } else { + f->first_audio_page_offset = 0; + } + + return TRUE; +} + +static void vorbis_deinit(stb_vorbis *p) +{ + int i,j; + + setup_free(p, p->vendor); + for (i=0; i < p->comment_list_length; ++i) { + setup_free(p, p->comment_list[i]); + } + setup_free(p, p->comment_list); + + if (p->residue_config) { + for (i=0; i < p->residue_count; ++i) { + Residue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + } + + if (p->codebooks) { + CHECK(p); + for (i=0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + if (p->mapping) { + for (i=0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + } + CHECK(p); + for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + setup_free(p, p->floor_buffers[i]); + #endif + setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + vorbis_deinit(p); + setup_free(p,p); +} + +static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes &= ~7; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) +{ + stb_vorbis_comment d; + d.vendor = f->vendor; + d.comment_list_length = f->comment_list_length; + d.comment_list = f->comment_list; + return d; +} + +int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, ogg_page_header, 4)) { + int j,len; + uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + const uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); + } + + f->stream = (uint8 *) data; + f->stream_end = (uint8 *) data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!is_whole_packet_present(f)) { + *samples = 0; + return 0; + } + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return (int) (f->stream - data); +} + +stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + vorbis_deinit(&p); + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = (int) (f->stream - data); + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); + #ifndef STB_VORBIS_NO_STDIO + return (unsigned int) (ftell(f->f) - f->f_start); + #endif +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } +} + + +#define SAMPLE_unknown 0xffffffff + +// seeking is implemented with a binary search, which narrows down the range to +// 64K, before using a linear search (because finding the synchronization +// pattern can be expensive, and the chance we'd find the end page again is +// relatively high for small ranges) +// +// two initial interpolation-style probes are used at the start of the search +// to try to bound either side of the binary search sensibly, while still +// working in O(log n) time if they fail. + +static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) +{ + uint8 header[27], lacing[255]; + int i,len; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + getn(f, header, 27); + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + getn(f, lacing, header[26]); + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; +} + +// rarely used function to seek back to the preceding page while finding the +// start of a packet +static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +{ + unsigned int previous_safe, end; + + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + + while (vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + set_file_offset(f, end); + } + + return 0; +} + +// implements the search logic for finding a page and starting decoding. if +// the function succeeds, current_loc_valid will be true and current_loc will +// be less than or equal to the provided sample number (the closer the +// better). +static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) +{ + ProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + uint32 delta, stream_length, padding, last_sample_limit; + double offset = 0.0, bytes_per_sample = 0.0; + int probe = 0; + + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) return error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); + + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + last_sample_limit = 0; + else + last_sample_limit = sample_number - padding; + + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + set_file_offset(f, left.page_end); + if (!get_seek_page_info(f, &left)) goto error; + } + + right = f->p_last; + assert(right.last_decoded_sample != ~0U); + + // starting from the start is handled differently + if (last_sample_limit <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) { + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + } + return 0; + } + + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) error = 8000; + if (error < 0 && error > -8000) error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + set_file_offset(f, (unsigned int) offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + set_file_offset(f, left.page_end + (delta / 2) - 32768); + } + + if (!vorbis_find_page(f, NULL, NULL)) goto error; + } + + for (;;) { + if (!get_seek_page_info(f, &mid)) goto error; + if (mid.last_decoded_sample != ~0U) break; + // (untested) no frames end on this page + set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough (if it wasn't an interpolation probe). + if (mid.page_start == right.page_start) { + if (probe >= 2 || delta <= 65536) + break; + } else { + if (last_sample_limit < mid.last_decoded_sample) + right = mid; + else + left = mid; + } + + ++probe; + } + + // seek back to start of the last packet + page_start = left.page_start; + set_file_offset(f, page_start); + if (!start_page(f)) return error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); + + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i-1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!go_to_page_before(f, page_start)) + goto error; + + page_start = stb_vorbis_get_file_offset(f); + if (!start_page(f)) goto error; + end_pos = f->segment_count - 1; + } + + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; + + for (i = 0; i < start_seg_with_known_loc; i++) + skip(f, f->segments[i]); + + // start decoding (optimizable - this frame is generally discarded) + if (!vorbis_pump_first_frame(f)) + return 0; + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + +error: + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return error(f, VORBIS_seek_failed); +} + +// the same as vorbis_decode_initial, but without advancing +static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + int bits_read, bytes_read; + + if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; + + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + ilog(f->mode_count-1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; + + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; + + return 1; +} + +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + uint32 max_frame_samples; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + // fast page-level search + if (!seek_to_sample_coarse(f, sample_number)) + return 0; + + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); + + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + maybe_start_packet(f); + flush_packet(f); + } + } + // the next frame should start with the sample + if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed); + return 1; +} + +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; + + if (sample_number != f->current_loc) { + int n; + uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } + + return 1; +} + +int stb_vorbis_seek_start(stb_vorbis *f) +{ + if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + return vorbis_pump_first_frame(f); +} + +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + unsigned int last; + uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + //previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging + last_page_loc = stb_vorbis_get_file_offset(f); + } + + set_file_offset(f, last_page_loc); + + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; +} + +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = (uint32) ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = (unsigned int) ftell(file); + fseek(file, 0, SEEK_END); + len = (unsigned int) (ftell(file) - start); + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) +{ + FILE *f; +#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) + if (0 != fopen_s(&f, filename, "rb")) + f = NULL; +#else + f = fopen(filename, "rb"); +#endif + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (!data) { + if (error) *error = VORBIS_unexpected_eof; + return NULL; + } + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + len; + p.stream_start = (uint8 *) p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + if (error) *error = VORBIS__no_error; + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 + +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) + +static int8 channel_position[7][6] = +{ + { 0 }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + int i; + } float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define FASTDEF(x) float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() +#else + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) +#endif + +static void copy_samples(short *dest, float *src, int len) +{ + int i; + check_endianness(); + for (i=0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += STB_BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } + #undef STB_BUFFER_SIZE +} + +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } + #undef STB_BUFFER_SIZE +} + +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output = NULL; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // NO_STDIO + +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // STB_VORBIS_NO_INTEGER_CONVERSION + +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} +#endif // STB_VORBIS_NO_PULLDATA_API + +/* Version history + 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223 + found with Mayhem by ForAllSecure + 1.16 - 2019-03-04 - fix warnings + 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found + 1.14 - 2018-02-11 - delete bogus dealloca usage + 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) + 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files + 1.11 - 2017-07-23 - fix MinGW compilation + 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory + 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version + 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; + avoid discarding last frame of audio data + 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API + some more crash fixes when out of memory or with corrupt files + 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) + some crash fixes when out of memory or with corrupt files + 1.05 - 2015-04-19 - don't define __forceinline if it's redundant + 1.04 - 2014-08-27 - fix missing const-correct case in API + 1.03 - 2014-08-07 - Warning fixes + 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float + 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + +#endif // STB_VORBIS_HEADER_ONLY + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/Frameworks/OpenMPT.old/OpenMPT/libopenmpt/.clang-format b/Frameworks/OpenMPT.old/OpenMPT/libopenmpt/.clang-format new file mode 100644 index 000000000..a50eb55a0 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/libopenmpt/.clang-format @@ -0,0 +1,45 @@ +Language: Cpp +AccessModifierOffset: -2 +AlignEscapedNewlines: DontAlign +AllowShortFunctionsOnASingleLine: Empty +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: true +BreakConstructorInitializersBeforeComma: true +BreakStringLiterals: false +ColumnLimit: 0 +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +IndentCaseLabels: true +IndentWidth: 2 +MaxEmptyLinesToKeep: 3 +PointerAlignment: Middle +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceBeforeParens: ControlStatements +SpacesBeforeTrailingComments: 2 +SpacesInContainerLiterals: false +SpacesInParentheses: true +SpacesInSquareBrackets: true +Standard: Cpp11 +TabWidth: 2 +UseTab: ForIndentation diff --git a/Frameworks/OpenMPT.old/OpenMPT/libopenmpt/Doxyfile b/Frameworks/OpenMPT.old/OpenMPT/libopenmpt/Doxyfile new file mode 100644 index 000000000..fcf535204 --- /dev/null +++ b/Frameworks/OpenMPT.old/OpenMPT/libopenmpt/Doxyfile @@ -0,0 +1,2469 @@ +# Doxyfile 1.8.13 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single 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. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "libopenmpt" + +# 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 = "unknown" + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "cross-platform C++ and C library to decode tracked music files" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. 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 = bin/docs + +# 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 causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = 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. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, 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. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, 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. +# The default value is: YES. + +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 and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +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. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, 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 +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# 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. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of 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. +# The default value is: NO. + +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-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +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 behavior. 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 behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +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. +# The default value is: NO. + +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. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that act 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 = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# 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. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +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. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# 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); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) 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. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES 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. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag 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. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# 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 respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# 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. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If 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, only methods in the interface are +# included. +# The default value is: NO. + +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. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO 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. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +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, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +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 then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +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. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES 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. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = 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 group names will +# appear in their defined order. +# The default value is: NO. + +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 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. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = 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. +# The default value is: YES. + +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. +# The default value is: YES. + +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. +# The default value is: YES. + +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. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have 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 value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +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. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag 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. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag 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. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = 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) +# The default value is: $file:$line: $text. + +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 standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is 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. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = libopenmpt/dox/index.dox \ + libopenmpt/dox/quickstart.md \ + README.md \ + libopenmpt/dox/dependencies.md \ + libopenmpt/dox/packaging.md \ + doc/contributing.md \ + doc/libopenmpt_styleguide.md \ + libopenmpt/dox/tests.md \ + libopenmpt/dox/changelog.md \ + libopenmpt/dox/todo.md \ + doc/module_formats.md \ + libopenmpt/libopenmpt.hpp \ + libopenmpt/libopenmpt.h \ + libopenmpt/libopenmpt_stream_callbacks_buffer.h \ + libopenmpt/libopenmpt_stream_callbacks_fd.h \ + libopenmpt/libopenmpt_stream_callbacks_file.h \ + libopenmpt/libopenmpt_config.h \ + libopenmpt/libopenmpt_version.h \ + libopenmpt/libopenmpt_ext.hpp \ + libopenmpt/libopenmpt_ext.h + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +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 patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# 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. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +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 +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = LIBOPENMPT_API \ + LIBOPENMPT_CXX_API \ + LIBOPENMPT_API_HELPER_EXPORT \ + LIBOPENMPT_API_HELPER_IMPORT \ + LIBOPENMPT_API_HELPER_PUBLIC \ + LIBOPENMPT_API_HELPER_LOCAL \ + OPENMPT_API_VERSION_HELPER_STRINGIZE \ + OPENMPT_API_VERSION_STRINGIZE \ + openmpt::detail + +# 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 = examples/ \ + LICENSE + +# 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. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +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 information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# 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 that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES 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 documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = 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. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES 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. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse-libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# 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. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +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 a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +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. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +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). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +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 left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# 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. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# 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 (see: http://developer.apple.com/tools/xcode/), 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. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset 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. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# 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. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# 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. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# 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. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# 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). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# 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. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to 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 +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# 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. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /