diff --git a/.gitignore b/.gitignore index c250cc5af2..af25317b42 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,20 @@ dat/ ehthumbs.db Icon Thumbs.db + +# Generated Bindings +/bindings/ + +# Compiled Python +*.pyc +*.pyd +__pycache__/ + +# CFFI generated source +_py*.c + +# Python Setuptools build and distribution folders. +/build/ +/dist/ +/.eggs +/*.egg-info diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000..408b377dc8 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,37 @@ +# Use the official gcc image +image: ubuntu:18.04 + +before_script: + # Install build dependencies + - apt -y update + - apt -y install build-essential + - apt -y install make + - apt -y install zlib1g-dev + - apt -y install libpng-dev + - apt -y install libjpeg62-dev + - apt -y install libgif-dev + - apt -y install libncurses5-dev + - apt -y install libfreetype6-dev + - apt -y install libx11-dev + - apt -y install libxrender-dev + - apt -y install libgl1-mesa-dev + - apt -y install libxext-dev + - apt -y install upx-ucl + - apt -y install libsqlite3-dev + - apt -y install libssl-dev + - apt -y install libffi-dev + - apt -y install libasound2-dev + - apt -y install libcurl4-openssl-dev + - apt -y install libfontconfig1-dev + +ecere-sdk: + # Build the application + stage: build + script: + - make troubleshoot + - make print-all-vars-stat + - make + artifacts: + paths: + - obj/ + expire_in: 1 week diff --git a/.travis.yml b/.travis.yml index 1ca2cd0a24..1b004aa010 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,14 +10,17 @@ compiler: os: - linux - - osx +# - osx # Travis builds are failing for no obvious reason at the moment + - windows matrix: exclude: - os: osx compiler: gcc + - os: windows + compiler: clang allow_failures: - - os: osx + - os: addons: apt: @@ -38,11 +41,31 @@ addons: - libssl-dev - libffi-dev - libasound2-dev + - libcurl4-openssl-dev before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install openssl ; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew link openssl --force ; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco install openssl; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco upgrade openssl; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then export PATH="C:\Program Files\OpenSSL-Win64\bin;":$PATH; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then wget http://ecere.com/tmp/missingDLLs.7z; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then "C:/Program Files/7-zip/7z" x missingDLLs.7z ; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then cp opengl32.dll C:/windows/system32/ ; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then cp glu32.dll C:/windows/system32/ ; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then cp ddraw.dll C:/windows/system32/ ; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then cp dsound.dll C:/windows/system32/ ; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then cp gnurx-0.dll C:/windows/system32/ ; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then cp gnurx-0.dll "C:/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/x86_64-w64-mingw32/lib/" ; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then wget http://www.ecere.com/wordpress/wp-content/uploads/2008/03/regex.h; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then cp regex.h "C:/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/" ; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then wget http://www.ecere.com/tmp/avx512dqintrin.h; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then wget http://www.ecere.com/tmp/avx512fintrin.h; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then cp avx512dqintrin.h "C:/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/" ; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then cp avx512fintrin.h "C:/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/" ; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then mingw32-make MSYSCON=defined OPENSSL_CONF="C:\Program Files\OpenSSL-Win64\bin\openssl.cfg" -j1 V=1 ENABLE_SSL=y troubleshoot; else make -j1 V=1 ENABLE_SSL=y troubleshoot; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then mingw32-make MSYSCON=defined OPENSSL_CONF="C:\Program Files\OpenSSL-Win64\bin\openssl.cfg" -j1 V=1 ENABLE_SSL=y print-all-vars-stat; else make -j1 V=1 ENABLE_SSL=y print-all-vars-stat; fi script: - - make -j1 V=1 ENABLE_SSL=y + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then mingw32-make MSYSCON=defined OPENSSL_CONF="C:\Program Files\OpenSSL-Win64\bin\openssl.cfg" -j1 V=1 ENABLE_SSL=y; else make -j1 V=1 ENABLE_SSL=y; fi diff --git a/Cleanfile b/Cleanfile index ef8f885a80..f31008e0e8 100644 --- a/Cleanfile +++ b/Cleanfile @@ -1,4 +1,4 @@ -.PHONY: distclean distclean_all_subdirs +.PHONY: distclean distclean_all_subdirs distclean_all_subdirs_internal ifneq ($(V),1) .SILENT: endif @@ -21,26 +21,43 @@ ifeq ($(wildcard $(_SDK_SRC_ROOT)Cleanfile),) _SDK_SRC_ROOT = $(find_sdk_src_root) endif -$(_SDK_SRC_ROOT)Cleanfile: ; -$(_SDK_SRC_ROOT)crossplatform.mk: ; - include $(_SDK_SRC_ROOT)crossplatform.mk -subdirs := $(sort $(filter-out obj,$(hs_ls_dir))) +subdirs := $(sort $(filter-out .configs obj __pycache__,$(hs_ls_dir))) -cd_make_distclean_all_subdirs = $(cd) $(call fp_opt_quotes,$(1)) && $(_MAKE) -f $(_SDK_SRC_ROOT)../Cleanfile _SDK_SRC_ROOT=$(_SDK_SRC_ROOT)../ $(if $(BUILD_DIR),BUILD_DIR=$(BUILD_DIR)$(1)/ ,) distclean distclean_all_subdirs && cd .. +cd_make_distclean_all_subdirs = $(cd) $(call fp_opt_quotes,$(1)) && $(_MAKE) -f $(_SDK_SRC_ROOT)../Cleanfile _SDK_SRC_ROOT=$(_SDK_SRC_ROOT)../ $(if $(BUILD_DIR),BUILD_DIR=$(BUILD_DIR)$(1)/ ,) distclean distclean_all_subdirs_internal && cd .. distclean_all_subdirs: + $(call echo,This may take a while...) +ifdef _SDK_SRC_ROOT + $(call hs_crossloop,$(subdirs),cd_make_distclean_all_subdirs) +endif + $(call echo,Done.) + +distclean_all_subdirs_internal: ifdef _SDK_SRC_ROOT $(call hs_crossloop,$(subdirs),cd_make_distclean_all_subdirs) endif distclean: ifdef _SDK_SRC_ROOT - $(call rmr,obj/) - $(call rmr,.configs/) - ifndef KEEP_EWS_FILES - $(call rm,*.ews) + ifndef KEEP_CONFIGS + $(if $(wildcard .configs/),$(call rmr,.configs/),) endif + ifndef KEEP_MAKEFILES $(call rm,*.Makefile) + endif + ifndef KEEP_OBJS + $(if $(wildcard obj/),$(call rmr,obj/),) + endif + ifndef KEEP_PY + $(call rm,_py*.c) + $(call rm,_py*.pyd) + $(if $(wildcard __pycache__/),$(call rmr,__pycache__/),) + endif + ifndef KEEP_WORKSPACES + $(call rm,*.ews) + endif endif + +$(MAKEFILE_LIST): ; diff --git a/EcereSDK/__init__.py b/EcereSDK/__init__.py new file mode 100644 index 0000000000..be8dbfb798 --- /dev/null +++ b/EcereSDK/__init__.py @@ -0,0 +1,10 @@ +"""attempt at a monolithic ecere package""" + +from EcereSDK.eC import * +from EcereSDK.ecere import * +from EcereSDK.EDA import * + +__all__ = [ 'eC', 'ecere', 'EDA' ] + +# wondering if this could be here? +# app = GuiApplication(appGlobals=globals()) diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000000..95cdca8143 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,27 @@ +exclude .appveyor.yml +exclude .gitattributes +exclude .gitignore +exclude .mailmap +exclude .travis.yml +recursive-exclude autoLayout * +recursive-exclude butterbur * +recursive-exclude compiler Makefile.bootstrap +#recursive-exclude doc * +recursive-exclude ecere Makefile.bootstrap +recursive-exclude EcereSDK * +#recursive-exclude epj2make * +#recursive-exclude ide * +#recursive-exclude documentor * +recursive-exclude installer * +recursive-exclude obj * +recursive-exclude samples * +recursive-exclude share * +#include bindings/py/build_eC.py +#include bindings/py/build_ecere.py +#include bindings/py/build_EDA.py +#include bindings/py/cffi-eC.h +#include bindings/py/cffi-ecere.h +#include bindings/py/cffi-EDA.h +#include bindings/py/eC.h +#include bindings/py/ecere.h +#include bindings/py/EDA.h diff --git a/Makefile b/Makefile index db30e6f842..46823a3dc5 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,17 @@ -.PHONY: all clean realclean distclean emptyoutput prepinstall actualinstall install copyonlyinstall uninstall troubleshoot outputdirs bootstrap deps ecere ecerecom ecerevanilla ear compiler prepbinaries epj2make ide documentor eda prepcodeguard codeguard fixprecompile cleantarget pots installer regenbootstrap updatebootstrap update_ecere update_libec update_ecp update_ecc update_ecs ecereaudio ifneq ($(V),1) .SILENT: endif -_CF_DIR = +.PHONY: all clean realclean wipeclean distclean emptyoutput prepinstall actualinstall install copyonlyinstall uninstall troubleshoot outputdirs bootstrap deps ecere ecerecom ecerevanilla ear compiler prepbinaries epj2make libec2 bgen ide documentor eda prepcodeguard codeguard fixprecompile cleantarget pots installer regenbootstrap updatebootstrap update_ecere update_libec update_ecp update_ecc update_ecs ecereaudio + +ROOT_ABSPATH := $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) +_CF_DIR = $(ROOT_ABSPATH) include crossplatform.mk include default.cf +.NOTPARALLEL: $(NOT_PARALLEL_TARGETS) + ifdef BSD_HOST INSTALL_FLAGS := CPFLAGS := -pRf @@ -34,6 +38,13 @@ endif endif endif +ifndef DISABLE_EDA_SQLITE + EDASQLite := defined +endif +ifndef DISABLE_EDA_dBASE + EDAdBASE := defined +endif + ifdef WINDOWS_HOST HOST_SOV := $(HOST_SO) else @@ -46,6 +57,8 @@ SOV := $(SO) ifndef DESTDIR +ifndef ECERE_SDK_INSTALL_DIR + ifeq ($(TARGET_ARCH),x86_64) ifneq ($(wildcard $(SystemDrive)/Program\ Files ),) export DESTDIR=$(SystemDrive)/Program Files/Ecere SDK @@ -64,6 +77,10 @@ else endif endif +else + export DESTDIR=$(ECERE_SDK_INSTALL_DIR) +endif # ECERE_SDK_INSTALL_DIR + endif # DESTDIR export prefix= @@ -169,17 +186,23 @@ XOBJDIR := obj$(OBJALT)/ XOBJBINDIR := $(OBJDIR)$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/bin/ XOBJLIBDIR := $(OBJDIR)$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/lib/ -all: prepbinaries ide epj2make documentor eda codeguard ecereaudio +all: prepbinaries bgen +ifndef ECERE_PYTHON_PACKAGE +all: epj2make ide documentor +endif +all: eda codeguard ecereaudio @$(call echo,The Ecere SDK is fully built.) +include Makefile.bindings + outputdirs: - $(if $(wildcard $(OBJDIR)),,$(call mkdir,$(OBJDIR))) - $(if $(wildcard $(OBJBINDIR)),,$(call mkdir,$(OBJBINDIR))) - $(if $(wildcard $(OBJLIBDIR)),,$(call mkdir,$(OBJLIBDIR))) + $(call mkdir,$(OBJDIR)) + $(call mkdir,$(OBJBINDIR)) + $(call mkdir,$(OBJLIBDIR)) ifdef CROSS_TARGET - $(if $(wildcard $(XOBJDIR)),,$(call mkdir,$(XOBJDIR))) - $(if $(wildcard $(XOBJBINDIR)),,$(call mkdir,$(XOBJBINDIR))) - $(if $(wildcard $(XOBJLIBDIR)),,$(call mkdir,$(XOBJLIBDIR))) + $(call mkdir,$(XOBJDIR)) + $(call mkdir,$(XOBJBINDIR)) + $(call mkdir,$(XOBJLIBDIR)) endif bootstrap: outputdirs @@ -243,35 +266,40 @@ endif @$(call echo,Building 2nd stage compiler) +cd compiler && $(_MAKE) -prepbinaries: compiler ecerecom +prepbinaries: compiler libec2 ecerecom @$(call echo,Enabling 2nd stage binaries...) ifdef WINDOWS_TARGET $(call cp,ecere/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecere$(SOV),$(OBJBINDIR)) $(call cp,ecere/obj/ecereCOM.release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecereCOM$(SOV),$(OBJBINDIR)) $(call cp,compiler/libec/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec$(SOV),$(OBJBINDIR)) + $(call cp,compiler/libec2/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec2$(SOV),$(OBJBINDIR)) endif ifdef LINUX_TARGET $(call cp,ecere/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecere$(SOV),$(OBJLIBDIR)) $(call cp,ecere/obj/ecereCOM.release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecereCOM$(SOV),$(OBJLIBDIR)) $(call cp,compiler/libec/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec$(SOV),$(OBJLIBDIR)) + $(call cp,compiler/libec2/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec2$(SOV),$(OBJLIBDIR)) ln -sf $(LP)ecere$(SOV) $(OBJLIBDIR)$(LP)ecere$(SO).0 ln -sf $(LP)ecereCOM$(SOV) $(OBJLIBDIR)$(LP)ecereCOM$(SO).0 ln -sf $(LP)ec$(SOV) $(OBJLIBDIR)$(LP)ec$(SO).0 + ln -sf $(LP)ec2$(SOV) $(OBJLIBDIR)$(LP)ec2$(SO).0 ln -sf $(LP)ecere$(SOV) $(OBJLIBDIR)$(LP)ecere$(SO) ln -sf $(LP)ecereCOM$(SOV) $(OBJLIBDIR)$(LP)ecereCOM$(SO) ln -sf $(LP)ec$(SOV) $(OBJLIBDIR)$(LP)ec$(SO) + ln -sf $(LP)ec2$(SOV) $(OBJLIBDIR)$(LP)ec2$(SO) endif ifndef WINDOWS_TARGET ifndef LINUX_TARGET $(call cp,ecere/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecere$(SO),$(OBJLIBDIR)) $(call cp,ecere/obj/ecereCOM.release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecereCOM$(SO),$(OBJLIBDIR)) $(call cp,compiler/libec/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec$(SO),$(OBJLIBDIR)) + $(call cp,compiler/libec2/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec2$(SO),$(OBJLIBDIR)) endif endif - $(call cp,ear/cmd/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ear$(E),$(OBJBINDIR)) - $(call cp,compiler/ecc/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecc$(E),$(OBJBINDIR)) - $(call cp,compiler/ecp/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecp$(E),$(OBJBINDIR)) - $(call cp,compiler/ecs/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecs$(E),$(OBJBINDIR)) + $(call cp,ear/cmd/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ear$(B32_SFX)$(E),$(OBJBINDIR)) + $(call cp,compiler/ecc/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecc$(B32_SFX)$(E),$(OBJBINDIR)) + $(call cp,compiler/ecp/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecp$(B32_SFX)$(E),$(OBJBINDIR)) + $(call cp,compiler/ecs/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecs$(B32_SFX)$(E),$(OBJBINDIR)) ifdef CROSS_TARGET @@ -279,23 +307,28 @@ ifdef WINDOWS_HOST $(call cp,ecere/obj/release.$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(HOST_LP)ecere$(HOST_SOV),$(XOBJBINDIR)) $(call cp,ecere/obj/ecereCOM.release.$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(HOST_LP)ecereCOM$(HOST_SOV),$(XOBJBINDIR)) $(call cp,compiler/libec/obj/release.$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(HOST_LP)ec$(HOST_SOV),$(XOBJBINDIR)) + $(call cp,compiler/libec2/obj/release.$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(HOST_LP)ec2$(HOST_SOV),$(XOBJBINDIR)) endif ifdef WINDOWS_HOST $(call cp,ecere/obj/release.$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(HOST_LP)ecere$(HOST_SOV),$(XOBJLIBDIR)) $(call cp,ecere/obj/ecereCOM.release.$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(HOST_LP)ecereCOM$(HOST_SOV),$(XOBJLIBDIR)) $(call cp,compiler/libec/obj/release.$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(HOST_LP)ec$(HOST_SOV),$(XOBJLIBDIR)) + $(call cp,compiler/libec2/obj/release.$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(HOST_LP)ec2$(HOST_SOV),$(XOBJLIBDIR)) ln -sf $(HOST_LP)ecere$(HOST_SOV) $(XOBJLIBDIR)$(LP)ecere$(HOST_SO).0 ln -sf $(HOST_LP)ecereCOM$(HOST_SOV) $(XOBJLIBDIR)$(LP)ecereCOM$(HOST_SO).0 ln -sf $(HOST_LP)ec$(HOST_SOV) $(XOBJLIBDIR)$(LP)ec$(HOST_SO).0 + ln -sf $(HOST_LP)ec2$(HOST_SOV) $(XOBJLIBDIR)$(LP)ec2$(HOST_SO).0 ln -sf $(HOST_LP)ecere$(HOST_SOV) $(XOBJLIBDIR)$(LP)ecere$(HOST_SO) ln -sf $(HOST_LP)ecereCOM$(HOST_SOV) $(XOBJLIBDIR)$(LP)ecereCOM$(HOST_SO) ln -sf $(HOST_LP)ec$(HOST_SOV) $(XOBJLIBDIR)$(LP)ec$(HOST_SO) + ln -sf $(HOST_LP)ec2$(HOST_SOV) $(XOBJLIBDIR)$(LP)ec2$(HOST_SO) endif ifndef WINDOWS_HOST ifndef LINUX_HOST $(call cp,ecere/obj/release.$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(HOST_LP)ecere$(HOST_SO),$(XOBJLIBDIR)) $(call cp,ecere/obj/ecereCOM.release.$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(HOST_LP)ecereCOM$(HOST_SO),$(XOBJLIBDIR)) $(call cp,compiler/libec/obj/release.$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(HOST_LP)ec$(HOST_SO),$(XOBJLIBDIR)) + $(call cp,compiler/libec2/obj/release.$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(HOST_LP)ec2$(HOST_SO),$(XOBJLIBDIR)) endif endif $(call cp,ear/cmd/obj/release.$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ear$(HOST_E),$(XOBJBINDIR)) @@ -309,6 +342,14 @@ epj2make: prepbinaries @$(call echo,Building epj2make...) +cd epj2make && $(_MAKE) +libec2: compiler + @$(call echo,Building libec2...) + +cd compiler/libec2 && $(_MAKE) + +bgen: prepbinaries + @$(call echo,Building bgen...) + +cd bgen && $(_MAKE) + ecereaudio: prepbinaries ifneq ($(ECERE_AUDIO),n) @$(call echo,Building EcereAudio...) @@ -362,7 +403,12 @@ emptyoutput: outputdirs $(call rm,$(SODESTDIR)$(LP)ecereCOM$(SO)) $(call rm,$(SODESTDIR)$(LP)ec$(SO)) $(call rm,$(SODESTDIR)$(LP)EDA$(SO)) +ifdef EDAdBASE + $(call rm,$(SODESTDIR)$(LP)EDAdBASE$(SO)) +endif +ifdef EDASQLite $(call rm,$(SODESTDIR)$(LP)EDASQLite$(SO)) +endif ifdef EDASQLiteCipher $(call rm,$(SODESTDIR)$(LP)EDASQLiteCipher$(SO)) endif @@ -374,7 +420,12 @@ ifdef LINUX_TARGET $(call rm,$(SODESTDIR)$(LP)ecereCOM$(SO).0) $(call rm,$(SODESTDIR)$(LP)ec$(SO).0) $(call rm,$(SODESTDIR)$(LP)EDA$(SO).0) +ifdef EDAdBASE + $(call rm,$(SODESTDIR)$(LP)EDAdBASE$(SO).0) +endif +ifdef EDASQLite $(call rm,$(SODESTDIR)$(LP)EDASQLite$(SO).0) +endif ifdef EDASQLiteCipher $(call rm,$(SODESTDIR)$(LP)EDASQLiteCipher$(SO).0) endif @@ -384,8 +435,14 @@ endif $(call rm,$(SODESTDIR)$(LP)ecere$(SOV)) $(call rm,$(SODESTDIR)$(LP)ecereCOM$(SOV)) $(call rm,$(SODESTDIR)$(LP)ec$(SOV)) + $(call rm,$(SODESTDIR)$(LP)ec2$(SOV)) $(call rm,$(SODESTDIR)$(LP)EDA$(SOV)) +ifdef EDAdBASE + $(call rm,$(SODESTDIR)$(LP)EDAdBASE$(SOV)) +endif +ifdef EDASQLite $(call rm,$(SODESTDIR)$(LP)EDASQLite$(SOV)) +endif ifdef EDASQLiteCipher $(call rm,$(SODESTDIR)$(LP)EDASQLiteCipher$(SOV)) endif @@ -393,11 +450,12 @@ ifneq ($(ECERE_AUDIO),n) $(call rm,$(SODESTDIR)$(LP)EcereAudio$(SOV)) endif endif - $(call rm,$(OBJBINDIR)ear$(E)) - $(call rm,$(OBJBINDIR)ecc$(E)) - $(call rm,$(OBJBINDIR)ecp$(E)) - $(call rm,$(OBJBINDIR)ecs$(E)) + $(call rm,$(OBJBINDIR)ear$(B32_SFX)$(E)) + $(call rm,$(OBJBINDIR)ecc$(B32_SFX)$(E)) + $(call rm,$(OBJBINDIR)ecp$(B32_SFX)$(E)) + $(call rm,$(OBJBINDIR)ecs$(B32_SFX)$(E)) $(call rm,$(OBJBINDIR)epj2make$(E)) + $(call rm,$(OBJBINDIR)bgen$(E)) $(call rm,$(OBJBINDIR)ecere-ide$(E)) $(call rm,$(OBJBINDIR)documentor$(E)) ifdef CodeGuard @@ -411,6 +469,8 @@ cleantarget: +cd ecere && $(_MAKE) cleantarget +cd eda && $(_MAKE) cleantarget +cd epj2make && $(_MAKE) cleantarget + +cd compiler/libec2 && $(_MAKE) cleantarget + +cd bgen && $(_MAKE) cleantarget +cd ide && $(_MAKE) cleantarget +cd installer && $(_MAKE) cleantarget ifneq ($(ECERE_AUDIO),n) @@ -432,7 +492,7 @@ installer: @$(call echo,The Ecere SDK Windows Installer is fully built.) endif -clean: emptyoutput +clean: emptyoutput bindings_clean ifndef LINUX_TARGET +cd deps && $(_MAKE) clean endif @@ -440,6 +500,8 @@ endif +cd compiler && $(_MAKE) clean +cd ear && $(_MAKE) clean +cd epj2make && $(_MAKE) clean + +cd compiler/libec2 && $(_MAKE) clean + +cd bgen && $(_MAKE) clean +cd ide && $(_MAKE) clean +cd documentor && $(_MAKE) clean ifneq ($(ECERE_AUDIO),n) @@ -451,7 +513,7 @@ endif +cd eda && $(_MAKE) clean @$(call echo,Done.) -realclean: outputdirs +realclean: bindings_realclean ifndef LINUX_TARGET +cd deps && $(_MAKE) realclean endif @@ -459,6 +521,8 @@ endif +cd compiler && $(_MAKE) realclean +cd ear && $(_MAKE) realclean +cd epj2make && $(_MAKE) realclean + +cd compiler/libec2 && $(_MAKE) realclean + +cd bgen && $(_MAKE) realclean +cd ide && $(_MAKE) realclean +cd documentor && $(_MAKE) realclean ifneq ($(ECERE_AUDIO),n) @@ -471,30 +535,58 @@ endif $(call rmr,obj/$(PLATFORM)/) @$(call echo,Done.) -distclean: - $(MAKE) -f Cleanfile distclean distclean_all_subdirs +wipeclean: bindings_wipeclean + $(call rmr,obj/) + +cd deps && $(_MAKE) wipeclean + +cd ecere && $(_MAKE) wipeclean + +cd compiler && $(_MAKE) wipeclean + +cd ear && $(_MAKE) wipeclean + +cd epj2make && $(_MAKE) wipeclean + +cd compiler/libec2 && $(_MAKE) wipeclean + +cd bgen && $(_MAKE) wipeclean + +cd ide && $(_MAKE) wipeclean + +cd documentor && $(_MAKE) wipeclean + +cd audio && $(_MAKE) wipeclean + +cd eda && $(_MAKE) wipeclean + @$(call echo,Done.) + +distclean: bindings_distclean + $(_MAKE) -f Cleanfile distclean distclean_all_subdirs @$(call echo,Done.) DOC = doc -Makefile: ; -crossplatform.mk: ; -default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; BINARIES = \ ecere/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecere$(SOV) \ ecere/obj/ecereCOM.release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecereCOM$(SOV) \ ecere/obj/vanilla.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/libecereVanilla$(A) \ compiler/libec/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec$(SOV) \ - compiler/ecp/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecp$(E) \ - compiler/ecc/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecc$(E) \ - compiler/ecs/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecs$(E) \ - ear/cmd/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ear$(E) \ + compiler/libec2/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec2$(SOV) \ + compiler/ecp/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecp$(B32_SFX)$(E) \ + compiler/ecc/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecc$(B32_SFX)$(E) \ + compiler/ecs/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecs$(B32_SFX)$(E) \ + ear/cmd/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ear$(B32_SFX)$(E) \ + bgen/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/bgen$(E) \ + eda/libeda/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDA$(SOV) + +ifdef EDAdBASE +BINARIES += eda/drivers/dbase/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDAdBASE$(SOV) +endif +ifdef EDASQLite +BINARIES += \ + eda/drivers/sqlite/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDASQLite$(SOV) +endif + +ifndef ECERE_PYTHON_PACKAGE +BINARIES += \ epj2make/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/epj2make$(E) \ documentor/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/documentor$(E) \ - ide/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecere-ide$(E) \ - eda/libeda/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDA$(SOV) \ - eda/drivers/sqlite/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDASQLite$(SOV) + ide/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecere-ide$(E) +endif ifneq ($(ECERE_AUDIO),n) BINARIES += audio/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EcereAudio$(SOV) @@ -508,6 +600,14 @@ ifdef EDASQLiteCipher BINARIES += eda/drivers/sqliteCipher/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDASQLiteCipher$(SOV) endif +OBJPYDIR := $(OBJDIR)$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/py + +.PHONY: py_prepinstall +py_prepinstall: prepinstall + $(call mkdir,$(call path,$(OBJPYDIR)/)) + $(call cp,ecere/obj/ecereCOM.release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecereCOM$(SOV),$(OBJPYDIR)/$(LP)ecereCOM$(SO)) + $(call cp,ecere/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecere$(SOV),$(OBJPYDIR)/$(LP)ecere$(SO)) + # Making sure everything is in $(OBJBINDIR) and $(OBJLIBDIR) # Shared Libraries (in $(OBJBINDIR) on Windows and $(OBJLIBDIR) otherwise) # Symlinks for libs on Linux @@ -518,8 +618,14 @@ ifdef WINDOWS_TARGET $(call cp,ecere/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecere$(SO),$(OBJBINDIR)) $(call cp,ecere/obj/ecereCOM.release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecereCOM$(SO),$(OBJBINDIR)) $(call cp,compiler/libec/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec$(SO),$(OBJBINDIR)) + $(call cp,compiler/libec2/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec2$(SO),$(OBJBINDIR)) $(call cp,eda/libeda/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDA$(SO),$(OBJBINDIR)) +ifdef EDAdBASE + $(call cp,eda/drivers/dbase/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDAdBASE$(SO),$(OBJBINDIR)) +endif +ifdef EDASQLite $(call cp,eda/drivers/sqlite/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDASQLite$(SO),$(OBJBINDIR)) +endif ifneq ($(ECERE_AUDIO),n) $(call cp,audio/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EcereAudio$(SO),$(OBJBINDIR)) endif @@ -532,8 +638,14 @@ ifdef LINUX_TARGET $(call cp,ecere/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecere$(SOV),$(OBJLIBDIR)) $(call cp,ecere/obj/ecereCOM.release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecereCOM$(SOV),$(OBJLIBDIR)) $(call cp,compiler/libec/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec$(SOV),$(OBJLIBDIR)) + $(call cp,compiler/libec2/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec2$(SOV),$(OBJLIBDIR)) $(call cp,eda/libeda/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDA$(SOV),$(OBJLIBDIR)) +ifdef EDAdBASE + $(call cp,eda/drivers/dbase/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDAdBASE$(SOV),$(OBJLIBDIR)) +endif +ifdef EDASQLite $(call cp,eda/drivers/sqlite/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDASQLite$(SOV),$(OBJLIBDIR)) +endif ifneq ($(ECERE_AUDIO),n) $(call cp,audio/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EcereAudio$(SOV),$(OBJLIBDIR)) endif @@ -543,8 +655,14 @@ endif ln -sf $(LP)ecere$(SOV) $(OBJLIBDIR)$(LP)ecere$(SO).0 ln -sf $(LP)ecereCOM$(SOV) $(OBJLIBDIR)$(LP)ecereCOM$(SO).0 ln -sf $(LP)ec$(SOV) $(OBJLIBDIR)$(LP)ec$(SO).0 + ln -sf $(LP)ec2$(SOV) $(OBJLIBDIR)$(LP)ec2$(SO).0 ln -sf $(LP)EDA$(SOV) $(OBJLIBDIR)$(LP)EDA$(SO).0 +ifdef EDAdBASE + ln -sf $(LP)EDAdBASE$(SOV) $(OBJLIBDIR)$(LP)EDAdBASE$(SO).0 +endif +ifdef EDASQLite ln -sf $(LP)EDASQLite$(SOV) $(OBJLIBDIR)$(LP)EDASQLite$(SO).0 +endif ifdef EDASQLiteCipher ln -sf $(LP)EDASQLiteCipher$(SOV) $(OBJLIBDIR)$(LP)EDASQLiteCipher$(SO).0 endif @@ -555,8 +673,14 @@ endif ln -sf $(LP)ecere$(SOV) $(OBJLIBDIR)$(LP)ecere$(SO) ln -sf $(LP)ecereCOM$(SOV) $(OBJLIBDIR)$(LP)ecereCOM$(SO) ln -sf $(LP)ec$(SOV) $(OBJLIBDIR)$(LP)ec$(SO) + ln -sf $(LP)ec2$(SOV) $(OBJLIBDIR)$(LP)ec2$(SO) ln -sf $(LP)EDA$(SOV) $(OBJLIBDIR)$(LP)EDA$(SO) +ifdef EDAdBASE + ln -sf $(LP)EDAdBASE$(SOV) $(OBJLIBDIR)$(LP)EDAdBASE$(SO) +endif +ifdef EDASQLite ln -sf $(LP)EDASQLite$(SOV) $(OBJLIBDIR)$(LP)EDASQLite$(SO) +endif ifdef EDASQLiteCipher ln -sf $(LP)EDASQLiteCipher$(SOV) $(OBJLIBDIR)$(LP)EDASQLiteCipher$(SO) endif @@ -568,8 +692,14 @@ ifndef LINUX_TARGET $(call cp,ecere/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecere$(SO),$(OBJLIBDIR)) $(call cp,ecere/obj/ecereCOM.release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ecereCOM$(SO),$(OBJLIBDIR)) $(call cp,compiler/libec/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec$(SO),$(OBJLIBDIR)) + $(call cp,compiler/libec2/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)ec2$(SO),$(OBJLIBDIR)) $(call cp,eda/libeda/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDA$(SO),$(OBJLIBDIR)) +ifdef EDAdBASE + $(call cp,eda/drivers/dbase/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDAdBASE$(SO),$(OBJLIBDIR)) +endif +ifdef EDASQLite $(call cp,eda/drivers/sqlite/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDASQLite$(SO),$(OBJLIBDIR)) +endif ifneq ($(ECERE_AUDIO),n) $(call cp,audio/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EcereAudio$(SO),$(OBJLIBDIR)) endif @@ -579,13 +709,16 @@ endif endif endif + $(call cp,ear/cmd/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ear$(B32_SFX)$(E),$(OBJBINDIR)) + $(call cp,compiler/ecc/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecc$(B32_SFX)$(E),$(OBJBINDIR)) + $(call cp,compiler/ecp/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecp$(B32_SFX)$(E),$(OBJBINDIR)) + $(call cp,compiler/ecs/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecs$(B32_SFX)$(E),$(OBJBINDIR)) + $(call cp,bgen/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/bgen$(E),$(OBJBINDIR)) +ifndef ECERE_PYTHON_PACKAGE $(call cp,ide/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecere-ide$(E),$(OBJBINDIR)) - $(call cp,ear/cmd/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ear$(E),$(OBJBINDIR)) - $(call cp,compiler/ecc/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecc$(E),$(OBJBINDIR)) - $(call cp,compiler/ecp/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecp$(E),$(OBJBINDIR)) - $(call cp,compiler/ecs/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ecs$(E),$(OBJBINDIR)) $(call cp,epj2make/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/epj2make$(E),$(OBJBINDIR)) $(call cp,documentor/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/documentor$(E),$(OBJBINDIR)) +endif ifdef CodeGuard $(call cp,codeGuard/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/CodeGuard$(E),$(OBJBINDIR)) endif @@ -600,14 +733,20 @@ copyonlyinstall: actualinstall actualinstall: ifdef WINDOWS_TARGET - $(call mkdir,"$(BINDIR)/") - $(call mkdir,"$(DESTSLIBDIR)/") - $(call mkdir,"$(DOCDIR)/") + $(call mkdir,$(call path,$(BINDIR)/)) + $(call mkdir,$(call path,$(DESTSLIBDIR)/)) + $(call mkdir,$(call path,$(DOCDIR)/)) $(call cp,$(OBJBINDIR)$(LP)ecere$(SO),"$(DESTLIBDIR)/") $(call cp,$(OBJBINDIR)$(LP)ecereCOM$(SO),"$(DESTLIBDIR)/") $(call cp,$(OBJBINDIR)$(LP)ec$(SO),"$(DESTLIBDIR)/") + $(call cp,$(OBJBINDIR)$(LP)ec2$(SO),"$(DESTLIBDIR)/") $(call cp,$(OBJBINDIR)$(LP)EDA$(SO),"$(DESTLIBDIR)/") +ifdef EDAdBASE + $(call cp,$(OBJBINDIR)$(LP)EDAdBASE$(SO),"$(DESTLIBDIR)/") +endif +ifdef EDASQLite $(call cp,$(OBJBINDIR)$(LP)EDASQLite$(SO),"$(DESTLIBDIR)/") +endif ifdef EDASQLiteCipher $(call cp,$(OBJBINDIR)$(LP)EDASQLiteCipher$(SO),"$(DESTLIBDIR)/") endif @@ -615,11 +754,12 @@ ifneq ($(ECERE_AUDIO),n) $(call cp,$(OBJBINDIR)$(LP)EcereAudio$(SO),"$(DESTLIBDIR)/") endif $(call cp,$(OBJBINDIR)ecere-ide$(E),"$(BINDIR)/") - $(call cp,$(OBJBINDIR)ear$(E),"$(BINDIR)/") - $(call cp,$(OBJBINDIR)ecc$(E),"$(BINDIR)/") - $(call cp,$(OBJBINDIR)ecp$(E),"$(BINDIR)/") - $(call cp,$(OBJBINDIR)ecs$(E),"$(BINDIR)/") + $(call cp,$(OBJBINDIR)ear$(B32_SFX)$(E),"$(BINDIR)/") + $(call cp,$(OBJBINDIR)ecc$(B32_SFX)$(E),"$(BINDIR)/") + $(call cp,$(OBJBINDIR)ecp$(B32_SFX)$(E),"$(BINDIR)/") + $(call cp,$(OBJBINDIR)ecs$(B32_SFX)$(E),"$(BINDIR)/") $(call cp,$(OBJBINDIR)epj2make$(E),"$(BINDIR)/") + $(call cp,$(OBJBINDIR)bgen$(E),"$(BINDIR)/") $(call cp,$(OBJBINDIR)documentor$(E),"$(BINDIR)/") ifdef CodeGuard $(call cp,$(OBJBINDIR)CodeGuard$(E),"$(BINDIR)/") @@ -635,8 +775,14 @@ ifdef OSX_TARGET install $(OBJLIBDIR)$(LP)ecere$(SO) $(DESTLIBDIR)/ install $(OBJLIBDIR)$(LP)ecereCOM$(SO) $(DESTLIBDIR)/ install $(OBJLIBDIR)$(LP)ec$(SO) $(DESTLIBDIR)/ + install $(OBJLIBDIR)$(LP)ec2$(SO) $(DESTLIBDIR)/ install $(OBJLIBDIR)$(LP)EDA$(SO) $(DESTLIBDIR)/ +ifdef EDAdBASE + install $(OBJLIBDIR)$(LP)EDAdBASE$(SO) $(DESTLIBDIR)/ +endif +ifdef EDASQLite install $(OBJLIBDIR)$(LP)EDASQLite$(SO) $(DESTLIBDIR)/ +endif ifdef EDASQLiteCipher install $(OBJLIBDIR)$(LP)EDASQLiteCipher$(SO) $(DESTLIBDIR)/ endif @@ -644,11 +790,12 @@ ifneq ($(ECERE_AUDIO),n) install $(OBJLIBDIR)$(LP)EcereAudio$(SO) $(DESTLIBDIR)/ endif install $(OBJBINDIR)ecere-ide$(E) $(BINDIR)/ - install $(OBJBINDIR)ear$(E) $(BINDIR)/ - install $(OBJBINDIR)ecc$(E) $(BINDIR)/ - install $(OBJBINDIR)ecp$(E) $(BINDIR)/ - install $(OBJBINDIR)ecs$(E) $(BINDIR)/ + install $(OBJBINDIR)ear$(B32_SFX)$(E) $(BINDIR)/ + install $(OBJBINDIR)ecc$(B32_SFX)$(E) $(BINDIR)/ + install $(OBJBINDIR)ecp$(B32_SFX)$(E) $(BINDIR)/ + install $(OBJBINDIR)ecs$(B32_SFX)$(E) $(BINDIR)/ install $(OBJBINDIR)epj2make$(E) $(BINDIR)/ + install $(OBJBINDIR)bgen$(E) $(BINDIR)/ install $(OBJBINDIR)documentor$(E) $(BINDIR)/ ifdef CodeGuard install $(OBJBINDIR)CodeGuard$(E) $(BINDIR)/ @@ -677,8 +824,14 @@ ifdef LINUX_TARGET install $(INSTALL_FLAGS) $(OBJLIBDIR)$(LP)ecere$(SOV) $(DESTLIBDIR)/$(LP)ecere$(SOV) install $(INSTALL_FLAGS) $(OBJLIBDIR)$(LP)ecereCOM$(SOV) $(DESTLIBDIR)/$(LP)ecereCOM$(SOV) install $(INSTALL_FLAGS) $(OBJLIBDIR)$(LP)ec$(SOV) $(DESTLIBDIR)/ec/$(LP)ec$(SOV) + install $(INSTALL_FLAGS) $(OBJLIBDIR)$(LP)ec2$(SOV) $(DESTLIBDIR)/ec/$(LP)ec2$(SOV) install $(INSTALL_FLAGS) $(OBJLIBDIR)$(LP)EDA$(SOV) $(DESTLIBDIR)/ec/$(LP)EDA$(SOV) +ifdef EDAdBASE + install $(INSTALL_FLAGS) $(OBJLIBDIR)$(LP)EDAdBASE$(SOV) $(DESTLIBDIR)/ec/$(LP)EDAdBASE$(SOV) +endif +ifdef EDASQLite install $(INSTALL_FLAGS) $(OBJLIBDIR)$(LP)EDASQLite$(SOV) $(DESTLIBDIR)/ec/$(LP)EDASQLite$(SOV) +endif ifdef EDASQLiteCipher install $(INSTALL_FLAGS) $(OBJLIBDIR)$(LP)EDASQLiteCipher$(SOV) $(DESTLIBDIR)/ec/$(LP)EDASQLiteCipher$(SOV) endif @@ -688,8 +841,14 @@ endif ln -sf $(LP)ecere$(SOV) $(DESTLIBDIR)/$(LP)ecere$(SO).0 ln -sf $(LP)ecereCOM$(SOV) $(DESTLIBDIR)/$(LP)ecereCOM$(SO).0 ln -sf $(LP)ec$(SOV) $(DESTLIBDIR)/ec/$(LP)ec$(SO).0 + ln -sf $(LP)ec2$(SOV) $(DESTLIBDIR)/ec/$(LP)ec2$(SO).0 ln -sf $(LP)EDA$(SOV) $(DESTLIBDIR)/ec/$(LP)EDA$(SO).0 +ifdef EDAdBASE + ln -sf $(LP)EDAdBASE$(SOV) $(DESTLIBDIR)/ec/$(LP)EDAdBASE$(SO).0 +endif +ifdef EDASQLite ln -sf $(LP)EDASQLite$(SOV) $(DESTLIBDIR)/ec/$(LP)EDASQLite$(SO).0 +endif ifdef EDASQLiteCipher ln -sf $(LP)EDASQLiteCipher$(SOV) $(DESTLIBDIR)/ec/$(LP)EDASQLiteCipher$(SO).0 endif @@ -699,8 +858,14 @@ endif ln -sf $(LP)ecere$(SOV) $(DESTLIBDIR)/$(LP)ecere$(SO) ln -sf $(LP)ecereCOM$(SOV) $(DESTLIBDIR)/$(LP)ecereCOM$(SO) ln -sf $(LP)ec$(SOV) $(DESTLIBDIR)/ec/$(LP)ec$(SO) + ln -sf $(LP)ec2$(SOV) $(DESTLIBDIR)/ec/$(LP)ec2$(SO) ln -sf $(LP)EDA$(SOV) $(DESTLIBDIR)/ec/$(LP)EDA$(SO) +ifdef EDAdBASE + ln -sf $(LP)EDAdBASE$(SOV) $(DESTLIBDIR)/ec/$(LP)EDAdBASE$(SO) +endif +ifdef EDASQLite ln -sf $(LP)EDASQLite$(SOV) $(DESTLIBDIR)/ec/$(LP)EDASQLite$(SO) +endif ln -sf ../$(LP)ecere$(SOV) $(DESTLIBDIR)/ec/$(LP)ecere$(SO) ln -sf ../$(LP)ecereCOM$(SOV) $(DESTLIBDIR)/ec/$(LP)ecereCOM$(SO) ifdef EDASQLiteCipher @@ -718,11 +883,12 @@ else install $(INSTALL_FLAGS) $(OBJLIBDIR)$(LP)ecereCOM$(SO) $(DESTLIBDIR)/$(LP)ecereCOM$(SO) endif install $(INSTALL_FLAGS) $(OBJBINDIR)ecere-ide$(E) $(BINDIR)/ecere-ide$(E) - install $(INSTALL_FLAGS) $(OBJBINDIR)ear$(E) $(BINDIR)/ear$(E) - install $(INSTALL_FLAGS) $(OBJBINDIR)ecc$(E) $(BINDIR)/ecc$(E) - install $(INSTALL_FLAGS) $(OBJBINDIR)ecp$(E) $(BINDIR)/ecp$(E) - install $(INSTALL_FLAGS) $(OBJBINDIR)ecs$(E) $(BINDIR)/ecs$(E) + install $(INSTALL_FLAGS) $(OBJBINDIR)ear$(B32_SFX)$(E) $(BINDIR)/ear$(B32_SFX)$(E) + install $(INSTALL_FLAGS) $(OBJBINDIR)ecc$(B32_SFX)$(E) $(BINDIR)/ecc$(B32_SFX)$(E) + install $(INSTALL_FLAGS) $(OBJBINDIR)ecp$(B32_SFX)$(E) $(BINDIR)/ecp$(B32_SFX)$(E) + install $(INSTALL_FLAGS) $(OBJBINDIR)ecs$(B32_SFX)$(E) $(BINDIR)/ecs$(B32_SFX)$(E) install $(INSTALL_FLAGS) $(OBJBINDIR)epj2make$(E) $(BINDIR)/epj2make$(E) + install $(INSTALL_FLAGS) $(OBJBINDIR)bgen$(E) $(BINDIR)/bgen$(E) install $(INSTALL_FLAGS) $(OBJBINDIR)documentor$(E) $(BINDIR)/documentor$(E) ifdef CodeGuard install $(INSTALL_FLAGS) $(OBJBINDIR)CodeGuard$(E) $(BINDIR)/CodeGuard$(E) @@ -758,10 +924,13 @@ ifdef DEBIAN_PACKAGE cp $(DESTDIR)$(prefix)/share/doc/libecere0/changelog.gz $(DESTDIR)$(prefix)/share/doc/ecere-sdk/ mkdir -p $(DESTDIR)$(prefix)/share/doc/libecc0 ln -sf ../libecere0/changelog.gz $(DESTDIR)$(prefix)/share/doc/libecc0/ + mkdir -p $(DESTDIR)$(prefix)/share/doc/libecc2 + ln -sf ../libecere0/changelog.gz $(DESTDIR)$(prefix)/share/doc/libecc2/ mkdir -p $(DESTDIR)$(prefix)/share/doc/libecerecom0 cp $(DESTDIR)$(prefix)/share/doc/libecere0/changelog.gz $(DESTDIR)$(prefix)/share/doc/libecerecom0/ mkdir -p $(DESTDIR)$(prefix)/share/doc/libeda0 ln -sf ../libecere0/changelog.gz $(DESTDIR)$(prefix)/share/doc/libeda0/ + # todo or not? add edadbase and edasqlitecipher? mkdir -p $(DESTDIR)$(prefix)/share/doc/libedasqlite0 ln -sf ../libecere0/changelog.gz $(DESTDIR)$(prefix)/share/doc/libedasqlite0/ mkdir -p $(DESTDIR)$(prefix)/share/doc/libecereaudio0 @@ -774,9 +943,15 @@ uninstall: $(call rm,"$(DESTLIBDIR)/$(LP)ecere$(SO)") $(call rm,"$(DESTLIBDIR)/$(LP)ecereCOM$(SO)") $(call rm,"$(DESTLIBDIR)/$(LP)ec$(SO)") + $(call rm,"$(DESTLIBDIR)/$(LP)ec2$(SO)") $(call rm,"$(DESTLIBDIR)/$(LP)EDA$(SO)") ifdef LINUX_TARGET +ifdef EDAdBASE + $(call rm,"$(DESTLIBDIR)/ec/$(LP)EDAdBASE$(SO)") +endif +ifdef EDASQLite $(call rm,"$(DESTLIBDIR)/ec/$(LP)EDASQLite$(SO)") +endif ifdef EDASQLiteCipher $(call rm,"$(DESTLIBDIR)/ec/$(LP)EDASQLiteCipher$(SO)") endif @@ -784,7 +959,12 @@ ifneq ($(ECERE_AUDIO),n) $(call rm,"$(DESTLIBDIR)/ec/$(LP)EcereAudio$(SO)") endif else +ifdef EDAdBASE + $(call rm,"$(DESTLIBDIR)/$(LP)EDAdBASE$(SO)") +endif +ifdef EDASQLite $(call rm,"$(DESTLIBDIR)/$(LP)EDASQLite$(SO)") +endif ifdef EDASQLiteCipher $(call rm,"$(DESTLIBDIR)/$(LP)EDASQLiteCipher$(SO)") endif @@ -793,11 +973,12 @@ ifneq ($(ECERE_AUDIO),n) endif endif $(call rm,"$(BINDIR)/ecere-ide$(E)") - $(call rm,"$(BINDIR)/ear$(E)") - $(call rm,"$(BINDIR)/ecc$(E)") - $(call rm,"$(BINDIR)/ecp$(E)") - $(call rm,"$(BINDIR)/ecs$(E)") + $(call rm,"$(BINDIR)/ear$(B32_SFX)$(E)") + $(call rm,"$(BINDIR)/ecc$(B32_SFX)$(E)") + $(call rm,"$(BINDIR)/ecp$(B32_SFX)$(E)") + $(call rm,"$(BINDIR)/ecs$(B32_SFX)$(E)") $(call rm,"$(BINDIR)/epj2make$(E)") + $(call rm,"$(BINDIR)/bgen$(E)") $(call rm,"$(BINDIR)/documentor$(E)") ifdef CodeGuard $(call rm,"$(BINDIR)/CodeGuard$(E)") @@ -815,7 +996,12 @@ ifdef LINUX_TARGET $(call rm,"$(DESTLIBDIR)/$(LP)ecereCOM$(SO).0") $(call rm,"$(DESTLIBDIR)/ec/$(LP)ec$(SO).0") $(call rm,"$(DESTLIBDIR)/ec/$(LP)EDA$(SO).0") +ifdef EDAdBASE + $(call rm,"$(DESTLIBDIR)/ec/$(LP)EDAdBASE$(SO).0") +endif +ifdef EDASQLite $(call rm,"$(DESTLIBDIR)/ec/$(LP)EDASQLite$(SO).0") +endif ifdef EDASQLiteCipher $(call rm,"$(DESTLIBDIR)/ec/$(LP)EDASQLiteCipher$(SO).0") endif @@ -823,8 +1009,14 @@ endif $(call rm,"$(DESTLIBDIR)/$(LP)ecere$(SOV)") $(call rm,"$(DESTLIBDIR)/$(LP)ecereCOM$(SOV)") $(call rm,"$(DESTLIBDIR)/ec/$(LP)ec$(SOV)") + $(call rm,"$(DESTLIBDIR)/ec/$(LP)ec2$(SOV)") $(call rm,"$(DESTLIBDIR)/ec/$(LP)EDA$(SOV)") +ifdef EDAdBASE + $(call rm,"$(DESTLIBDIR)/ec/$(LP)EDAdBASE$(SOV)") +endif +ifdef EDASQLite $(call rm,"$(DESTLIBDIR)/ec/$(LP)EDASQLite$(SOV)") +endif ifdef EDASQLiteCipher $(call rm,"$(DESTLIBDIR)/ec/$(LP)EDASQLiteCipher$(SOV)") endif @@ -930,3 +1122,5 @@ troubleshoot: @$(call echo,OPENSSL_INCLUDE_DIR=$(OPENSSL_INCLUDE_DIR)) @$(call echo,OPENSSL_LIB_DIR=$(OPENSSL_LIB_DIR)) @$(call echo,OPENSSL_BIN_DIR=$(OPENSSL_BIN_DIR)) + @$(call echo,ROOT_ABSPATH=$(ROOT_ABSPATH)) + @$(call echo,DYLD_LIBRARY_PATH=$(DYLD_LIBRARY_PATH)) diff --git a/Makefile.bindings b/Makefile.bindings new file mode 100644 index 0000000000..51ae4d3020 --- /dev/null +++ b/Makefile.bindings @@ -0,0 +1,405 @@ +ifneq ($(V),1) +.SILENT: +endif + +ifndef CROSSPLATFORM_MK +include crossplatform.mk +include default.cf +endif + +renameEcereCOM = $(subst ecereCOM,eC,$(1)) +#$(call renameEcereCOM,$(word $(1),$(BGEN_LIBS))) $(call selectLib,$(1)) +selectLib = $(word $(1),$(BGEN_LIBS)) + +ifndef BINDINGS_DIR +BINDINGS_DIR := bindings +endif +ifndef C_BINDINGS_DIR +C_BINDINGS_DIR := $(BINDINGS_DIR)/c +endif +ifndef CPP_BINDINGS_DIR +CPP_BINDINGS_DIR := $(BINDINGS_DIR)/cpp +endif +ifndef PY_BINDINGS_DIR +PY_BINDINGS_DIR := $(BINDINGS_DIR)/py +endif + +ifndef BGEN_LIBS +BGEN_LIBS := obj/linux/lib/$(LP)ecereCOM$(SO) obj/linux/lib/$(LP)ecere$(SO) obj/linux/lib/$(LP)EDA$(SO) $(ADDITIONAL_BGEN_LIBS) +endif +BGEN_ARGS := -fren Log=__e_log,Logf=__e_logf,Sleep=__sleep,Print=printx +BGEN_QUIET := $(if $(SILENT_IS_ON),-quiet,) + +_BGEN_OUTS := $(foreach _lib,$(BGEN_LIBS),$(call renameEcereCOM,$(call lib_path_to_name,$(_lib)))) +_BGEN_C_OUTPUT := $(addsuffix .h,$(_BGEN_OUTS)) $(addsuffix .c,$(_BGEN_OUTS)) $(addsuffix _c.Makefile,$(_BGEN_OUTS)) +_BGEN_CPP_OUTPUT := $(addsuffix .h,$(_BGEN_OUTS)) $(addsuffix .c,$(_BGEN_OUTS)) $(addsuffix _cpp.Makefile,$(_BGEN_OUTS)) +_BGEN_PY_OUTPUT := $(addprefix cffi-,$(addsuffix .h,$(_BGEN_OUTS))) $(addsuffix .py,$(_BGEN_OUTS)) $(addprefix build_,$(addsuffix .py,$(_BGEN_OUTS))) + +# note: this is needed because we chdir without the use of a makefile specifying a different _CF_DIR +# and cannot call bgen with the usual relative path specified by $(BGEN) +# i.e.: +cd $(C_BINDINGS_DIR) && $(BGEN) $(BGEN_ARGS) $(BGEN_QUIET) -c $(BGEN_LIBS) +#ROOT_ABSPATH = $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) +#BGEN := $(call sys_path,$(ROOT_ABSPATH)$(_CF_DIR)obj/$(HOST_PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/bin/bgen$(HOST_E)) + +ifdef WINDOWS_HOST +PYTHON := python +else +PYTHON := python3 +endif +PY_VER := 3 + +BG_LANG_c := C +BG_LANG_cpp := C++ +BG_LANG_py := Python $(PY_VER) + +lib_path_to_name = $(basename $(if $(LP),$(patsubst $(LP)%$(SO),%$(SO),$(notdir $(1))),$(notdir $(1)))) +get_binding_name = $(call renameEcereCOM,$(call lib_path_to_name,$(call selectLib,$(1)))) + +bg_src_file_c = $(call get_binding_name,$(1)).c +bg_src_file_cpp = $(call get_binding_name,$(1)).cpp +bg_src_file_py = $(call get_binding_name,$(1)).py + +bg_hdr_file_c = $(call get_binding_name,$(1)).h +bg_hdr_file_cpp = $(call get_binding_name,$(1)).hpp +bg_hdr_file_py = + +bg_mak_file_c = $(call get_binding_name,$(1))_c.Makefile +bg_mak_file_cpp = $(call get_binding_name,$(1))_cpp.Makefile +bg_mak_file_py = build_$(call get_binding_name,$(1)).py + +bg_file_path_c = $(C_BINDINGS_DIR)/$(call bg_$(3)_file_$(2),$(1)) +bg_file_path_cpp = $(CPP_BINDINGS_DIR)/$(call bg_$(3)_file_$(2),$(1)) +bg_file_path_py = $(PY_BINDINGS_DIR)/$(call bg_$(3)_file_$(2),$(1)) + +bg_bld_cmd_c = $(_MAKE) -f $(call bg_mak_file_$(2),$(1)) $(3) +bg_bld_cmd_cpp = $(_MAKE) -f $(call bg_mak_file_$(2),$(1)) $(3) +bg_bld_cmd_py = $(PYTHON) $(call bg_mak_file_$(2),$(1)) + +bg_print_lib_name = $(call echo,$(call get_binding_name,$(1))) + +bg_bind_____echo = $(call bg_bind_echo_,$(1),$(2)) +bg_bind_echo_ = $(if $(call selectLib,$(1)),$(if $(wildcard $(call bg_file_path_$(2),$(1),$(2),mak)),@$(call echo,Building $(BG_LANG_$(2)) bindings for $(call get_binding_name,$(1)) ($(call selectLib,$(1)))...),),) + +bg_bind_cd_build = $(if $(call selectLib,$(1)),$(if $(wildcard $(call bg_file_path_$(2),$(1),$(2),mak)),$(call _bg_bind_cd_build,$(1),$(2),$(3)),$(call echo,Error: $(call bg_file_path_$(2),$(1),$(2),mak) not found!)),) +_bg_bind_cd_build = +cd $(BINDINGS_DIR)/$(2) && $(call bg_bld_cmd_$(2),$(1),$(2),$(3)) +# todo: make this an actual error? + +bg_bind_any_gened_exists = $(wildcard $(call bg_file_path_$(2),$(1),$(2),src))$(wildcard $(call bg_file_path_$(2),$(1),$(2),hdr))$(wildcard $(call bg_file_path_$(2),$(1),$(2),mak)) +bg_bind_rmr_gened = $(if $(call selectLib,$(1)),$(if $(2),$(if $(call bg_bind_any_gened_exists,$(1),$(2)),$(call rmr,$(call bg_file_path_$(2),$(1),$(2),src) $(call bg_file_path_$(2),$(1),$(2),hdr) $(call bg_file_path_$(2),$(1),$(2),mak)),),),) + +# All +################################################################################################################################ + +.PHONY: bgentalk +bgentalk: bindings_build + @$(call echo,"") + @$(call echo, have a great day!) + @$(call echo, -bgen) + +.PHONY: list_bindings_name +list_bindings_name: + $(call bg_print_lib_name,1) + $(call bg_print_lib_name,2) + $(call bg_print_lib_name,3) + $(call bg_print_lib_name,4) + $(call bg_print_lib_name,5) + $(call bg_print_lib_name,6) + $(call bg_print_lib_name,7) + $(call bg_print_lib_name,8) + $(call bg_print_lib_name,9) + +.PHONY: bindings +.NOTPARALLEL: bindings +bindings: bgentalk + +.PHONY: bindingsgen +.NOTPARALLEL: bindingsgen +bindingsgen: bindings_gen + +.PHONY: bindings_gen +.NOTPARALLEL: bindings_gen +bindings_gen: c_bindings_gen cpp_bindings_gen py_bindings_gen + +.PHONY: bindings_build +.NOTPARALLEL: bindings_build +bindings_build: c_bindings_build cpp_bindings_build py_bindings_build + +.PHONY: bindings_clean +.NOTPARALLEL: bindings_clean +bindings_clean: c_bindings_clean cpp_bindings_clean py_bindings_clean + +.PHONY: bindings_cleantarget +.NOTPARALLEL: bindings_cleantarget +bindings_cleantarget: c_bindings_cleantarget cpp_bindings_cleantarget py_bindings_cleantarget + +.PHONY: bindings_realclean +.NOTPARALLEL: bindings_realclean +bindings_realclean: c_bindings_realclean cpp_bindings_realclean py_bindings_realclean + +.PHONY: bindings_wipeclean +.NOTPARALLEL: bindings_wipeclean +bindings_wipeclean: c_bindings_wipeclean cpp_bindings_wipeclean py_bindings_wipeclean + +.PHONY: bindings_distclean +.NOTPARALLEL: bindings_distclean +bindings_distclean: + $(call rmr,$(BINDINGS_DIR)/) + +# C +################################################################################################################################ + +.PHONY: c_bindings +.NOTPARALLEL: c_bindings +c_bindings: c_bindings_build + +.PHONY: c_bindings_gen +.NOTPARALLEL: c_bindings_gen +c_bindings_gen: $(BINARIES) + @$(call echo,Generating C bindings...) + $(call mkdir,$(BINDINGS_DIR)) + $(call mkdir,$(C_BINDINGS_DIR)) + $(if $(BGEN_SKIP),,$(BGEN) -dir $(C_BINDINGS_DIR) $(BGEN_ARGS) $(BGEN_QUIET) -c $(BGEN_LIBS)) + +.PHONY: c_bindings_build +.NOTPARALLEL: c_bindings_build +c_bindings_build: c_bindings_gen + $(call bg_bind_____echo,1,c) + $(call bg_bind_cd_build,1,c,all) + $(call bg_bind_____echo,2,c) + $(call bg_bind_cd_build,2,c,all) + $(call bg_bind_____echo,3,c) + $(call bg_bind_cd_build,3,c,all) + $(call bg_bind_____echo,4,c) + $(call bg_bind_cd_build,4,c,all) + $(call bg_bind_____echo,5,c) + $(call bg_bind_cd_build,5,c,all) + $(call bg_bind_____echo,6,c) + $(call bg_bind_cd_build,6,c,all) + $(call bg_bind_____echo,7,c) + $(call bg_bind_cd_build,7,c,all) + $(call bg_bind_____echo,8,c) + $(call bg_bind_cd_build,8,c,all) + $(call bg_bind_____echo,9,c) + $(call bg_bind_cd_build,9,c,all) + +.PHONY: c_bindings_install +.NOTPARALLEL: c_bindings_install +c_bindings_install: + install $(INSTALL_FLAGS) $(OBJLIBDIR)$(LP)ecere_c$(SOV) $(DESTLIBDIR)/$(LP)ecere_c$(SOV) + install $(INSTALL_FLAGS) $(OBJLIBDIR)$(LP)eC_c$(SOV) $(DESTLIBDIR)/$(LP)eC_c$(SOV) + install $(INSTALL_FLAGS) $(OBJLIBDIR)$(LP)EDA_c$(SOV) $(DESTLIBDIR)/ec/$(LP)EDA_c$(SOV) + ln -sf $(LP)ecere_c$(SOV) $(DESTLIBDIR)/$(LP)ecere_c$(SO).0 + ln -sf $(LP)eC_c$(SOV) $(DESTLIBDIR)/$(LP)eC_c$(SO).0 + ln -sf $(LP)EDA_c$(SOV) $(DESTLIBDIR)/ec/$(LP)EDA_c$(SO).0 + +.PHONY: c_bindings_clean +.NOTPARALLEL: c_bindings_clean +c_bindings_clean: + $(call bg_bind_cd_build,1,c,clean) + $(call bg_bind_cd_build,2,c,clean) + $(call bg_bind_cd_build,3,c,clean) + $(call bg_bind_cd_build,4,c,clean) + $(call bg_bind_cd_build,5,c,clean) + $(call bg_bind_cd_build,6,c,clean) + $(call bg_bind_cd_build,7,c,clean) + $(call bg_bind_cd_build,8,c,clean) + $(call bg_bind_cd_build,9,c,clean) + $(call rmr,$(_BGEN_C_OUTPUT)) + +.PHONY: c_bindings_cleantarget +.NOTPARALLEL: c_bindings_cleantarget +c_bindings_cleantarget: + $(call bg_bind_cd_build,1,c,cleantarget) + $(call bg_bind_cd_build,2,c,cleantarget) + $(call bg_bind_cd_build,3,c,cleantarget) + $(call bg_bind_cd_build,4,c,cleantarget) + $(call bg_bind_cd_build,5,c,cleantarget) + $(call bg_bind_cd_build,6,c,cleantarget) + $(call bg_bind_cd_build,7,c,cleantarget) + $(call bg_bind_cd_build,8,c,cleantarget) + $(call bg_bind_cd_build,9,c,cleantarget) + +.PHONY: c_bindings_realclean +.NOTPARALLEL: c_bindings_realclean +c_bindings_realclean: + $(call rmr,$(C_BINDINGS_DIR)/obj) + $(call bg_bind_rmr_gened,1,c) + $(call bg_bind_rmr_gened,2,c) + $(call bg_bind_rmr_gened,3,c) + $(call bg_bind_rmr_gened,4,c) + $(call bg_bind_rmr_gened,5,c) + $(call bg_bind_rmr_gened,6,c) + $(call bg_bind_rmr_gened,7,c) + $(call bg_bind_rmr_gened,8,c) + $(call bg_bind_rmr_gened,9,c) + +.PHONY: c_bindings_wipeclean +c_bindings_wipeclean: + $(call rmr,$(C_BINDINGS_DIR)) + $(call bg_bind_rmr_gened,1,c) + $(call bg_bind_rmr_gened,2,c) + $(call bg_bind_rmr_gened,3,c) + $(call bg_bind_rmr_gened,4,c) + $(call bg_bind_rmr_gened,5,c) + $(call bg_bind_rmr_gened,6,c) + $(call bg_bind_rmr_gened,7,c) + $(call bg_bind_rmr_gened,8,c) + $(call bg_bind_rmr_gened,9,c) + +# C++ +################################################################################################################################ + +.PHONY: cpp_bindings +.NOTPARALLEL: cpp_bindings +cpp_bindings: cpp_bindings_build + +.PHONY: cpp_bindings_gen +.NOTPARALLEL: cpp_bindings_gen +cpp_bindings_gen: $(BINARIES) + @$(call echo,Generating C++ bindings...) + $(call mkdir,$(BINDINGS_DIR)) + $(call mkdir,$(CPP_BINDINGS_DIR)) + $(if $(BGEN_SKIP),,$(BGEN) -dir $(CPP_BINDINGS_DIR) $(BGEN_ARGS) $(BGEN_QUIET) -cpp -bypassmacros $(BGEN_LIBS)) + +.PHONY: cpp_bindings_build +.NOTPARALLEL: cpp_bindings_build +cpp_bindings_build: cpp_bindings_gen + $(call bg_bind_____echo,1,cpp) + $(call bg_bind_cd_build,1,cpp,all) + $(call bg_bind_____echo,2,cpp) + $(call bg_bind_cd_build,2,cpp,all) + $(call bg_bind_____echo,3,cpp) + $(call bg_bind_cd_build,3,cpp,all) + $(call bg_bind_____echo,4,cpp) + $(call bg_bind_cd_build,4,cpp,all) + $(call bg_bind_____echo,5,cpp) + $(call bg_bind_cd_build,5,cpp,all) + $(call bg_bind_____echo,6,cpp) + $(call bg_bind_cd_build,6,cpp,all) + $(call bg_bind_____echo,7,cpp) + $(call bg_bind_cd_build,7,cpp,all) + $(call bg_bind_____echo,8,cpp) + $(call bg_bind_cd_build,8,cpp,all) + $(call bg_bind_____echo,9,cpp) + $(call bg_bind_cd_build,9,cpp,all) + +.PHONY: cpp_bindings_clean +.NOTPARALLEL: cpp_bindings_clean +cpp_bindings_clean: + $(call bg_bind_cd_build,1,cpp,clean) + $(call bg_bind_cd_build,2,cpp,clean) + $(call bg_bind_cd_build,3,cpp,clean) + $(call bg_bind_cd_build,4,cpp,clean) + $(call bg_bind_cd_build,5,cpp,clean) + $(call bg_bind_cd_build,6,cpp,clean) + $(call bg_bind_cd_build,7,cpp,clean) + $(call bg_bind_cd_build,8,cpp,clean) + $(call bg_bind_cd_build,9,cpp,clean) + $(call rmr,$(_BGEN_CPP_OUTPUT)) + +.PHONY: cpp_bindings_cleantarget +.NOTPARALLEL: cpp_bindings_cleantarget +cpp_bindings_cleantarget: + $(call bg_bind_cd_build,1,cpp,cleantarget) + $(call bg_bind_cd_build,2,cpp,cleantarget) + $(call bg_bind_cd_build,3,cpp,cleantarget) + $(call bg_bind_cd_build,4,cpp,cleantarget) + $(call bg_bind_cd_build,5,cpp,cleantarget) + $(call bg_bind_cd_build,6,cpp,cleantarget) + $(call bg_bind_cd_build,7,cpp,cleantarget) + $(call bg_bind_cd_build,8,cpp,cleantarget) + $(call bg_bind_cd_build,9,cpp,cleantarget) + +.PHONY: cpp_bindings_realclean +.NOTPARALLEL: cpp_bindings_realclean +cpp_bindings_realclean: + $(call rmr,$(CPP_BINDINGS_DIR)/obj) + $(call bg_bind_rmr_gened,1,cpp) + $(call bg_bind_rmr_gened,2,cpp) + $(call bg_bind_rmr_gened,3,cpp) + $(call bg_bind_rmr_gened,4,cpp) + $(call bg_bind_rmr_gened,5,cpp) + $(call bg_bind_rmr_gened,6,cpp) + $(call bg_bind_rmr_gened,7,cpp) + $(call bg_bind_rmr_gened,8,cpp) + $(call bg_bind_rmr_gened,9,cpp) + +.PHONY: cpp_bindings_wipeclean +.NOTPARALLEL: cpp_bindings_wipeclean +cpp_bindings_wipeclean: + $(call rmr,$(CPP_BINDINGS_DIR)) + $(call bg_bind_rmr_gened,1,cpp) + $(call bg_bind_rmr_gened,2,cpp) + $(call bg_bind_rmr_gened,3,cpp) + $(call bg_bind_rmr_gened,4,cpp) + $(call bg_bind_rmr_gened,5,cpp) + $(call bg_bind_rmr_gened,6,cpp) + $(call bg_bind_rmr_gened,7,cpp) + $(call bg_bind_rmr_gened,8,cpp) + $(call bg_bind_rmr_gened,9,cpp) + +# Python +################################################################################################################################ + +.PHONY: py_bindings +.NOTPARALLEL: py_bindings +py_bindings: py_bindings_build + +.PHONY: py_bindings_gen +.NOTPARALLEL: py_bindings_gen +py_bindings_gen: $(BINARIES) + @$(call echo,Generating Python bindings...) + $(call mkdir,$(BINDINGS_DIR)) + $(call mkdir,$(PY_BINDINGS_DIR)) + $(if $(BGEN_SKIP),,$(BGEN) -dir $(PY_BINDINGS_DIR) $(BGEN_ARGS) $(BGEN_QUIET) -py $(BGEN_LIBS)) + +OBJLIBDIR := $(OBJDIR)$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/lib/ + +.PHONY: py_bindings_build +.NOTPARALLEL: py_bindings_build +py_bindings_build: c_bindings_build py_bindings_gen + $(if $(wildcard $(ADDITIONAL_BGEN_LIBS)),$(call cp,$(wildcard $(ADDITIONAL_BGEN_LIBS)),$(OBJLIBDIR)),) +ifdef PY_NA + $(error Using $(PYTHON) failed. Please set PYTHON to point to a valid python program name or location) +else + $(call bg_bind_____echo,1,py) + $(call bg_bind_cd_build,1,py) + $(call bg_bind_____echo,2,py) + $(call bg_bind_cd_build,2,py) + $(call bg_bind_____echo,3,py) + $(call bg_bind_cd_build,3,py) + $(call bg_bind_____echo,4,py) + $(call bg_bind_cd_build,4,py) + $(call bg_bind_____echo,5,py) + $(call bg_bind_cd_build,5,py) + $(call bg_bind_____echo,6,py) + $(call bg_bind_cd_build,6,py) + $(call bg_bind_____echo,7,py) + $(call bg_bind_cd_build,7,py) + $(call bg_bind_____echo,8,py) + $(call bg_bind_cd_build,8,py) + $(call bg_bind_____echo,9,py) + $(call bg_bind_cd_build,9,py) +endif + +.PHONY: py_bindings_clean +.NOTPARALLEL: py_bindings_clean +py_bindings_clean: + $(call rmr,$(_BGEN_PY_OUTPUT)) + +.PHONY: py_bindings_cleantarget +.NOTPARALLEL: py_bindings_cleantarget +py_bindings_cleantarget: +# @$(call echo,todo: py_bindings_cleantarget) + +.PHONY: py_bindings_realclean +.NOTPARALLEL: py_bindings_realclean +py_bindings_realclean: + $(call rmr,$(PY_BINDINGS_DIR)/obj) + $(call rmr,$(PY_BINDINGS_DIR)/__pycache__) +# @$(call echo,todo: py_bindings_realclean -- _py*.c _py*.pyd) + +.PHONY: py_bindings_wipeclean +.NOTPARALLEL: py_bindings_wipeclean +py_bindings_wipeclean: + $(call rmr,$(PY_BINDINGS_DIR)) diff --git a/README.md b/README.md index 38619071da..be3492a498 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![GitHub version](https://badge.fury.io/gh/ecere%2Fecere-sdk.svg)](https://github.com/ecere/ecere-sdk/releases/latest) -[![Travis status](https://api.travis-ci.org/ecere/ecere-sdk.svg?branch=master)](https://travis-ci.org/ecere/ecere-sdk) +[![Travis status](https://api.travis-ci.org/ecere/ecere-sdk.svg?branch=ec2)](https://travis-ci.org/ecere/ecere-sdk) [![Ecere docs](https://img.shields.io/badge/docs-being_improved-yellow.svg)](http://ecere.org/overview)
[![GitHub size](https://reposs.herokuapp.com/?path=ecere/ecere-sdk&style=flat)](https://github.com/ecere/ecere-sdk/graphs/traffic) [![GitHub license](https://img.shields.io/badge/license-BSD%203--Clause%20(Revised)-blue.svg)](https://tldrlegal.com/license/bsd-3-clause-license-(revised))
@@ -7,7 +7,7 @@ [![Ecere web](https://img.shields.io/badge/web-ec--lang.org-9DA1CA.svg)](http://ec-lang.org) [![Ecere forum](https://img.shields.io/badge/forum-ecere.org%2Fforums-9DA1CA.svg)](http://ecere.org/forums) [![Ecere tracker](https://img.shields.io/badge/tracker-ecere.org%2Fmantis-9DA1CA.svg)](http://ecere.org/mantis) -[![Freenode chat](https://img.shields.io/badge/irc-%20Freenode_%23ecere-9DA1CA.svg)](http://webchat.freenode.net/?channels=ecere)
+[![Libera chat](https://img.shields.io/badge/irc-%20Libera_%23ecere-9DA1CA.svg)](https://web.libera.chat/?theme=cli#ecere)
[![Meetup group](https://img.shields.io/badge/meetup-Ottawa-88B094.svg)](http://www.meetup.com/eC-Programming-Language-Meetup) # Ecere SDK @@ -109,4 +109,4 @@ descriptive info has been added yet. [Git Repository](http://github.com/ecere/ecere-sdk) ( git://github.com/ecere/ecere-sdk.git )
[Support forums](http://ecere.org/forums)
[Bug tracker](http://ecere.org/mantis)
-[IRC](http://webchat.freenode.net/?channels=ecere) - **#ecere** on irc.freenode.net
+[IRC](https://web.libera.chat/?theme=cli#ecere) - **#ecere** on irc.libera.chat
diff --git a/audio/Makefile b/audio/Makefile index 73ecae5372..adb8fe6693 100644 --- a/audio/Makefile +++ b/audio/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -129,7 +133,7 @@ endif all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(if $(ECERE_SDK_SRC),$(if $(wildcard $(call escspace,$(ECERE_SDK_SRC)/crossplatform.mk)),,@$(call echo,Ecere SDK Source Warning: The value of ECERE_SDK_SRC is pointing to an incorrect ($(ECERE_SDK_SRC)) location.)),) $(if $(ECERE_SDK_SRC),,$(if $(ECP_DEBUG)$(ECC_DEBUG)$(ECS_DEBUG),@$(call echo,ECC Debug Warning: Please define ECERE_SDK_SRC before using ECP_DEBUG, ECC_DEBUG or ECS_DEBUG),)) @@ -213,7 +217,7 @@ $(OBJ)mixer.o: $(OBJ)mixer.c $(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.c -o $(call quote_path,$@) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -238,9 +242,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/audio/alsa.ec b/audio/alsa.ec index 8305a85807..66adbe58b8 100644 --- a/audio/alsa.ec +++ b/audio/alsa.ec @@ -22,13 +22,13 @@ public void AudioSetBalance(double percent) { balance = percent; } - + // TODO: Clarify the range of percent, Acovel invokes this with 0..1 slider values public bool AudioSetVolume(VolumeControl type, double percent) { bool result = false; if(type == application) { - volume = percent; + volume = percent * 100; result = true; } else diff --git a/autoLayout/SlideTest.epj b/autoLayout/SlideTest.epj new file mode 100644 index 0000000000..d45452685b --- /dev/null +++ b/autoLayout/SlideTest.epj @@ -0,0 +1,71 @@ +{ + "Version" : 0.2, + "ModuleName" : "SlideTest", + "Options" : { + "Warnings" : "All", + "PreprocessorDefinitions" : [ + "IMPORT_STATIC=\"\"" + ], + "TargetType" : "Executable", + "TargetFileName" : "SlideTest", + "Libraries" : [ + "ecere" + ] + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "Console" : true, + "FastMath" : false + } + }, + { + "Name" : "Release", + "Options" : { + "Debug" : false, + "Optimization" : "Speed", + "FastMath" : true + } + } + ], + "Files" : [ + { + "Folder" : "autoLayout", + "Files" : [ + "autoLayout.ec", + "wrapText.ec" + ] + }, + { + "Folder" : "graphics", + "Files" : [ + "bg1.png" + ] + }, + { + "Folder" : "slides", + "Files" : [ + "slide1.ec", + "./base.ec", + "titleSlide.ec", + "languageSlide.ec", + "classesSlide.ec", + "instancesSlide.ec", + "formSlide.ec", + "buttonSlide.ec", + "gnosisSlide.ec" + ] + }, + "slides.ec" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/autoLayout/autoLayout/autoLayout.ec b/autoLayout/autoLayout/autoLayout.ec new file mode 100644 index 0000000000..c9824bcac9 --- /dev/null +++ b/autoLayout/autoLayout/autoLayout.ec @@ -0,0 +1,561 @@ +public import "ecere" +import "wrapText" + +// Add 'unset' ? +enum UnitType { percent, pixels, points }; + +struct Dimension +{ + UnitType type; + union { double d; int i; }; + property double { set { d = value; type = percent; } get { return 0; } /* TODO: Compute this? Avoid use? */ } + property int { set { i = value; type = pixels; } get { return 0; } /* TODO: Compute this? Avoid use? */ } + int getPixels(int p) { return (type == pixels) ? i : (int)(d * p + 0.5); } +}; + +struct DimensionBox { Dimension left, top, right, bottom; }; + +struct Dimensions { Dimension w, h; }; + +enum Direction { horizontal, vertical }; + +enum HVAlignment : Alignment { bottom = right, top = left }; +enum SelfAlignment : HVAlignment { inherit }; + +FontResource defaultFont { "Tahoma", 8.25f }; + +class Element +{ +private: + List nodes; + DimensionBox margin; + DimensionBox border; + DimensionBox padding; + Size contentSize; // Computed from the content (Max of Text/Primitives extent and Children minimum extent) + Element parent; + + Dimensions minSize, maxSize; // Size specification + Size clientSize; + BorderStyle borderStyle; + Point scroll; + bool hScroll, vScroll; + const String caption; + ColorAlpha bgColor; + ColorAlpha fgColor; + ColorAlpha borderColor; + Direction direction; + bool autoLayoutFlag; + Point position; // Position relative to parent's client area, including margin offset + Point tlPosition; + HVAlignment hAlignment, vAlignment; // Alignment of content (children or graphics) ? + SelfAlignment selfHAlignment, selfVAlignment; + + selfHAlignment = inherit; + selfVAlignment = inherit; + + autoLayoutFlag = true; + fgColor = black; + bgColor = 0; + + FontResource font; + Font fontObject; + BitmapResource bitmap; + Bitmap bmpObject; + ColorAlpha bitmapTint; + bitmapTint = white; + /* + List primitives; // Displays above nodes + List bgPrimitives; // Displays behind nodes, need to figure out how to scale to width (e.g. Rectangle with a gradient) + List effects; + */ + + void loadGraphics(DisplaySystem displaySystem) + { + if(!fontObject) + { + Element e = this; + FontResource font; + while(e && !e.font) e = e.parent; + font = e ? e.font : defaultFont; + displaySystem.LoadResource(font); + fontObject = font.font; + } + + if(!bmpObject && bitmap) + { + displaySystem.LoadResource(bitmap); + bmpObject = bitmap.bitmap; + } + + for(n : nodes) + n.loadGraphics(displaySystem); + } + + void computeContentSize(DisplaySystem displaySystem) + { + int cw = clientSize.w, ch = clientSize.h; + int minimum = 0, thickness = 0; + Size graphicsSize { }; + int rcw = cw, rch = ch; + for(n : nodes) + { + Element e = n; + if(e.autoLayoutFlag) + { + int xw = e.maxSize.w.getPixels(cw); + int xh = e.maxSize.h.getPixels(ch); + int nw = e.minSize.w.getPixels(cw); + int nh = e.minSize.h.getPixels(ch); + int w = 0, h = 0; + Box m + { + left = e.margin.left.getPixels(cw); + right = e.margin.right.getPixels(cw); + top = e.margin.top.getPixels(ch); + bottom = e.margin.bottom.getPixels(ch); + }; + + xw += m.left = m.right; + xh += m.top = m.bottom; + nw += m.left = m.right; + nh += m.top = m.bottom; + + if(xw > w) + w = Min(xw, rcw); + //if(xh > h) + h = Min(xh, rch); + + if(xw && xw < w) w = xw; + if(xh && xh < h) h = xh; + if(nw && nw > w) w = nw; + if(nh && nh > h) h = nh; + + if(!w) w = cw; + if(!h) h = ch; + + w -= m.left + m.right; + h -= m.top + m.bottom; + + e.clientSize = { w, h }; + + e.computeContentSize(displaySystem); + if(!e.hScroll && e.contentSize.w > nw) nw = e.contentSize.w; + if(!e.vScroll && e.contentSize.h > nh) nh = e.contentSize.h; + + if(direction == horizontal) + rcw -= nw; + + if(direction == horizontal) + { + minimum += nw; + if(nh > thickness) thickness = nh; + } + else + { + minimum += nh; + if(nw > thickness) thickness = nw; + } + } + } + if(caption) + { + /*if(!cw) cw = MAXINT; + if(!ch) ch = MAXINT;*/ + + //displaySystem.FontExtent(fontObject, caption, strlen(caption), (int *)&graphicsSize.w, (int *)&graphicsSize.h); + wrapTextExtent(displaySystem, fontObject, caption, cw, ch, (int *)&graphicsSize.w, (int *)&graphicsSize.h); + if(graphicsSize.w) + graphicsSize.w += 1; + } + + // Set the content size to the max of (minimum extent of children, graphics extent) + if(direction == horizontal) + contentSize = { Max(graphicsSize.w, minimum), Max(graphicsSize.h, thickness) }; + else + contentSize = { Max(graphicsSize.w, thickness), Max(graphicsSize.h, minimum) }; + } + + void updateTLPosition() + { + for(n : nodes) + { + Element e = n; + e.tlPosition = { e.position.x + tlPosition.x, e.position.y + tlPosition.y }; + n.updateTLPosition(); + } + } + + void autoLayout() + { + int cw = clientSize.w, ch = clientSize.h; + int totalMax = 0; + int totalUsed[Alignment] = { 0 }; + int thickness = direction == vertical ? clientSize.w : clientSize.h; + int start = 0; + SelfAlignment lastAlignment = (direction == horizontal) ? nodes[0].selfHAlignment : nodes[0].selfVAlignment; + int totalMin = 0; + if(lastAlignment == inherit) lastAlignment = (direction == horizontal) ? hAlignment : vAlignment; + + // Allocate extra space + for(n : nodes) + { + Element e = n; + e.clientSize = { }; + if(e.autoLayoutFlag) + { + Box m + { + left = e.margin.left.getPixels(cw); + right = e.margin.right.getPixels(cw); + top = e.margin.top.getPixels(ch); + bottom = e.margin.bottom.getPixels(ch); + }; + + if(direction == horizontal) + { + int xw = e.maxSize.w.getPixels(cw); + int nw = e.minSize.w.getPixels(cw); + int w = Max(nw, e.contentSize.w); + int mm = m.left + m.right; + if(xw && xw < w) w = xw; + + totalMin += w + mm; + + if(xw && w > xw) xw = w; + + totalMax += Max(w, xw) + mm; + totalUsed[e.selfHAlignment == inherit ? hAlignment : e.selfHAlignment] += Max(xw, w) + mm; + } + else + { + int xh = e.maxSize.h.getPixels(ch); + int nh = e.minSize.h.getPixels(ch); + int h = Max(nh, e.contentSize.h); + int mm = m.top + m.bottom; + if(xh && xh < h) h = xh; + + totalMin += h + mm; + + if(xh && h > xh) xh = h; + + totalMax += Max(h, xh) + mm; + totalUsed[e.selfVAlignment == inherit ? vAlignment : e.selfVAlignment] += Max(xh, h) + mm; + } + } + } + + if(direction == horizontal) + { + if(totalUsed[lastAlignment] < cw) + { + if(lastAlignment == right) + start = (cw - totalUsed[lastAlignment]); + else if(lastAlignment == center) + start = (cw - totalUsed[lastAlignment]) / 2; + } + } + else + { + if(totalUsed[lastAlignment] < ch) + { + if(lastAlignment == right) + start = (ch - totalUsed[lastAlignment]); + else if(lastAlignment == center) + start = (ch - totalUsed[lastAlignment]) / 2; + } + } + + for(n : nodes) + { + Element e = n; + int w = e.hScroll ? 0 : e.contentSize.w; + int h = e.vScroll ? 0 : e.contentSize.h; + int nw = e.minSize.w.getPixels(cw); + int nh = e.minSize.h.getPixels(ch); + int xw = e.maxSize.w.getPixels(cw); + int xh = e.maxSize.h.getPixels(ch); + Box m + { + left = e.margin.left.getPixels(cw); + right = e.margin.right.getPixels(cw); + top = e.margin.top.getPixels(ch); + bottom = e.margin.bottom.getPixels(ch); + }; + bool positionUpdated = false; + + if(nw > w) w = nw; + if(nh > h) h = nh; + + w += m.left + m.right; + h += m.top + m.bottom; + xw += m.left + m.right; + xh += m.top + m.bottom; + + if(xw && w > xw) w = xw; + if(xh && h > xh) h = xh; + + if(direction == horizontal) + { + SelfAlignment alignment = e.selfHAlignment == inherit ? hAlignment : e.selfHAlignment; + int y = 0; + xw = (w < xw && totalMax > totalMin && cw > totalMin) ? (int)(w + (xw - w) * Min(1.0f, (float)(cw - totalMin) / (totalMax - totalMin))) : 0; + + if(xw > w) w = xw; + if(xh > h) h = xh; + if(h > thickness) h = thickness; + + w = Min(w, Max(0, cw - start)); + + if(alignment != lastAlignment && totalUsed[alignment] < cw && lastAlignment != right && (lastAlignment != center || alignment == right)) + { + int newStart = (cw - totalUsed[alignment]); + if(alignment == center) + newStart /= 2; + if(newStart > start) + start = newStart; + lastAlignment = alignment; + } + + switch(e.selfVAlignment == inherit ? vAlignment : e.selfVAlignment) + { + case right: y = thickness - h; break; + case center: y = (thickness - h) / 2; break; + } + e.position = { start, y + m.top }; + start += w; + } + else + { + SelfAlignment alignment = e.selfVAlignment == inherit ? vAlignment : e.selfVAlignment; + int x = 0; + xh = (h < xh && totalMax > totalMin && ch > totalMin) ? (int)(h + (xh - h) * Min(1.0f, (float)(ch - totalMin) / (totalMax - totalMin))) : 0; + + if(xh > h) h = xh; + if(xw > w) w = xw; + if(w > thickness) w = thickness; + + h = Min(h, Max(0, ch - start)); + + if(alignment != lastAlignment && totalUsed[alignment] < ch && lastAlignment != right && (lastAlignment != center || alignment == right)) + { + int newStart = (ch - totalUsed[alignment]); + if(alignment == center) + newStart /= 2; + if(newStart > start) + start = newStart; + lastAlignment = alignment; + } + + switch(e.selfHAlignment == inherit ? hAlignment : e.selfHAlignment) + { + case right: x = thickness - w; break; + case center: x = (thickness - w) / 2; break; + } + + e.position = { x + m.left, start }; + start += h; + } + w -= m.left + m.right; + h -= m.top + m.bottom; + + if(w != e.clientSize.w || h != e.clientSize.h) + { + e.tlPosition = { e.position.x + tlPosition.x, e.position.y + tlPosition.y }; + e.clientSize = { w, h }; + if(e.autoLayoutFlag && e.nodes) + e.autoLayout(); + } + else + { + if(e.tlPosition.x != e.position.x + tlPosition.x || e.tlPosition.y != e.position.y + tlPosition.y ) + positionUpdated = true; + e.tlPosition = { e.position.x + tlPosition.x, e.position.y + tlPosition.y }; + if(positionUpdated && e.nodes) + e.updateTLPosition(); + } + } + } + + void render(Surface surface) + { + surface.background = bgColor; + surface.Area(tlPosition.x, tlPosition.y, tlPosition.x + clientSize.w - 1, tlPosition.y + clientSize.h - 1); + if(bmpObject) + { + int sw = bmpObject.width, sh = bmpObject.height; + int x = (clientSize.w - sw) / 2; + int y = (clientSize.h - sh) / 2; + surface.blitTint = bitmapTint; + surface.Blit(bmpObject, x,y,0,0, sw,sh); + } + if(caption) + { + int sw = contentSize.w, sh = contentSize.h; + int x = tlPosition.x, y = tlPosition.y; + if(hAlignment == center) + x += (clientSize.w - sw) / 2; + if(vAlignment == center) + y += (clientSize.h - sh) / 2; + + surface.foreground = fgColor; + surface.font = fontObject; + // surface.WriteText(x, y, caption, strlen(caption)); + wrapText(surface, caption, x, y, tlPosition.x + clientSize.w, tlPosition.y + clientSize.h); + } + if(nodes) + { + for(n : nodes) + n.render(surface); + } + } + +public: + property Element parent + { + set + { + if(!value.nodes) value.nodes = { }; + value.nodes.Add(this); + + parent = value; + } + } +} + +/* +class Elemental : Col +{ + Bar r1 + { + Element b0 { caption = "<<" }; + Bar s1 { }; + Element b1 { caption = "The" }; + Element b2 { caption = "Quick" }; + Element b3 { caption = "Brown" }; + Bar s2 { }; + }; + Bar r2 + { + Element b4 { caption = "Fox." }; + Element b5 { }; + Element b6 { }; + }; + Bar r3 + { + Element b7 { caption = "Left" }; + Element b8 { caption = "Address Bar" }; + Element b9 { caption = "Right" }; + }; +} + +{ [ + { "class == Elemental", bgColor = ivory }, + { "id == Elemental::r1", bgColor = gray, maxSize = { 100%, 100 } }, + { "id == Elemental::b0", fgColor = white, bgColor = navy }, + { "id == Elemental::b1", bgColor = red }, + { "id == Elemental::b2", bgColor = blue, fgColor = white }, + + { "id == Elemental::r2", bgColor = lightGray, maxSize = { 100%, 150 } }, + { "id == Elemental::b4", bgColor = yellow }, + { "id == Elemental::b5", bgColor = aquamarine, maxSize = { 25%, 50 } }, + { "id == Elemental::b6", bgColor = tomato, maxSize = { 50%, 50 } }, + + { "id == Elemental::r3", bgColor = lightGray, maxSize = { 100%, 0 } }, + { "id == Elemental::b7", bgColor = skyBlue }, + { "id == Elemental::b8", bgColor = teal, maxSize.w = 100% }, + { "id == Elemental::b9", bgColor = maroon } +] }; +*/ + +class Bar : Element +{ + direction = horizontal; + maxSize = { 1.0, 1.0 }; +} + +class Col : Element +{ + direction = vertical; + maxSize = { 1.0, 1.0 }; +} + +/* +class Elemental : Col +{ + bgColor = ivory; + + Bar header { this, bgColor = blue }; + Bar middle { this, bgColor = white }; + Col col1 { middle, bgColor = lime }; + Element e1 { col1, caption = "Foo", bgColor = gray, selfHAlignment = center, selfVAlignment = center }; + Col col2 { middle, bgColor = skyBlue }; + Element e2 { col2, caption = "Bar", bgColor = lightGray, selfHAlignment = center, selfVAlignment = center }; + Col col3 { middle, bgColor = tomato }; + Element e3 { col3, caption = "Third", bgColor = lightGray, selfHAlignment = center, selfVAlignment = center }; + Bar footer { this, bgColor = red }; +} + +class Elemental2 : Col +{ + bgColor = skyBlue; + + Bar r1 { this }; + Col c1 { r1, maxSize.w = 0.25, bgColor = blue }; + Col c2 { r1, maxSize.w = 0.5, bgColor = red }; + Col c3 { r1, maxSize.w = 0.25, bgColor = blue }; + + Bar r { this, bgColor = beige, maxSize.h = 10 }; + + Bar r2 { this }; + Col d1 { r2, maxSize.w = 0.25, bgColor = blue }; + Col d11 { r2, maxSize.w = 0.5, caption = "Hello", bgColor = green }; + Col d2 { r2, maxSize.w = 0.5, bgColor = red }; + Col d3 { r2, maxSize.w = 0.25, bgColor = blue }; + Col d4 { r2, maxSize.w = 0.25, bgColor = green }; + + Bar rr { this, bgColor = beige, maxSize.h = 10 }; + + Bar r3 { this }; + Col { r3, minSize.w = 30, maxSize.w = 0, bgColor = blue }; + Col { r3, maxSize.w = 1.0, caption = "Hello", bgColor = green }; + Col { r3, minSize.w = 30, maxSize.w = 0, bgColor = red }; +} +*/ + +class AutoLayoutForm : Window +{ + displayDriver = "OpenGL"; + caption = ""; + background = formColor; + borderStyle = sizable; + hasMaximize = true; + hasMinimize = true; + hasClose = true; + clientSize = { 640, 480 }; + + Element contents; + + bool OnLoadGraphics() + { + contents.loadGraphics(displaySystem); + return true; + } + + void OnResize(int width, int height) + { + int nw = contents.minSize.w.getPixels(width); + int nh = contents.minSize.h.getPixels(height); + contents.clientSize = { Max(nw, width), Max(nh, height) }; + if(contents.nodes) + { + contents.computeContentSize(displaySystem); + contents.autoLayout(); + } + Update(null); + } + + void OnRedraw(Surface surface) + { + contents.render(surface); + } +} diff --git a/autoLayout/autoLayout/wrapText.ec b/autoLayout/autoLayout/wrapText.ec new file mode 100644 index 0000000000..7cdb3e35a8 --- /dev/null +++ b/autoLayout/autoLayout/wrapText.ec @@ -0,0 +1,135 @@ +import IMPORT_STATIC "ecere" + +int wrapText(Surface surface, const String text, int sx, int sy, int ex, int ey) +{ + const String start = text; + const String drawUntil = null; + int w = 0; + int y = sy; + int tw, th; + int lh; + + surface.TextExtent("W", 1, &tw, &th); + lh = th; + surface.Clip({ sx, sy, ex, ey }); + + while(true) + { + bool canAddMore = false; + if(ey - y >= 2*th) + { + const String s = drawUntil ? drawUntil + 1 : start; + const String nextSpace = strchr(s, ' '); + const String newLine = strchr(s, '\n'); + if(newLine && (!nextSpace || (newLine < nextSpace))) + nextSpace = newLine; + if(!nextSpace) + nextSpace = strchr(s, 0); + if(nextSpace) + { + surface.TextExtent(drawUntil ? drawUntil : start, (int)(nextSpace - (drawUntil ? drawUntil : start)), &tw, &th); + if(!th) th = lh; + if(w + tw < ex - sx || !drawUntil) + { + drawUntil = nextSpace; + w += tw; + if(*nextSpace != 0 && *nextSpace != '\n') + canAddMore = true; + } + } + } + if(!canAddMore) + { + if(drawUntil) + { + surface.WriteText(sx, y, start, (int)(drawUntil - start)); + w = 0; + y += th; + if(!*drawUntil) + break; + start = drawUntil + 1; + drawUntil = null; + } + else + { + surface.WriteText(sx, y, start, strlen(start)); + y += th; + break; + } + } + } + + surface.Clip(null); + return y; +} + +int wrapTextExtent(DisplaySystem displaySystem, Font font, const String text, int ex, int ey, int * wtw, int * wth) +{ + const String start = text; + const String drawUntil = null; + int w = 0; + int y = 0; + int tw, th; + int lh; + + displaySystem.FontExtent(font, "W", 1, &tw, &th); + lh = th; + + *wtw = 0; + *wth = th; + + while(true) + { + bool canAddMore = false; + if(ey - y >= 2*th) + { + const String s = drawUntil ? drawUntil + 1 : start; + const String nextSpace = strchr(s, ' '); + const String newLine = strchr(s, '\n'); + if(newLine && (!nextSpace || (newLine < nextSpace))) + nextSpace = newLine; + if(!nextSpace) + nextSpace = strchr(s, 0); + if(nextSpace) + { + displaySystem.FontExtent(font, drawUntil ? drawUntil : start, (int)(nextSpace - (drawUntil ? drawUntil : start)), &tw, &th); + if(!th) th = lh; + if(w + tw < ex || !drawUntil) + { + drawUntil = nextSpace; + w += tw; + if(*nextSpace != 0 && *nextSpace != '\n') + canAddMore = true; + } + } + } + if(!canAddMore) + { + if(!drawUntil && !y && !w) + { + drawUntil = strchr(start, '\0'); + if(drawUntil) + displaySystem.FontExtent(font, start, (int)(drawUntil - start), &w, &th); + } + if(drawUntil) + { + *wtw = Max(*wtw, w); + *wth = Max(*wth, y + th); + w = 0; + y += th; + if(!*drawUntil) + break; + start = drawUntil + 1; + drawUntil = null; + } + else + { + *wtw = Max(*wtw, w); + *wth = Max(*wth, y + th); + y += th; + break; + } + } + } + return y; +} diff --git a/autoLayout/base.ec b/autoLayout/base.ec new file mode 100644 index 0000000000..101f143ef3 --- /dev/null +++ b/autoLayout/base.ec @@ -0,0 +1,46 @@ +import "autoLayout" + +class Title : Element { maxSize = { 1430, 0 }, font = { "Verdana", 60, bold = true }, fgColor = textColor; }; +class CCol : Col { hAlignment = center, vAlignment = center; }; +class BMBar : Bar { maxSize.h = 60; }; +class MBar : Bar { maxSize.h = 30; }; +define textColor = 0xFF2F3A3E; +class Header : Element { font = { "Verdana", 50, bold = true }; fgColor = textColor; } +class SmallHeader : Element { font = { "Verdana", 30, bold = true }; fgColor = textColor; } +class Bullet : Element +{ + fgColor = textColor; + property const String caption + { + set { text.caption = value; } + get { return text.caption; } + } + Element bullet { this, caption = "â–ª ", fgColor = 0xFF104A4A; }; + Col text { this, fgColor = textColor; }; +} + +class BaseSlide : CCol +{ + bitmap = { "graphics/bg1.png" }; + bitmapTint = { 41, white }; + bgColor = 0x59C6D2E3; + font = { "Verdana", 32 }; +} + +class CodeBlock : CCol +{ + selfHAlignment = center, selfVAlignment = center; + vAlignment = center; + maxSize = { }; + //bgColor = { 180, 0x094B55 }; + bgColor = { 220, black }; + font = { "Consolas", 30 }; + + // Since we don't have padding yet + CCol text { this, fgColor = lime /*white*/; maxSize = { 1.0, 1.0 }; /*margin = { 20, 20, 20, 20 }*/ }; + property const String caption + { + set { text.caption = value; } + get { return text.caption; } + } +} diff --git a/autoLayout/graphics/bg1.png b/autoLayout/graphics/bg1.png new file mode 100644 index 0000000000..c6167e5e2d Binary files /dev/null and b/autoLayout/graphics/bg1.png differ diff --git a/autoLayout/ryanStyles/Animation.ec b/autoLayout/ryanStyles/Animation.ec new file mode 100644 index 0000000000..3fe95a9ef7 --- /dev/null +++ b/autoLayout/ryanStyles/Animation.ec @@ -0,0 +1,54 @@ +import "ecere" +import "Style" + +enum TweenMode { + size, + background, + border, + padding, + margin, + position +}; + +class KeyFrame { +public: + Style style; + Array tween; + property Container tween { + set { + if(tween == null) { + tween = {}; + } + for(mode : value) { + tween.Add(mode); + } + } + } +}; + +class Timeline { + Map frames {}; +}; + +class Animation { + Timeline timeline; +}; + +void Test() { + Animation a { }; + a.timeline = { + frames = {[ + {0.0, { + style = { + background={color=red} + }, + tween=[background] + }}, + {1.0, { + style = { + background={color=blue} + } + }} + ]} + }; +} \ No newline at end of file diff --git a/autoLayout/ryanStyles/Button.ec b/autoLayout/ryanStyles/Button.ec new file mode 100644 index 0000000000..ca86164879 --- /dev/null +++ b/autoLayout/ryanStyles/Button.ec @@ -0,0 +1,59 @@ +import "ecere" +import "Layout" +import "Style" + +class LayoutButton : LayoutWindow { +public: + Style styleDown; + bool pressedDown; + Container + + +

NAME

+

runtests.pl - run one or more test cases

SYNOPSIS

+

runtests.pl [options] [test number] [!test number] [key word] [!key word]

DESCRIPTION

+

runtests.pl runs one, several or all the existing test cases in curl's test suite. It is often called from the root Makefile of the curl package with 'make test'.

TEST NUMBER

+

If no test case number is given, all existing tests that the script can find will be considered for running. You can specify single test cases to run, space-separated, like "1 3 5 7 11", and you can specify a range like "45 to 67". You can also specify only the tests you don't want to run by listing the numbers with a leading exclamation point, like "!66". +

It is also possible to specify tests to skip based on a key word describing the test. These are specified with a leading exclamation point and the key word or phrase, like "!HTTP NTLM auth". Likewise, tests to run can be specified simply by specifying the unadorned key words, like "FTPS". Remember that the exclamation marks and spaces will need to be quoted somehow when entered at many command shells.

OPTIONS

+

+

-a +

Continue running the rest of the test cases even if one test fails. By default, the test script stops as soon as an error is detected. +

-bN +

Use N as the base TCP/UDP port number on which to start the test servers. +

-c <curl> +

Provide a path to a custom curl binary to run the tests with. Default is the curl executable in the build tree. +

-d +

Enable protocol debug: have the servers display protocol output. +

-e +

Run the test event-based (if possible). This will make runtests invoke curl with --test-event option. This option only works if both curl and libcurl were built debug-enabled. +

-g +

Run the given test(s) with gdb. This is best used on a single test case and curl built --disable-shared. This then fires up gdb with command line set to run the specified test case. Simply (set a break-point and) type 'run' to start. +

-h +

Displays a help text about this program's command line options. +

-k +

Keep output and log files in log/ after a test run, even if no error was detected. Useful for debugging. +

-l +

Lists all test case names. +

-n +

Disable the check for and use of valgrind. +

-p +

Prints out all files in "log/" to stdout when a test case fails. Very practical when used in the automated and distributed tests since then the people checking the failures and the reasons for them might not have physical access to the machine and logs. +

-r +

Display run time statistics. (Requires Perl Time::HiRes module) +

-rf +

Display full run time statistics. (Requires Perl Time::HiRes module) +

-s +

Shorter output. Speaks less than default. +

-t[num] +

Selects a torture test for the given tests. This makes runtests.pl first run the tests once and count the number of memory allocations made. It then reruns the test that number of times, each time forcing one of the allocations to fail until all allocs have been tested. By setting num you can force the allocation with that number to be set to fail at once instead of looping through everyone, which is very handy when debugging and then often in combination with -g. +

-v +

Enable verbose output. Speaks more than default. +

-vc <curl> +

Provide a path to a custom curl binary to run when verifying that the servers running are indeed our test servers. Default is the curl executable in the build tree.

RUNNING TESTS

+

Many tests have conditions that must be met before the test case can run fine. They could depend on built-in features in libcurl or features present in the operating system or even in third-party libraries that curl may or may not use. +

The test script checks most of these by itself to determine when it is safe to attempt to run each test. Those which cannot be run due to failed requirements will simply be skipped and listed at the completion of all test cases. In some unusual configurations, the test script cannot make the correct determination for all tests. In these cases, the problematic tests can be skipped using the "!keyword" skip feature documented earlier.

WRITING TESTS

+

The simplest way to write test cases is to start with a similar existing test, save it with a new number and then adjust it to fit. There's an attempt to document the test case file format in the tests/FILEFORMAT.

+ This HTML page was made with roffit. + diff --git a/deps/curl-7.51.0/tests/runtests.pdf b/deps/curl-7.51.0/tests/runtests.pdf new file mode 100644 index 0000000000..5c074a4a31 Binary files /dev/null and b/deps/curl-7.51.0/tests/runtests.pdf differ diff --git a/deps/curl-7.51.0/tests/runtests.pl b/deps/curl-7.51.0/tests/runtests.pl new file mode 100644 index 0000000000..b8497f9c20 --- /dev/null +++ b/deps/curl-7.51.0/tests/runtests.pl @@ -0,0 +1,5400 @@ +#!/usr/bin/env perl +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### + +# Experimental hooks are available to run tests remotely on machines that +# are able to run curl but are unable to run the test harness. +# The following sections need to be modified: +# +# $HOSTIP, $HOST6IP - Set to the address of the host running the test suite +# $CLIENTIP, $CLIENT6IP - Set to the address of the host running curl +# runclient, runclientoutput - Modify to copy all the files in the log/ +# directory to the system running curl, run the given command remotely +# and save the return code or returned stdout (respectively), then +# copy all the files from the remote system's log/ directory back to +# the host running the test suite. This can be done a few ways, such +# as using scp & ssh, rsync & telnet, or using a NFS shared directory +# and ssh. +# +# 'make && make test' needs to be done on both machines before making the +# above changes and running runtests.pl manually. In the shared NFS case, +# the contents of the tests/server/ directory must be from the host +# running the test suite, while the rest must be from the host running curl. +# +# Note that even with these changes a number of tests will still fail (mainly +# to do with cookies, those that set environment variables, or those that +# do more than touch the file system in a or +# section). These can be added to the $TESTCASES line below, +# e.g. $TESTCASES="!8 !31 !63 !cookies..." +# +# Finally, to properly support -g and -n, checktestcmd needs to change +# to check the remote system's PATH, and the places in the code where +# the curl binary is read directly to determine its type also need to be +# fixed. As long as the -g option is never given, and the -n is always +# given, this won't be a problem. + + +# These should be the only variables that might be needed to get edited: + +BEGIN { + push(@INC, $ENV{'srcdir'}) if(defined $ENV{'srcdir'}); + push(@INC, "."); + # run time statistics needs Time::HiRes + eval { + no warnings "all"; + require Time::HiRes; + import Time::HiRes qw( time ); + } +} + +use strict; +use warnings; +use Cwd; + +# Subs imported from serverhelp module +use serverhelp qw( + serverfactors + servername_id + servername_str + servername_canon + server_pidfilename + server_logfilename + ); + +# Variables and subs imported from sshhelp module +use sshhelp qw( + $sshdexe + $sshexe + $sftpexe + $sshconfig + $sftpconfig + $sshdlog + $sshlog + $sftplog + $sftpcmds + display_sshdconfig + display_sshconfig + display_sftpconfig + display_sshdlog + display_sshlog + display_sftplog + exe_ext + find_sshd + find_ssh + find_sftp + find_httptlssrv + sshversioninfo + ); + +use pathhelp; + +require "getpart.pm"; # array functions +require "valgrind.pm"; # valgrind report parser +require "ftp.pm"; + +my $HOSTIP="127.0.0.1"; # address on which the test server listens +my $HOST6IP="[::1]"; # address on which the test server listens +my $CLIENTIP="127.0.0.1"; # address which curl uses for incoming connections +my $CLIENT6IP="[::1]"; # address which curl uses for incoming connections + +my $base = 8990; # base port number + +my $HTTPPORT; # HTTP server port +my $HTTP6PORT; # HTTP IPv6 server port +my $HTTPSPORT; # HTTPS (stunnel) server port +my $FTPPORT; # FTP server port +my $FTP2PORT; # FTP server 2 port +my $FTPSPORT; # FTPS (stunnel) server port +my $FTP6PORT; # FTP IPv6 server port +my $TFTPPORT; # TFTP +my $TFTP6PORT; # TFTP +my $SSHPORT; # SCP/SFTP +my $SOCKSPORT; # SOCKS4/5 port +my $POP3PORT; # POP3 +my $POP36PORT; # POP3 IPv6 server port +my $IMAPPORT; # IMAP +my $IMAP6PORT; # IMAP IPv6 server port +my $SMTPPORT; # SMTP +my $SMTP6PORT; # SMTP IPv6 server port +my $RTSPPORT; # RTSP +my $RTSP6PORT; # RTSP IPv6 server port +my $GOPHERPORT; # Gopher +my $GOPHER6PORT; # Gopher IPv6 server port +my $HTTPTLSPORT; # HTTP TLS (non-stunnel) server port +my $HTTPTLS6PORT; # HTTP TLS (non-stunnel) IPv6 server port +my $HTTPPROXYPORT; # HTTP proxy port, when using CONNECT +my $HTTPPIPEPORT; # HTTP pipelining port +my $HTTPUNIXPATH; # HTTP server Unix domain socket path +my $HTTP2PORT; # HTTP/2 server port + +my $srcdir = $ENV{'srcdir'} || '.'; +my $CURL="../src/curl".exe_ext(); # what curl executable to run on the tests +my $VCURL=$CURL; # what curl binary to use to verify the servers with + # VCURL is handy to set to the system one when the one you + # just built hangs or crashes and thus prevent verification +my $DBGCURL=$CURL; #"../src/.libs/curl"; # alternative for debugging +my $LOGDIR="log"; +my $TESTDIR="$srcdir/data"; +my $LIBDIR="./libtest"; +my $UNITDIR="./unit"; +# TODO: change this to use server_inputfilename() +my $SERVERIN="$LOGDIR/server.input"; # what curl sent the server +my $SERVER2IN="$LOGDIR/server2.input"; # what curl sent the second server +my $PROXYIN="$LOGDIR/proxy.input"; # what curl sent the proxy +my $CURLLOG="$LOGDIR/curl.log"; # all command lines run +my $FTPDCMD="$LOGDIR/ftpserver.cmd"; # copy ftp server instructions here +my $SERVERLOGS_LOCK="$LOGDIR/serverlogs.lock"; # server logs advisor read lock +my $CURLCONFIG="../curl-config"; # curl-config from current build + +# Normally, all test cases should be run, but at times it is handy to +# simply run a particular one: +my $TESTCASES="all"; + +# To run specific test cases, set them like: +# $TESTCASES="1 2 3 7 8"; + +####################################################################### +# No variables below this point should need to be modified +# + +# invoke perl like this: +my $perl="perl -I$srcdir"; +my $server_response_maxtime=13; + +my $debug_build=0; # built debug enabled (--enable-debug) +my $has_memory_tracking=0; # built with memory tracking (--enable-curldebug) +my $libtool; + +# name of the file that the memory debugging creates: +my $memdump="$LOGDIR/memdump"; + +# the path to the script that analyzes the memory debug output file: +my $memanalyze="$perl $srcdir/memanalyze.pl"; + +my $pwd = getcwd(); # current working directory + +my $start; +my $ftpchecktime=1; # time it took to verify our test FTP server + +my $stunnel = checkcmd("stunnel4") || checkcmd("tstunnel") || checkcmd("stunnel"); +my $valgrind = checktestcmd("valgrind"); +my $valgrind_logfile="--logfile"; +my $valgrind_tool; +my $gdb = checktestcmd("gdb"); +my $httptlssrv = find_httptlssrv(); + +my $has_ssl; # set if libcurl is built with SSL support +my $has_largefile; # set if libcurl is built with large file support +my $has_idn; # set if libcurl is built with IDN support +my $http_ipv6; # set if HTTP server has IPv6 support +my $http_unix; # set if HTTP server has Unix sockets support +my $ftp_ipv6; # set if FTP server has IPv6 support +my $tftp_ipv6; # set if TFTP server has IPv6 support +my $gopher_ipv6; # set if Gopher server has IPv6 support +my $has_ipv6; # set if libcurl is built with IPv6 support +my $has_unix; # set if libcurl is built with Unix sockets support +my $has_libz; # set if libcurl is built with libz support +my $has_getrlimit; # set if system has getrlimit() +my $has_ntlm; # set if libcurl is built with NTLM support +my $has_ntlm_wb; # set if libcurl is built with NTLM delegation to winbind +my $has_sspi; # set if libcurl is built with Windows SSPI +my $has_gssapi; # set if libcurl is built with a GSS-API library +my $has_kerberos; # set if libcurl is built with Kerberos support +my $has_spnego; # set if libcurl is built with SPNEGO support +my $has_charconv; # set if libcurl is built with CharConv support +my $has_tls_srp; # set if libcurl is built with TLS-SRP support +my $has_metalink; # set if curl is built with Metalink support +my $has_http2; # set if libcurl is built with HTTP2 support +my $has_crypto; # set if libcurl is built with cryptographic support +my $has_cares; # set if built with c-ares +my $has_threadedres;# set if built with threaded resolver +my $has_psl; # set if libcurl is built with PSL support + +# this version is decided by the particular nghttp2 library that is being used +my $h2cver = "h2c"; + +my $has_openssl; # built with a lib using an OpenSSL-like API +my $has_gnutls; # built with GnuTLS +my $has_nss; # built with NSS +my $has_yassl; # built with yassl +my $has_polarssl; # built with polarssl +my $has_axtls; # built with axTLS +my $has_winssl; # built with WinSSL (Secure Channel aka Schannel) +my $has_darwinssl; # built with DarwinSSL (Secure Transport) +my $has_boringssl; # built with BoringSSL +my $has_libressl; # built with libressl +my $has_mbedtls; # built with mbedTLS + +my $has_sslpinning; # built with a TLS backend that supports pinning + +my $has_shared = "unknown"; # built shared + +my $resolver; # name of the resolver backend (for human presentation) +my $ssllib; # name of the SSL library we use (for human presentation) + +my $has_textaware; # set if running on a system that has a text mode concept + # on files. Windows for example + +my @protocols; # array of lowercase supported protocol servers + +my $skipped=0; # number of tests skipped; reported in main loop +my %skipped; # skipped{reason}=counter, reasons for skip +my @teststat; # teststat[testnum]=reason, reasons for skip +my %disabled_keywords; # key words of tests to skip +my %enabled_keywords; # key words of tests to run +my %disabled; # disabled test cases + +my $sshdid; # for socks server, ssh daemon version id +my $sshdvernum; # for socks server, ssh daemon version number +my $sshdverstr; # for socks server, ssh daemon version string +my $sshderror; # for socks server, ssh daemon version error + +my $defserverlogslocktimeout = 20; # timeout to await server logs lock removal +my $defpostcommanddelay = 0; # delay between command and postcheck sections + +my $timestats; # time stamping and stats generation +my $fullstats; # show time stats for every single test +my %timeprepini; # timestamp for each test preparation start +my %timesrvrini; # timestamp for each test required servers verification start +my %timesrvrend; # timestamp for each test required servers verification end +my %timetoolini; # timestamp for each test command run starting +my %timetoolend; # timestamp for each test command run stopping +my %timesrvrlog; # timestamp for each test server logs lock removal +my %timevrfyend; # timestamp for each test result verification end + +my $testnumcheck; # test number, set in singletest sub. +my %oldenv; + +####################################################################### +# variables that command line options may set +# + +my $short; +my $automakestyle; +my $verbose; +my $debugprotocol; +my $anyway; +my $gdbthis; # run test case with gdb debugger +my $gdbxwin; # use windowed gdb when using gdb +my $keepoutfiles; # keep stdout and stderr files after tests +my $listonly; # only list the tests +my $postmortem; # display detailed info about failed tests +my $run_event_based; # run curl with --test-event to test the event API + +my %run; # running server +my %doesntrun; # servers that don't work, identified by pidfile +my %serverpidfile;# all server pid file names, identified by server id +my %runcert; # cert file currently in use by an ssl running server + +# torture test variables +my $torture; +my $tortnum; +my $tortalloc; + +####################################################################### +# logmsg is our general message logging subroutine. +# +sub logmsg { + for(@_) { + print "$_"; + } +} + +# get the name of the current user +my $USER = $ENV{USER}; # Linux +if (!$USER) { + $USER = $ENV{USERNAME}; # Windows + if (!$USER) { + $USER = $ENV{LOGNAME}; # Some Unix (I think) + } +} + +# enable memory debugging if curl is compiled with it +$ENV{'CURL_MEMDEBUG'} = $memdump; +$ENV{'CURL_ENTROPY'}="12345678"; +$ENV{'CURL_FORCETIME'}=1; # for debug NTLM magic +$ENV{'HOME'}=$pwd; + +sub catch_zap { + my $signame = shift; + logmsg "runtests.pl received SIG$signame, exiting\n"; + stopservers($verbose); + die "Somebody sent me a SIG$signame"; +} +$SIG{INT} = \&catch_zap; +$SIG{TERM} = \&catch_zap; + +########################################################################## +# Clear all possible '*_proxy' environment variables for various protocols +# to prevent them to interfere with our testing! + +my $protocol; +foreach $protocol (('ftp', 'http', 'ftps', 'https', 'no', 'all')) { + my $proxy = "${protocol}_proxy"; + # clear lowercase version + delete $ENV{$proxy} if($ENV{$proxy}); + # clear uppercase version + delete $ENV{uc($proxy)} if($ENV{uc($proxy)}); +} + +# make sure we don't get affected by other variables that control our +# behaviour + +delete $ENV{'SSL_CERT_DIR'} if($ENV{'SSL_CERT_DIR'}); +delete $ENV{'SSL_CERT_PATH'} if($ENV{'SSL_CERT_PATH'}); +delete $ENV{'CURL_CA_BUNDLE'} if($ENV{'CURL_CA_BUNDLE'}); + +####################################################################### +# Load serverpidfile hash with pidfile names for all possible servers. +# +sub init_serverpidfile_hash { + for my $proto (('ftp', 'http', 'imap', 'pop3', 'smtp', 'http/2')) { + for my $ssl (('', 's')) { + for my $ipvnum ((4, 6)) { + for my $idnum ((1, 2, 3)) { + my $serv = servername_id("$proto$ssl", $ipvnum, $idnum); + my $pidf = server_pidfilename("$proto$ssl", $ipvnum, $idnum); + $serverpidfile{$serv} = $pidf; + } + } + } + } + for my $proto (('tftp', 'sftp', 'socks', 'ssh', 'rtsp', 'gopher', 'httptls')) { + for my $ipvnum ((4, 6)) { + for my $idnum ((1, 2)) { + my $serv = servername_id($proto, $ipvnum, $idnum); + my $pidf = server_pidfilename($proto, $ipvnum, $idnum); + $serverpidfile{$serv} = $pidf; + } + } + } + for my $proto (('http', 'imap', 'pop3', 'smtp', 'http/2')) { + for my $ssl (('', 's')) { + my $serv = servername_id("$proto$ssl", "unix", 1); + my $pidf = server_pidfilename("$proto$ssl", "unix", 1); + $serverpidfile{$serv} = $pidf; + } + } +} + +####################################################################### +# Check if a given child process has just died. Reaps it if so. +# +sub checkdied { + use POSIX ":sys_wait_h"; + my $pid = $_[0]; + if((not defined $pid) || $pid <= 0) { + return 0; + } + my $rc = waitpid($pid, &WNOHANG); + return ($rc == $pid)?1:0; +} + +####################################################################### +# Start a new thread/process and run the given command line in there. +# Return the pids (yes plural) of the new child process to the parent. +# +sub startnew { + my ($cmd, $pidfile, $timeout, $fake)=@_; + + logmsg "startnew: $cmd\n" if ($verbose); + + my $child = fork(); + my $pid2 = 0; + + if(not defined $child) { + logmsg "startnew: fork() failure detected\n"; + return (-1,-1); + } + + if(0 == $child) { + # Here we are the child. Run the given command. + + # Put an "exec" in front of the command so that the child process + # keeps this child's process ID. + exec("exec $cmd") || die "Can't exec() $cmd: $!"; + + # exec() should never return back here to this process. We protect + # ourselves by calling die() just in case something goes really bad. + die "error: exec() has returned"; + } + + # Ugly hack but ssh client and gnutls-serv don't support pid files + if ($fake) { + if(open(OUT, ">$pidfile")) { + print OUT $child . "\n"; + close(OUT); + logmsg "startnew: $pidfile faked with pid=$child\n" if($verbose); + } + else { + logmsg "startnew: failed to write fake $pidfile with pid=$child\n"; + } + # could/should do a while connect fails sleep a bit and loop + sleep $timeout; + if (checkdied($child)) { + logmsg "startnew: child process has failed to start\n" if($verbose); + return (-1,-1); + } + } + + my $count = $timeout; + while($count--) { + if(-f $pidfile && -s $pidfile && open(PID, "<$pidfile")) { + $pid2 = 0 + ; + close(PID); + if(($pid2 > 0) && pidexists($pid2)) { + # if $pid2 is valid, then make sure this pid is alive, as + # otherwise it is just likely to be the _previous_ pidfile or + # similar! + last; + } + # invalidate $pid2 if not actually alive + $pid2 = 0; + } + if (checkdied($child)) { + logmsg "startnew: child process has died, server might start up\n" + if($verbose); + # We can't just abort waiting for the server with a + # return (-1,-1); + # because the server might have forked and could still start + # up normally. Instead, just reduce the amount of time we remain + # waiting. + $count >>= 2; + } + sleep(1); + } + + # Return two PIDs, the one for the child process we spawned and the one + # reported by the server itself (in case it forked again on its own). + # Both (potentially) need to be killed at the end of the test. + return ($child, $pid2); +} + + +####################################################################### +# Check for a command in the PATH of the test server. +# +sub checkcmd { + my ($cmd)=@_; + my @paths=(split(":", $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", + "/sbin", "/usr/bin", "/usr/local/bin", + "./libtest/.libs", "./libtest"); + for(@paths) { + if( -x "$_/$cmd" && ! -d "$_/$cmd") { + # executable bit but not a directory! + return "$_/$cmd"; + } + } +} + +####################################################################### +# Get the list of tests that the tests/data/Makefile.am knows about! +# +my $disttests; +sub get_disttests { + my @dist = `cd data && make show`; + $disttests = join("", @dist); +} + +####################################################################### +# Check for a command in the PATH of the machine running curl. +# +sub checktestcmd { + my ($cmd)=@_; + return checkcmd($cmd); +} + +####################################################################### +# Run the application under test and return its return code +# +sub runclient { + my ($cmd)=@_; + my $ret = system($cmd); + print "CMD ($ret): $cmd\n" if($verbose && !$torture); + return $ret; + +# This is one way to test curl on a remote machine +# my $out = system("ssh $CLIENTIP cd \'$pwd\' \\; \'$cmd\'"); +# sleep 2; # time to allow the NFS server to be updated +# return $out; +} + +####################################################################### +# Run the application under test and return its stdout +# +sub runclientoutput { + my ($cmd)=@_; + return `$cmd`; + +# This is one way to test curl on a remote machine +# my @out = `ssh $CLIENTIP cd \'$pwd\' \\; \'$cmd\'`; +# sleep 2; # time to allow the NFS server to be updated +# return @out; + } + +####################################################################### +# Memory allocation test and failure torture testing. +# +sub torture { + my $testcmd = shift; + my $gdbline = shift; + + # remove memdump first to be sure we get a new nice and clean one + unlink($memdump); + + # First get URL from test server, ignore the output/result + runclient($testcmd); + + logmsg " CMD: $testcmd\n" if($verbose); + + # memanalyze -v is our friend, get the number of allocations made + my $count=0; + my @out = `$memanalyze -v $memdump`; + for(@out) { + if(/^Allocations: (\d+)/) { + $count = $1; + last; + } + } + if(!$count) { + logmsg " found no allocs to make fail\n"; + return 0; + } + + logmsg " $count allocations to make fail\n"; + + for ( 1 .. $count ) { + my $limit = $_; + my $fail; + my $dumped_core; + + if($tortalloc && ($tortalloc != $limit)) { + next; + } + + if($verbose) { + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = + localtime(time()); + my $now = sprintf("%02d:%02d:%02d ", $hour, $min, $sec); + logmsg "Fail alloc no: $limit at $now\r"; + } + + # make the memory allocation function number $limit return failure + $ENV{'CURL_MEMLIMIT'} = $limit; + + # remove memdump first to be sure we get a new nice and clean one + unlink($memdump); + + logmsg "*** Alloc number $limit is now set to fail ***\n" if($gdbthis); + + my $ret = 0; + if($gdbthis) { + runclient($gdbline); + } + else { + $ret = runclient($testcmd); + } + #logmsg "$_ Returned " . ($ret >> 8) . "\n"; + + # Now clear the variable again + delete $ENV{'CURL_MEMLIMIT'} if($ENV{'CURL_MEMLIMIT'}); + + if(-r "core") { + # there's core file present now! + logmsg " core dumped\n"; + $dumped_core = 1; + $fail = 2; + } + + # verify that it returns a proper error code, doesn't leak memory + # and doesn't core dump + if(($ret & 255) || ($ret >> 8) >= 128) { + logmsg " system() returned $ret\n"; + $fail=1; + } + else { + my @memdata=`$memanalyze $memdump`; + my $leak=0; + for(@memdata) { + if($_ ne "") { + # well it could be other memory problems as well, but + # we call it leak for short here + $leak=1; + } + } + if($leak) { + logmsg "** MEMORY FAILURE\n"; + logmsg @memdata; + logmsg `$memanalyze -l $memdump`; + $fail = 1; + } + } + if($fail) { + logmsg " Failed on alloc number $limit in test.\n", + " invoke with \"-t$limit\" to repeat this single case.\n"; + stopservers($verbose); + return 1; + } + } + + logmsg "torture OK\n"; + return 0; +} + +####################################################################### +# Stop a test server along with pids which aren't in the %run hash yet. +# This also stops all servers which are relative to the given one. +# +sub stopserver { + my ($server, $pidlist) = @_; + # + # kill sockfilter processes for pingpong relative server + # + if($server =~ /^(ftp|imap|pop3|smtp)s?(\d*)(-ipv6|)$/) { + my $proto = $1; + my $idnum = ($2 && ($2 > 1)) ? $2 : 1; + my $ipvnum = ($3 && ($3 =~ /6$/)) ? 6 : 4; + killsockfilters($proto, $ipvnum, $idnum, $verbose); + } + # + # All servers relative to the given one must be stopped also + # + my @killservers; + if($server =~ /^(ftp|http|imap|pop3|smtp|httppipe)s((\d*)(-ipv6|-unix|))$/) { + # given a stunnel based ssl server, also kill non-ssl underlying one + push @killservers, "${1}${2}"; + } + elsif($server =~ /^(ftp|http|imap|pop3|smtp|httppipe)((\d*)(-ipv6|-unix|))$/) { + # given a non-ssl server, also kill stunnel based ssl piggybacking one + push @killservers, "${1}s${2}"; + } + elsif($server =~ /^(socks)((\d*)(-ipv6|))$/) { + # given a socks server, also kill ssh underlying one + push @killservers, "ssh${2}"; + } + elsif($server =~ /^(ssh)((\d*)(-ipv6|))$/) { + # given a ssh server, also kill socks piggybacking one + push @killservers, "socks${2}"; + } + push @killservers, $server; + # + # kill given pids and server relative ones clearing them in %run hash + # + foreach my $server (@killservers) { + if($run{$server}) { + # we must prepend a space since $pidlist may already contain a pid + $pidlist .= " $run{$server}"; + $run{$server} = 0; + } + $runcert{$server} = 0 if($runcert{$server}); + } + killpid($verbose, $pidlist); + # + # cleanup server pid files + # + foreach my $server (@killservers) { + my $pidfile = $serverpidfile{$server}; + my $pid = processexists($pidfile); + if($pid > 0) { + logmsg "Warning: $server server unexpectedly alive\n"; + killpid($verbose, $pid); + } + unlink($pidfile) if(-f $pidfile); + } +} + +####################################################################### +# Verify that the server that runs on $ip, $port is our server. This also +# implies that we can speak with it, as there might be occasions when the +# server runs fine but we cannot talk to it ("Failed to connect to ::1: Can't +# assign requested address") +# +sub verifyhttp { + my ($proto, $ipvnum, $idnum, $ip, $port_or_path) = @_; + my $server = servername_id($proto, $ipvnum, $idnum); + my $pid = 0; + my $bonus=""; + # $port_or_path contains a path for Unix sockets, sws ignores the port + my $port = ($ipvnum eq "unix") ? 80 : $port_or_path; + + my $verifyout = "$LOGDIR/". + servername_canon($proto, $ipvnum, $idnum) .'_verify.out'; + unlink($verifyout) if(-f $verifyout); + + my $verifylog = "$LOGDIR/". + servername_canon($proto, $ipvnum, $idnum) .'_verify.log'; + unlink($verifylog) if(-f $verifylog); + + if($proto eq "gopher") { + # gopher is funny + $bonus="1/"; + } + + my $flags = "--max-time $server_response_maxtime "; + $flags .= "--output $verifyout "; + $flags .= "--silent "; + $flags .= "--verbose "; + $flags .= "--globoff "; + $flags .= "--unix-socket '$port_or_path' " if $ipvnum eq "unix"; + $flags .= "-1 " if($has_axtls); + $flags .= "--insecure " if($proto eq 'https'); + $flags .= "\"$proto://$ip:$port/${bonus}verifiedserver\""; + + my $cmd = "$VCURL $flags 2>$verifylog"; + + # verify if our/any server is running on this port + logmsg "RUN: $cmd\n" if($verbose); + my $res = runclient($cmd); + + $res >>= 8; # rotate the result + if($res & 128) { + logmsg "RUN: curl command died with a coredump\n"; + return -1; + } + + if($res && $verbose) { + logmsg "RUN: curl command returned $res\n"; + if(open(FILE, "<$verifylog")) { + while(my $string = ) { + logmsg "RUN: $string" if($string !~ /^([ \t]*)$/); + } + close(FILE); + } + } + + my $data; + if(open(FILE, "<$verifyout")) { + while(my $string = ) { + $data = $string; + last; # only want first line + } + close(FILE); + } + + if($data && ($data =~ /WE ROOLZ: (\d+)/)) { + $pid = 0+$1; + } + elsif($res == 6) { + # curl: (6) Couldn't resolve host '::1' + logmsg "RUN: failed to resolve host ($proto://$ip:$port/verifiedserver)\n"; + return -1; + } + elsif($data || ($res && ($res != 7))) { + logmsg "RUN: Unknown server on our $server port: $port ($res)\n"; + return -1; + } + return $pid; +} + +####################################################################### +# Verify that the server that runs on $ip, $port is our server. This also +# implies that we can speak with it, as there might be occasions when the +# server runs fine but we cannot talk to it ("Failed to connect to ::1: Can't +# assign requested address") +# +sub verifyftp { + my ($proto, $ipvnum, $idnum, $ip, $port) = @_; + my $server = servername_id($proto, $ipvnum, $idnum); + my $pid = 0; + my $time=time(); + my $extra=""; + + my $verifylog = "$LOGDIR/". + servername_canon($proto, $ipvnum, $idnum) .'_verify.log'; + unlink($verifylog) if(-f $verifylog); + + if($proto eq "ftps") { + $extra .= "--insecure --ftp-ssl-control "; + } + + my $flags = "--max-time $server_response_maxtime "; + $flags .= "--silent "; + $flags .= "--verbose "; + $flags .= "--globoff "; + $flags .= $extra; + $flags .= "\"$proto://$ip:$port/verifiedserver\""; + + my $cmd = "$VCURL $flags 2>$verifylog"; + + # check if this is our server running on this port: + logmsg "RUN: $cmd\n" if($verbose); + my @data = runclientoutput($cmd); + + my $res = $? >> 8; # rotate the result + if($res & 128) { + logmsg "RUN: curl command died with a coredump\n"; + return -1; + } + + foreach my $line (@data) { + if($line =~ /WE ROOLZ: (\d+)/) { + # this is our test server with a known pid! + $pid = 0+$1; + last; + } + } + if($pid <= 0 && @data && $data[0]) { + # this is not a known server + logmsg "RUN: Unknown server on our $server port: $port\n"; + return 0; + } + # we can/should use the time it took to verify the FTP server as a measure + # on how fast/slow this host/FTP is. + my $took = int(0.5+time()-$time); + + if($verbose) { + logmsg "RUN: Verifying our test $server server took $took seconds\n"; + } + $ftpchecktime = $took>=1?$took:1; # make sure it never is below 1 + + return $pid; +} + +####################################################################### +# Verify that the server that runs on $ip, $port is our server. This also +# implies that we can speak with it, as there might be occasions when the +# server runs fine but we cannot talk to it ("Failed to connect to ::1: Can't +# assign requested address") +# +sub verifyrtsp { + my ($proto, $ipvnum, $idnum, $ip, $port) = @_; + my $server = servername_id($proto, $ipvnum, $idnum); + my $pid = 0; + + my $verifyout = "$LOGDIR/". + servername_canon($proto, $ipvnum, $idnum) .'_verify.out'; + unlink($verifyout) if(-f $verifyout); + + my $verifylog = "$LOGDIR/". + servername_canon($proto, $ipvnum, $idnum) .'_verify.log'; + unlink($verifylog) if(-f $verifylog); + + my $flags = "--max-time $server_response_maxtime "; + $flags .= "--output $verifyout "; + $flags .= "--silent "; + $flags .= "--verbose "; + $flags .= "--globoff "; + # currently verification is done using http + $flags .= "\"http://$ip:$port/verifiedserver\""; + + my $cmd = "$VCURL $flags 2>$verifylog"; + + # verify if our/any server is running on this port + logmsg "RUN: $cmd\n" if($verbose); + my $res = runclient($cmd); + + $res >>= 8; # rotate the result + if($res & 128) { + logmsg "RUN: curl command died with a coredump\n"; + return -1; + } + + if($res && $verbose) { + logmsg "RUN: curl command returned $res\n"; + if(open(FILE, "<$verifylog")) { + while(my $string = ) { + logmsg "RUN: $string" if($string !~ /^([ \t]*)$/); + } + close(FILE); + } + } + + my $data; + if(open(FILE, "<$verifyout")) { + while(my $string = ) { + $data = $string; + last; # only want first line + } + close(FILE); + } + + if($data && ($data =~ /RTSP_SERVER WE ROOLZ: (\d+)/)) { + $pid = 0+$1; + } + elsif($res == 6) { + # curl: (6) Couldn't resolve host '::1' + logmsg "RUN: failed to resolve host ($proto://$ip:$port/verifiedserver)\n"; + return -1; + } + elsif($data || ($res != 7)) { + logmsg "RUN: Unknown server on our $server port: $port\n"; + return -1; + } + return $pid; +} + +####################################################################### +# Verify that the ssh server has written out its pidfile, recovering +# the pid from the file and returning it if a process with that pid is +# actually alive. +# +sub verifyssh { + my ($proto, $ipvnum, $idnum, $ip, $port) = @_; + my $server = servername_id($proto, $ipvnum, $idnum); + my $pidfile = server_pidfilename($proto, $ipvnum, $idnum); + my $pid = 0; + if(open(FILE, "<$pidfile")) { + $pid=0+; + close(FILE); + } + if($pid > 0) { + # if we have a pid it is actually our ssh server, + # since runsshserver() unlinks previous pidfile + if(!pidexists($pid)) { + logmsg "RUN: SSH server has died after starting up\n"; + checkdied($pid); + unlink($pidfile); + $pid = -1; + } + } + return $pid; +} + +####################################################################### +# Verify that we can connect to the sftp server, properly authenticate +# with generated config and key files and run a simple remote pwd. +# +sub verifysftp { + my ($proto, $ipvnum, $idnum, $ip, $port) = @_; + my $server = servername_id($proto, $ipvnum, $idnum); + my $verified = 0; + # Find out sftp client canonical file name + my $sftp = find_sftp(); + if(!$sftp) { + logmsg "RUN: SFTP server cannot find $sftpexe\n"; + return -1; + } + # Find out ssh client canonical file name + my $ssh = find_ssh(); + if(!$ssh) { + logmsg "RUN: SFTP server cannot find $sshexe\n"; + return -1; + } + # Connect to sftp server, authenticate and run a remote pwd + # command using our generated configuration and key files + my $cmd = "\"$sftp\" -b $sftpcmds -F $sftpconfig -S \"$ssh\" $ip > $sftplog 2>&1"; + my $res = runclient($cmd); + # Search for pwd command response in log file + if(open(SFTPLOGFILE, "<$sftplog")) { + while() { + if(/^Remote working directory: /) { + $verified = 1; + last; + } + } + close(SFTPLOGFILE); + } + return $verified; +} + +####################################################################### +# Verify that the non-stunnel HTTP TLS extensions capable server that runs +# on $ip, $port is our server. This also implies that we can speak with it, +# as there might be occasions when the server runs fine but we cannot talk +# to it ("Failed to connect to ::1: Can't assign requested address") +# +sub verifyhttptls { + my ($proto, $ipvnum, $idnum, $ip, $port) = @_; + my $server = servername_id($proto, $ipvnum, $idnum); + my $pidfile = server_pidfilename($proto, $ipvnum, $idnum); + my $pid = 0; + + my $verifyout = "$LOGDIR/". + servername_canon($proto, $ipvnum, $idnum) .'_verify.out'; + unlink($verifyout) if(-f $verifyout); + + my $verifylog = "$LOGDIR/". + servername_canon($proto, $ipvnum, $idnum) .'_verify.log'; + unlink($verifylog) if(-f $verifylog); + + my $flags = "--max-time $server_response_maxtime "; + $flags .= "--output $verifyout "; + $flags .= "--verbose "; + $flags .= "--globoff "; + $flags .= "--insecure "; + $flags .= "--tlsauthtype SRP "; + $flags .= "--tlsuser jsmith "; + $flags .= "--tlspassword abc "; + $flags .= "\"https://$ip:$port/verifiedserver\""; + + my $cmd = "$VCURL $flags 2>$verifylog"; + + # verify if our/any server is running on this port + logmsg "RUN: $cmd\n" if($verbose); + my $res = runclient($cmd); + + $res >>= 8; # rotate the result + if($res & 128) { + logmsg "RUN: curl command died with a coredump\n"; + return -1; + } + + if($res && $verbose) { + logmsg "RUN: curl command returned $res\n"; + if(open(FILE, "<$verifylog")) { + while(my $string = ) { + logmsg "RUN: $string" if($string !~ /^([ \t]*)$/); + } + close(FILE); + } + } + + my $data; + if(open(FILE, "<$verifyout")) { + while(my $string = ) { + $data .= $string; + } + close(FILE); + } + + if($data && ($data =~ /(GNUTLS|GnuTLS)/) && open(FILE, "<$pidfile")) { + $pid=0+; + close(FILE); + if($pid > 0) { + # if we have a pid it is actually our httptls server, + # since runhttptlsserver() unlinks previous pidfile + if(!pidexists($pid)) { + logmsg "RUN: $server server has died after starting up\n"; + checkdied($pid); + unlink($pidfile); + $pid = -1; + } + } + return $pid; + } + elsif($res == 6) { + # curl: (6) Couldn't resolve host '::1' + logmsg "RUN: failed to resolve host (https://$ip:$port/verifiedserver)\n"; + return -1; + } + elsif($data || ($res && ($res != 7))) { + logmsg "RUN: Unknown server on our $server port: $port ($res)\n"; + return -1; + } + return $pid; +} + +####################################################################### +# STUB for verifying socks +# +sub verifysocks { + my ($proto, $ipvnum, $idnum, $ip, $port) = @_; + my $server = servername_id($proto, $ipvnum, $idnum); + my $pidfile = server_pidfilename($proto, $ipvnum, $idnum); + my $pid = 0; + if(open(FILE, "<$pidfile")) { + $pid=0+; + close(FILE); + } + if($pid > 0) { + # if we have a pid it is actually our socks server, + # since runsocksserver() unlinks previous pidfile + if(!pidexists($pid)) { + logmsg "RUN: SOCKS server has died after starting up\n"; + checkdied($pid); + unlink($pidfile); + $pid = -1; + } + } + return $pid; +} + +####################################################################### +# Verify that the server that runs on $ip, $port is our server. +# Retry over several seconds before giving up. The ssh server in +# particular can take a long time to start if it needs to generate +# keys on a slow or loaded host. +# +# Just for convenience, test harness uses 'https' and 'httptls' literals +# as values for 'proto' variable in order to differentiate different +# servers. 'https' literal is used for stunnel based https test servers, +# and 'httptls' is used for non-stunnel https test servers. +# + +my %protofunc = ('http' => \&verifyhttp, + 'https' => \&verifyhttp, + 'rtsp' => \&verifyrtsp, + 'ftp' => \&verifyftp, + 'pop3' => \&verifyftp, + 'imap' => \&verifyftp, + 'smtp' => \&verifyftp, + 'httppipe' => \&verifyhttp, + 'ftps' => \&verifyftp, + 'tftp' => \&verifyftp, + 'ssh' => \&verifyssh, + 'socks' => \&verifysocks, + 'gopher' => \&verifyhttp, + 'httptls' => \&verifyhttptls); + +sub verifyserver { + my ($proto, $ipvnum, $idnum, $ip, $port) = @_; + + my $count = 30; # try for this many seconds + my $pid; + + while($count--) { + my $fun = $protofunc{$proto}; + + $pid = &$fun($proto, $ipvnum, $idnum, $ip, $port); + + if($pid > 0) { + last; + } + elsif($pid < 0) { + # a real failure, stop trying and bail out + return 0; + } + sleep(1); + } + return $pid; +} + +####################################################################### +# Single shot server responsiveness test. This should only be used +# to verify that a server present in %run hash is still functional +# +sub responsiveserver { + my ($proto, $ipvnum, $idnum, $ip, $port) = @_; + my $prev_verbose = $verbose; + + $verbose = 0; + my $fun = $protofunc{$proto}; + my $pid = &$fun($proto, $ipvnum, $idnum, $ip, $port); + $verbose = $prev_verbose; + + if($pid > 0) { + return 1; # responsive + } + + my $srvrname = servername_str($proto, $ipvnum, $idnum); + logmsg " server precheck FAILED (unresponsive $srvrname server)\n"; + return 0; +} + +####################################################################### +# start the http2 server +# +sub runhttp2server { + my ($verbose, $port) = @_; + my $server; + my $srvrname; + my $pidfile; + my $logfile; + my $flags = ""; + my $proto="http/2"; + my $ipvnum = 4; + my $idnum = 0; + my $exe = "$perl $srcdir/http2-server.pl"; + my $verbose_flag = "--verbose "; + + $server = servername_id($proto, $ipvnum, $idnum); + + $pidfile = $serverpidfile{$server}; + + # don't retry if the server doesn't work + if ($doesntrun{$pidfile}) { + return (0,0); + } + + my $pid = processexists($pidfile); + if($pid > 0) { + stopserver($server, "$pid"); + } + unlink($pidfile) if(-f $pidfile); + + $srvrname = servername_str($proto, $ipvnum, $idnum); + + $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + + $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" "; + $flags .= "--port $HTTP2PORT "; + $flags .= $verbose_flag if($debugprotocol); + + my $cmd = "$exe $flags"; + my ($http2pid, $pid2) = startnew($cmd, $pidfile, 15, 0); + + if($http2pid <= 0 || !pidexists($http2pid)) { + # it is NOT alive + logmsg "RUN: failed to start the $srvrname server\n"; + stopserver($server, "$pid2"); + $doesntrun{$pidfile} = 1; + return (0,0); + } + + if($verbose) { + logmsg "RUN: $srvrname server is now running PID $http2pid\n"; + } + + return ($http2pid, $pid2); +} + +####################################################################### +# start the http server +# +sub runhttpserver { + my ($proto, $verbose, $alt, $port_or_path) = @_; + my $ip = $HOSTIP; + my $ipvnum = 4; + my $idnum = 1; + my $server; + my $srvrname; + my $pidfile; + my $logfile; + my $flags = ""; + my $exe = "$perl $srcdir/httpserver.pl"; + my $verbose_flag = "--verbose "; + + if($alt eq "ipv6") { + # if IPv6, use a different setup + $ipvnum = 6; + $ip = $HOST6IP; + } + elsif($alt eq "proxy") { + # basically the same, but another ID + $idnum = 2; + } + elsif($alt eq "pipe") { + # basically the same, but another ID + $idnum = 3; + $exe = "python $srcdir/http_pipe.py"; + $verbose_flag .= "1 "; + } + elsif($alt eq "unix") { + # IP (protocol) is mutually exclusive with Unix sockets + $ipvnum = "unix"; + } + + $server = servername_id($proto, $ipvnum, $idnum); + + $pidfile = $serverpidfile{$server}; + + # don't retry if the server doesn't work + if ($doesntrun{$pidfile}) { + return (0,0); + } + + my $pid = processexists($pidfile); + if($pid > 0) { + stopserver($server, "$pid"); + } + unlink($pidfile) if(-f $pidfile); + + $srvrname = servername_str($proto, $ipvnum, $idnum); + + $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + + $flags .= "--gopher " if($proto eq "gopher"); + $flags .= "--connect $HOSTIP " if($alt eq "proxy"); + $flags .= $verbose_flag if($debugprotocol); + $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" "; + $flags .= "--id $idnum " if($idnum > 1); + if($ipvnum eq "unix") { + $flags .= "--unix-socket '$port_or_path' "; + } else { + $flags .= "--ipv$ipvnum --port $port_or_path "; + } + $flags .= "--srcdir \"$srcdir\""; + + my $cmd = "$exe $flags"; + my ($httppid, $pid2) = startnew($cmd, $pidfile, 15, 0); + + if($httppid <= 0 || !pidexists($httppid)) { + # it is NOT alive + logmsg "RUN: failed to start the $srvrname server\n"; + stopserver($server, "$pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + + # Server is up. Verify that we can speak to it. + my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port_or_path); + if(!$pid3) { + logmsg "RUN: $srvrname server failed verification\n"; + # failed to talk to it properly. Kill the server and return failure + stopserver($server, "$httppid $pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + $pid2 = $pid3; + + if($verbose) { + logmsg "RUN: $srvrname server is now running PID $httppid\n"; + } + + sleep(1); + + return ($httppid, $pid2); +} + +####################################################################### +# start the http server +# +sub runhttp_pipeserver { + my ($proto, $verbose, $alt, $port) = @_; + my $ip = $HOSTIP; + my $ipvnum = 4; + my $idnum = 1; + my $server; + my $srvrname; + my $pidfile; + my $logfile; + my $flags = ""; + + if($alt eq "ipv6") { + # No IPv6 + } + + $server = servername_id($proto, $ipvnum, $idnum); + + $pidfile = $serverpidfile{$server}; + + # don't retry if the server doesn't work + if ($doesntrun{$pidfile}) { + return (0,0); + } + + my $pid = processexists($pidfile); + if($pid > 0) { + stopserver($server, "$pid"); + } + unlink($pidfile) if(-f $pidfile); + + $srvrname = servername_str($proto, $ipvnum, $idnum); + + $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + + $flags .= "--verbose 1 " if($debugprotocol); + $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" "; + $flags .= "--id $idnum " if($idnum > 1); + $flags .= "--port $port --srcdir \"$srcdir\""; + + my $cmd = "$srcdir/http_pipe.py $flags"; + my ($httppid, $pid2) = startnew($cmd, $pidfile, 15, 0); + + if($httppid <= 0 || !pidexists($httppid)) { + # it is NOT alive + logmsg "RUN: failed to start the $srvrname server\n"; + stopserver($server, "$pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + + # Server is up. Verify that we can speak to it. + my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port); + if(!$pid3) { + logmsg "RUN: $srvrname server failed verification\n"; + # failed to talk to it properly. Kill the server and return failure + stopserver($server, "$httppid $pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + $pid2 = $pid3; + + if($verbose) { + logmsg "RUN: $srvrname server is now running PID $httppid\n"; + } + + sleep(1); + + return ($httppid, $pid2); +} + +####################################################################### +# start the https stunnel based server +# +sub runhttpsserver { + my ($verbose, $ipv6, $certfile) = @_; + my $proto = 'https'; + my $ip = ($ipv6 && ($ipv6 =~ /6$/)) ? "$HOST6IP" : "$HOSTIP"; + my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4; + my $idnum = 1; + my $server; + my $srvrname; + my $pidfile; + my $logfile; + my $flags = ""; + + if(!$stunnel) { + return (0,0); + } + + $server = servername_id($proto, $ipvnum, $idnum); + + $pidfile = $serverpidfile{$server}; + + # don't retry if the server doesn't work + if ($doesntrun{$pidfile}) { + return (0,0); + } + + my $pid = processexists($pidfile); + if($pid > 0) { + stopserver($server, "$pid"); + } + unlink($pidfile) if(-f $pidfile); + + $srvrname = servername_str($proto, $ipvnum, $idnum); + + $certfile = 'stunnel.pem' unless($certfile); + + $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + + $flags .= "--verbose " if($debugprotocol); + $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" "; + $flags .= "--id $idnum " if($idnum > 1); + $flags .= "--ipv$ipvnum --proto $proto "; + $flags .= "--certfile \"$certfile\" " if($certfile ne 'stunnel.pem'); + $flags .= "--stunnel \"$stunnel\" --srcdir \"$srcdir\" "; + $flags .= "--connect $HTTPPORT --accept $HTTPSPORT"; + + my $cmd = "$perl $srcdir/secureserver.pl $flags"; + my ($httpspid, $pid2) = startnew($cmd, $pidfile, 15, 0); + + if($httpspid <= 0 || !pidexists($httpspid)) { + # it is NOT alive + logmsg "RUN: failed to start the $srvrname server\n"; + stopserver($server, "$pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return(0,0); + } + + # Server is up. Verify that we can speak to it. + my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $HTTPSPORT); + if(!$pid3) { + logmsg "RUN: $srvrname server failed verification\n"; + # failed to talk to it properly. Kill the server and return failure + stopserver($server, "$httpspid $pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + # Here pid3 is actually the pid returned by the unsecure-http server. + + $runcert{$server} = $certfile; + + if($verbose) { + logmsg "RUN: $srvrname server is now running PID $httpspid\n"; + } + + sleep(1); + + return ($httpspid, $pid2); +} + +####################################################################### +# start the non-stunnel HTTP TLS extensions capable server +# +sub runhttptlsserver { + my ($verbose, $ipv6) = @_; + my $proto = "httptls"; + my $port = ($ipv6 && ($ipv6 =~ /6$/)) ? $HTTPTLS6PORT : $HTTPTLSPORT; + my $ip = ($ipv6 && ($ipv6 =~ /6$/)) ? "$HOST6IP" : "$HOSTIP"; + my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4; + my $idnum = 1; + my $server; + my $srvrname; + my $pidfile; + my $logfile; + my $flags = ""; + + if(!$httptlssrv) { + return (0,0); + } + + $server = servername_id($proto, $ipvnum, $idnum); + + $pidfile = $serverpidfile{$server}; + + # don't retry if the server doesn't work + if ($doesntrun{$pidfile}) { + return (0,0); + } + + my $pid = processexists($pidfile); + if($pid > 0) { + stopserver($server, "$pid"); + } + unlink($pidfile) if(-f $pidfile); + + $srvrname = servername_str($proto, $ipvnum, $idnum); + + $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + + $flags .= "--http "; + $flags .= "--debug 1 " if($debugprotocol); + $flags .= "--port $port "; + $flags .= "--priority NORMAL:+SRP "; + $flags .= "--srppasswd $srcdir/certs/srp-verifier-db "; + $flags .= "--srppasswdconf $srcdir/certs/srp-verifier-conf"; + + my $cmd = "$httptlssrv $flags > $logfile 2>&1"; + my ($httptlspid, $pid2) = startnew($cmd, $pidfile, 10, 1); # fake pidfile + + if($httptlspid <= 0 || !pidexists($httptlspid)) { + # it is NOT alive + logmsg "RUN: failed to start the $srvrname server\n"; + stopserver($server, "$pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + + # Server is up. Verify that we can speak to it. PID is from fake pidfile + my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port); + if(!$pid3) { + logmsg "RUN: $srvrname server failed verification\n"; + # failed to talk to it properly. Kill the server and return failure + stopserver($server, "$httptlspid $pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + $pid2 = $pid3; + + if($verbose) { + logmsg "RUN: $srvrname server is now running PID $httptlspid\n"; + } + + sleep(1); + + return ($httptlspid, $pid2); +} + +####################################################################### +# start the pingpong server (FTP, POP3, IMAP, SMTP) +# +sub runpingpongserver { + my ($proto, $id, $verbose, $ipv6) = @_; + my $port; + my $ip = ($ipv6 && ($ipv6 =~ /6$/)) ? "$HOST6IP" : "$HOSTIP"; + my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4; + my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1; + my $server; + my $srvrname; + my $pidfile; + my $logfile; + my $flags = ""; + + if($proto eq "ftp") { + $port = ($idnum>1)?$FTP2PORT:$FTPPORT; + + if($ipvnum==6) { + # if IPv6, use a different setup + $port = $FTP6PORT; + } + } + elsif($proto eq "pop3") { + $port = ($ipvnum==6) ? $POP36PORT : $POP3PORT; + } + elsif($proto eq "imap") { + $port = ($ipvnum==6) ? $IMAP6PORT : $IMAPPORT; + } + elsif($proto eq "smtp") { + $port = ($ipvnum==6) ? $SMTP6PORT : $SMTPPORT; + } + else { + print STDERR "Unsupported protocol $proto!!\n"; + return 0; + } + + $server = servername_id($proto, $ipvnum, $idnum); + + $pidfile = $serverpidfile{$server}; + + # don't retry if the server doesn't work + if ($doesntrun{$pidfile}) { + return (0,0); + } + + my $pid = processexists($pidfile); + if($pid > 0) { + stopserver($server, "$pid"); + } + unlink($pidfile) if(-f $pidfile); + + $srvrname = servername_str($proto, $ipvnum, $idnum); + + $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + + $flags .= "--verbose " if($debugprotocol); + $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" "; + $flags .= "--srcdir \"$srcdir\" --proto $proto "; + $flags .= "--id $idnum " if($idnum > 1); + $flags .= "--ipv$ipvnum --port $port --addr \"$ip\""; + + my $cmd = "$perl $srcdir/ftpserver.pl $flags"; + my ($ftppid, $pid2) = startnew($cmd, $pidfile, 15, 0); + + if($ftppid <= 0 || !pidexists($ftppid)) { + # it is NOT alive + logmsg "RUN: failed to start the $srvrname server\n"; + stopserver($server, "$pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + + # Server is up. Verify that we can speak to it. + my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port); + if(!$pid3) { + logmsg "RUN: $srvrname server failed verification\n"; + # failed to talk to it properly. Kill the server and return failure + stopserver($server, "$ftppid $pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + + $pid2 = $pid3; + + if($verbose) { + logmsg "RUN: $srvrname server is now running PID $ftppid\n"; + } + + sleep(1); + + return ($pid2, $ftppid); +} + +####################################################################### +# start the ftps server (or rather, tunnel) +# +sub runftpsserver { + my ($verbose, $ipv6, $certfile) = @_; + my $proto = 'ftps'; + my $ip = ($ipv6 && ($ipv6 =~ /6$/)) ? "$HOST6IP" : "$HOSTIP"; + my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4; + my $idnum = 1; + my $server; + my $srvrname; + my $pidfile; + my $logfile; + my $flags = ""; + + if(!$stunnel) { + return (0,0); + } + + $server = servername_id($proto, $ipvnum, $idnum); + + $pidfile = $serverpidfile{$server}; + + # don't retry if the server doesn't work + if ($doesntrun{$pidfile}) { + return (0,0); + } + + my $pid = processexists($pidfile); + if($pid > 0) { + stopserver($server, "$pid"); + } + unlink($pidfile) if(-f $pidfile); + + $srvrname = servername_str($proto, $ipvnum, $idnum); + + $certfile = 'stunnel.pem' unless($certfile); + + $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + + $flags .= "--verbose " if($debugprotocol); + $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" "; + $flags .= "--id $idnum " if($idnum > 1); + $flags .= "--ipv$ipvnum --proto $proto "; + $flags .= "--certfile \"$certfile\" " if($certfile ne 'stunnel.pem'); + $flags .= "--stunnel \"$stunnel\" --srcdir \"$srcdir\" "; + $flags .= "--connect $FTPPORT --accept $FTPSPORT"; + + my $cmd = "$perl $srcdir/secureserver.pl $flags"; + my ($ftpspid, $pid2) = startnew($cmd, $pidfile, 15, 0); + + if($ftpspid <= 0 || !pidexists($ftpspid)) { + # it is NOT alive + logmsg "RUN: failed to start the $srvrname server\n"; + stopserver($server, "$pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return(0,0); + } + + # Server is up. Verify that we can speak to it. + my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $FTPSPORT); + if(!$pid3) { + logmsg "RUN: $srvrname server failed verification\n"; + # failed to talk to it properly. Kill the server and return failure + stopserver($server, "$ftpspid $pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + # Here pid3 is actually the pid returned by the unsecure-ftp server. + + $runcert{$server} = $certfile; + + if($verbose) { + logmsg "RUN: $srvrname server is now running PID $ftpspid\n"; + } + + sleep(1); + + return ($ftpspid, $pid2); +} + +####################################################################### +# start the tftp server +# +sub runtftpserver { + my ($id, $verbose, $ipv6) = @_; + my $port = $TFTPPORT; + my $ip = $HOSTIP; + my $proto = 'tftp'; + my $ipvnum = 4; + my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1; + my $server; + my $srvrname; + my $pidfile; + my $logfile; + my $flags = ""; + + if($ipv6) { + # if IPv6, use a different setup + $ipvnum = 6; + $port = $TFTP6PORT; + $ip = $HOST6IP; + } + + $server = servername_id($proto, $ipvnum, $idnum); + + $pidfile = $serverpidfile{$server}; + + # don't retry if the server doesn't work + if ($doesntrun{$pidfile}) { + return (0,0); + } + + my $pid = processexists($pidfile); + if($pid > 0) { + stopserver($server, "$pid"); + } + unlink($pidfile) if(-f $pidfile); + + $srvrname = servername_str($proto, $ipvnum, $idnum); + + $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + + $flags .= "--verbose " if($debugprotocol); + $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" "; + $flags .= "--id $idnum " if($idnum > 1); + $flags .= "--ipv$ipvnum --port $port --srcdir \"$srcdir\""; + + my $cmd = "$perl $srcdir/tftpserver.pl $flags"; + my ($tftppid, $pid2) = startnew($cmd, $pidfile, 15, 0); + + if($tftppid <= 0 || !pidexists($tftppid)) { + # it is NOT alive + logmsg "RUN: failed to start the $srvrname server\n"; + stopserver($server, "$pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + + # Server is up. Verify that we can speak to it. + my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port); + if(!$pid3) { + logmsg "RUN: $srvrname server failed verification\n"; + # failed to talk to it properly. Kill the server and return failure + stopserver($server, "$tftppid $pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + $pid2 = $pid3; + + if($verbose) { + logmsg "RUN: $srvrname server is now running PID $tftppid\n"; + } + + sleep(1); + + return ($pid2, $tftppid); +} + + +####################################################################### +# start the rtsp server +# +sub runrtspserver { + my ($verbose, $ipv6) = @_; + my $port = $RTSPPORT; + my $ip = $HOSTIP; + my $proto = 'rtsp'; + my $ipvnum = 4; + my $idnum = 1; + my $server; + my $srvrname; + my $pidfile; + my $logfile; + my $flags = ""; + + if($ipv6) { + # if IPv6, use a different setup + $ipvnum = 6; + $port = $RTSP6PORT; + $ip = $HOST6IP; + } + + $server = servername_id($proto, $ipvnum, $idnum); + + $pidfile = $serverpidfile{$server}; + + # don't retry if the server doesn't work + if ($doesntrun{$pidfile}) { + return (0,0); + } + + my $pid = processexists($pidfile); + if($pid > 0) { + stopserver($server, "$pid"); + } + unlink($pidfile) if(-f $pidfile); + + $srvrname = servername_str($proto, $ipvnum, $idnum); + + $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + + $flags .= "--verbose " if($debugprotocol); + $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" "; + $flags .= "--id $idnum " if($idnum > 1); + $flags .= "--ipv$ipvnum --port $port --srcdir \"$srcdir\""; + + my $cmd = "$perl $srcdir/rtspserver.pl $flags"; + my ($rtsppid, $pid2) = startnew($cmd, $pidfile, 15, 0); + + if($rtsppid <= 0 || !pidexists($rtsppid)) { + # it is NOT alive + logmsg "RUN: failed to start the $srvrname server\n"; + stopserver($server, "$pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + + # Server is up. Verify that we can speak to it. + my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port); + if(!$pid3) { + logmsg "RUN: $srvrname server failed verification\n"; + # failed to talk to it properly. Kill the server and return failure + stopserver($server, "$rtsppid $pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + $pid2 = $pid3; + + if($verbose) { + logmsg "RUN: $srvrname server is now running PID $rtsppid\n"; + } + + sleep(1); + + return ($rtsppid, $pid2); +} + + +####################################################################### +# Start the ssh (scp/sftp) server +# +sub runsshserver { + my ($id, $verbose, $ipv6) = @_; + my $ip=$HOSTIP; + my $port = $SSHPORT; + my $socksport = $SOCKSPORT; + my $proto = 'ssh'; + my $ipvnum = 4; + my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1; + my $server; + my $srvrname; + my $pidfile; + my $logfile; + my $flags = ""; + + $server = servername_id($proto, $ipvnum, $idnum); + + $pidfile = $serverpidfile{$server}; + + # don't retry if the server doesn't work + if ($doesntrun{$pidfile}) { + return (0,0); + } + + my $pid = processexists($pidfile); + if($pid > 0) { + stopserver($server, "$pid"); + } + unlink($pidfile) if(-f $pidfile); + + $srvrname = servername_str($proto, $ipvnum, $idnum); + + $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + + $flags .= "--verbose " if($verbose); + $flags .= "--debugprotocol " if($debugprotocol); + $flags .= "--pidfile \"$pidfile\" "; + $flags .= "--id $idnum " if($idnum > 1); + $flags .= "--ipv$ipvnum --addr \"$ip\" "; + $flags .= "--sshport $port --socksport $socksport "; + $flags .= "--user \"$USER\""; + + my $cmd = "$perl $srcdir/sshserver.pl $flags"; + my ($sshpid, $pid2) = startnew($cmd, $pidfile, 60, 0); + + # on loaded systems sshserver start up can take longer than the timeout + # passed to startnew, when this happens startnew completes without being + # able to read the pidfile and consequently returns a zero pid2 above. + + if($sshpid <= 0 || !pidexists($sshpid)) { + # it is NOT alive + logmsg "RUN: failed to start the $srvrname server\n"; + stopserver($server, "$pid2"); + $doesntrun{$pidfile} = 1; + return (0,0); + } + + # ssh server verification allows some extra time for the server to start up + # and gives us the opportunity of recovering the pid from the pidfile, when + # this verification succeeds the recovered pid is assigned to pid2. + + my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port); + if(!$pid3) { + logmsg "RUN: $srvrname server failed verification\n"; + # failed to fetch server pid. Kill the server and return failure + stopserver($server, "$sshpid $pid2"); + $doesntrun{$pidfile} = 1; + return (0,0); + } + $pid2 = $pid3; + + # once it is known that the ssh server is alive, sftp server verification + # is performed actually connecting to it, authenticating and performing a + # very simple remote command. This verification is tried only one time. + + $sshdlog = server_logfilename($LOGDIR, 'ssh', $ipvnum, $idnum); + $sftplog = server_logfilename($LOGDIR, 'sftp', $ipvnum, $idnum); + + if(verifysftp('sftp', $ipvnum, $idnum, $ip, $port) < 1) { + logmsg "RUN: SFTP server failed verification\n"; + # failed to talk to it properly. Kill the server and return failure + display_sftplog(); + display_sftpconfig(); + display_sshdlog(); + display_sshdconfig(); + stopserver($server, "$sshpid $pid2"); + $doesntrun{$pidfile} = 1; + return (0,0); + } + + if($verbose) { + logmsg "RUN: $srvrname server is now running PID $pid2\n"; + } + + return ($pid2, $sshpid); +} + +####################################################################### +# Start the socks server +# +sub runsocksserver { + my ($id, $verbose, $ipv6) = @_; + my $ip=$HOSTIP; + my $port = $SOCKSPORT; + my $proto = 'socks'; + my $ipvnum = 4; + my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1; + my $server; + my $srvrname; + my $pidfile; + my $logfile; + my $flags = ""; + + $server = servername_id($proto, $ipvnum, $idnum); + + $pidfile = $serverpidfile{$server}; + + # don't retry if the server doesn't work + if ($doesntrun{$pidfile}) { + return (0,0); + } + + my $pid = processexists($pidfile); + if($pid > 0) { + stopserver($server, "$pid"); + } + unlink($pidfile) if(-f $pidfile); + + $srvrname = servername_str($proto, $ipvnum, $idnum); + + $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + + # The ssh server must be already running + if(!$run{'ssh'}) { + logmsg "RUN: SOCKS server cannot find running SSH server\n"; + $doesntrun{$pidfile} = 1; + return (0,0); + } + + # Find out ssh daemon canonical file name + my $sshd = find_sshd(); + if(!$sshd) { + logmsg "RUN: SOCKS server cannot find $sshdexe\n"; + $doesntrun{$pidfile} = 1; + return (0,0); + } + + # Find out ssh daemon version info + ($sshdid, $sshdvernum, $sshdverstr, $sshderror) = sshversioninfo($sshd); + if(!$sshdid) { + # Not an OpenSSH or SunSSH ssh daemon + logmsg "$sshderror\n" if($verbose); + logmsg "SCP, SFTP and SOCKS tests require OpenSSH 2.9.9 or later\n"; + $doesntrun{$pidfile} = 1; + return (0,0); + } + logmsg "ssh server found $sshd is $sshdverstr\n" if($verbose); + + # Find out ssh client canonical file name + my $ssh = find_ssh(); + if(!$ssh) { + logmsg "RUN: SOCKS server cannot find $sshexe\n"; + $doesntrun{$pidfile} = 1; + return (0,0); + } + + # Find out ssh client version info + my ($sshid, $sshvernum, $sshverstr, $ssherror) = sshversioninfo($ssh); + if(!$sshid) { + # Not an OpenSSH or SunSSH ssh client + logmsg "$ssherror\n" if($verbose); + logmsg "SCP, SFTP and SOCKS tests require OpenSSH 2.9.9 or later\n"; + $doesntrun{$pidfile} = 1; + return (0,0); + } + + # Verify minimum ssh client version + if((($sshid =~ /OpenSSH/) && ($sshvernum < 299)) || + (($sshid =~ /SunSSH/) && ($sshvernum < 100))) { + logmsg "ssh client found $ssh is $sshverstr\n"; + logmsg "SCP, SFTP and SOCKS tests require OpenSSH 2.9.9 or later\n"; + $doesntrun{$pidfile} = 1; + return (0,0); + } + logmsg "ssh client found $ssh is $sshverstr\n" if($verbose); + + # Verify if ssh client and ssh daemon versions match + if(($sshdid ne $sshid) || ($sshdvernum != $sshvernum)) { + # Our test harness might work with slightly mismatched versions + logmsg "Warning: version mismatch: sshd $sshdverstr - ssh $sshverstr\n" + if($verbose); + } + + # Config file options for ssh client are previously set from sshserver.pl + if(! -e $sshconfig) { + logmsg "RUN: SOCKS server cannot find $sshconfig\n"; + $doesntrun{$pidfile} = 1; + return (0,0); + } + + $sshlog = server_logfilename($LOGDIR, 'socks', $ipvnum, $idnum); + + # start our socks server + my $cmd="\"$ssh\" -N -F $sshconfig $ip > $sshlog 2>&1"; + my ($sshpid, $pid2) = startnew($cmd, $pidfile, 30, 1); # fake pidfile + + if($sshpid <= 0 || !pidexists($sshpid)) { + # it is NOT alive + logmsg "RUN: failed to start the $srvrname server\n"; + display_sshlog(); + display_sshconfig(); + display_sshdlog(); + display_sshdconfig(); + stopserver($server, "$pid2"); + $doesntrun{$pidfile} = 1; + return (0,0); + } + + # Ugly hack but ssh doesn't support pid files. PID is from fake pidfile. + my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port); + if(!$pid3) { + logmsg "RUN: $srvrname server failed verification\n"; + # failed to talk to it properly. Kill the server and return failure + stopserver($server, "$sshpid $pid2"); + $doesntrun{$pidfile} = 1; + return (0,0); + } + $pid2 = $pid3; + + if($verbose) { + logmsg "RUN: $srvrname server is now running PID $pid2\n"; + } + + return ($pid2, $sshpid); +} + +####################################################################### +# Single shot http and gopher server responsiveness test. This should only +# be used to verify that a server present in %run hash is still functional +# +sub responsive_http_server { + my ($proto, $verbose, $alt, $port_or_path) = @_; + my $ip = $HOSTIP; + my $ipvnum = 4; + my $idnum = 1; + + if($alt eq "ipv6") { + # if IPv6, use a different setup + $ipvnum = 6; + $ip = $HOST6IP; + } + elsif($alt eq "proxy") { + $idnum = 2; + } + elsif($alt eq "unix") { + # IP (protocol) is mutually exclusive with Unix sockets + $ipvnum = "unix"; + } + + return &responsiveserver($proto, $ipvnum, $idnum, $ip, $port_or_path); +} + +####################################################################### +# Single shot pingpong server responsiveness test. This should only be +# used to verify that a server present in %run hash is still functional +# +sub responsive_pingpong_server { + my ($proto, $id, $verbose, $ipv6) = @_; + my $port; + my $ip = ($ipv6 && ($ipv6 =~ /6$/)) ? "$HOST6IP" : "$HOSTIP"; + my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4; + my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1; + + if($proto eq "ftp") { + $port = ($idnum>1)?$FTP2PORT:$FTPPORT; + + if($ipvnum==6) { + # if IPv6, use a different setup + $port = $FTP6PORT; + } + } + elsif($proto eq "pop3") { + $port = ($ipvnum==6) ? $POP36PORT : $POP3PORT; + } + elsif($proto eq "imap") { + $port = ($ipvnum==6) ? $IMAP6PORT : $IMAPPORT; + } + elsif($proto eq "smtp") { + $port = ($ipvnum==6) ? $SMTP6PORT : $SMTPPORT; + } + else { + print STDERR "Unsupported protocol $proto!!\n"; + return 0; + } + + return &responsiveserver($proto, $ipvnum, $idnum, $ip, $port); +} + +####################################################################### +# Single shot rtsp server responsiveness test. This should only be +# used to verify that a server present in %run hash is still functional +# +sub responsive_rtsp_server { + my ($verbose, $ipv6) = @_; + my $port = $RTSPPORT; + my $ip = $HOSTIP; + my $proto = 'rtsp'; + my $ipvnum = 4; + my $idnum = 1; + + if($ipv6) { + # if IPv6, use a different setup + $ipvnum = 6; + $port = $RTSP6PORT; + $ip = $HOST6IP; + } + + return &responsiveserver($proto, $ipvnum, $idnum, $ip, $port); +} + +####################################################################### +# Single shot tftp server responsiveness test. This should only be +# used to verify that a server present in %run hash is still functional +# +sub responsive_tftp_server { + my ($id, $verbose, $ipv6) = @_; + my $port = $TFTPPORT; + my $ip = $HOSTIP; + my $proto = 'tftp'; + my $ipvnum = 4; + my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1; + + if($ipv6) { + # if IPv6, use a different setup + $ipvnum = 6; + $port = $TFTP6PORT; + $ip = $HOST6IP; + } + + return &responsiveserver($proto, $ipvnum, $idnum, $ip, $port); +} + +####################################################################### +# Single shot non-stunnel HTTP TLS extensions capable server +# responsiveness test. This should only be used to verify that a +# server present in %run hash is still functional +# +sub responsive_httptls_server { + my ($verbose, $ipv6) = @_; + my $proto = "httptls"; + my $port = ($ipv6 && ($ipv6 =~ /6$/)) ? $HTTPTLS6PORT : $HTTPTLSPORT; + my $ip = ($ipv6 && ($ipv6 =~ /6$/)) ? "$HOST6IP" : "$HOSTIP"; + my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4; + my $idnum = 1; + + return &responsiveserver($proto, $ipvnum, $idnum, $ip, $port); +} + +####################################################################### +# Remove all files in the specified directory +# +sub cleardir { + my $dir = $_[0]; + my $count; + my $file; + + # Get all files + opendir(DIR, $dir) || + return 0; # can't open dir + while($file = readdir(DIR)) { + if($file !~ /^\./) { + unlink("$dir/$file"); + $count++; + } + } + closedir DIR; + return $count; +} + +####################################################################### +# compare test results with the expected output, we might filter off +# some pattern that is allowed to differ, output test results +# +sub compare { + my ($testnum, $testname, $subject, $firstref, $secondref)=@_; + + my $result = compareparts($firstref, $secondref); + + if($result) { + # timestamp test result verification end + $timevrfyend{$testnum} = Time::HiRes::time() if($timestats); + + if(!$short) { + logmsg "\n $testnum: $subject FAILED:\n"; + logmsg showdiff($LOGDIR, $firstref, $secondref); + } + elsif(!$automakestyle) { + logmsg "FAILED\n"; + } + else { + # automakestyle + logmsg "FAIL: $testnum - $testname - $subject\n"; + } + } + return $result; +} + +####################################################################### +# display information about curl and the host the test suite runs on +# +sub checksystem { + + unlink($memdump); # remove this if there was one left + + my $feat; + my $curl; + my $libcurl; + my $versretval; + my $versnoexec; + my @version=(); + + my $curlverout="$LOGDIR/curlverout.log"; + my $curlvererr="$LOGDIR/curlvererr.log"; + my $versioncmd="$CURL --version 1>$curlverout 2>$curlvererr"; + + unlink($curlverout); + unlink($curlvererr); + + $versretval = runclient($versioncmd); + $versnoexec = $!; + + open(VERSOUT, "<$curlverout"); + @version = ; + close(VERSOUT); + + $resolver="stock"; + for(@version) { + chomp; + + if($_ =~ /^curl/) { + $curl = $_; + $curl =~ s/^(.*)(libcurl.*)/$1/g; + + $libcurl = $2; + if($curl =~ /win32|mingw(32|64)/) { + # This is a Windows MinGW build or native build, we need to use + # Win32-style path. + $pwd = pathhelp::sys_native_current_path(); + } + if ($libcurl =~ /winssl/i) { + $has_winssl=1; + $ssllib="WinSSL"; + } + elsif ($libcurl =~ /openssl/i) { + $has_openssl=1; + $has_sslpinning=1; + $ssllib="OpenSSL"; + } + elsif ($libcurl =~ /gnutls/i) { + $has_gnutls=1; + $has_sslpinning=1; + $ssllib="GnuTLS"; + } + elsif ($libcurl =~ /nss/i) { + $has_nss=1; + $has_sslpinning=1; + $ssllib="NSS"; + } + elsif ($libcurl =~ /(yassl|wolfssl)/i) { + $has_yassl=1; + $has_sslpinning=1; + $ssllib="yassl"; + } + elsif ($libcurl =~ /polarssl/i) { + $has_polarssl=1; + $has_sslpinning=1; + $ssllib="polarssl"; + } + elsif ($libcurl =~ /axtls/i) { + $has_axtls=1; + $ssllib="axTLS"; + } + elsif ($libcurl =~ /securetransport/i) { + $has_darwinssl=1; + $ssllib="DarwinSSL"; + } + elsif ($libcurl =~ /BoringSSL/i) { + $has_boringssl=1; + $has_sslpinning=1; + $ssllib="BoringSSL"; + } + elsif ($libcurl =~ /libressl/i) { + $has_libressl=1; + $has_sslpinning=1; + $ssllib="libressl"; + } + elsif ($libcurl =~ /mbedTLS/i) { + $has_mbedtls=1; + $has_sslpinning=1; + $ssllib="mbedTLS"; + } + if ($libcurl =~ /ares/i) { + $has_cares=1; + $resolver="c-ares"; + } + } + elsif($_ =~ /^Protocols: (.*)/i) { + # these are the protocols compiled in to this libcurl + @protocols = split(' ', lc($1)); + + # Generate a "proto-ipv6" version of each protocol to match the + # IPv6 name and a "proto-unix" to match the variant which + # uses Unix domain sockets. This works even if support isn't + # compiled in because the test will fail. + push @protocols, map(("$_-ipv6", "$_-unix"), @protocols); + + # 'http-proxy' is used in test cases to do CONNECT through + push @protocols, 'http-proxy'; + + # 'http-pipe' is the special server for testing pipelining + push @protocols, 'http-pipe'; + + # 'none' is used in test cases to mean no server + push @protocols, 'none'; + } + elsif($_ =~ /^Features: (.*)/i) { + $feat = $1; + if($feat =~ /TrackMemory/i) { + # built with memory tracking support (--enable-curldebug) + $has_memory_tracking = 1; + } + if($feat =~ /debug/i) { + # curl was built with --enable-debug + $debug_build = 1; + } + if($feat =~ /SSL/i) { + # ssl enabled + $has_ssl=1; + } + if($feat =~ /Largefile/i) { + # large file support + $has_largefile=1; + } + if($feat =~ /IDN/i) { + # IDN support + $has_idn=1; + } + if($feat =~ /IPv6/i) { + $has_ipv6 = 1; + } + if($feat =~ /UnixSockets/i) { + $has_unix = 1; + } + if($feat =~ /libz/i) { + $has_libz = 1; + } + if($feat =~ /NTLM/i) { + # NTLM enabled + $has_ntlm=1; + + # Use this as a proxy for any cryptographic authentication + $has_crypto=1; + } + if($feat =~ /NTLM_WB/i) { + # NTLM delegation to winbind daemon ntlm_auth helper enabled + $has_ntlm_wb=1; + } + if($feat =~ /SSPI/i) { + # SSPI enabled + $has_sspi=1; + } + if($feat =~ /GSS-API/i) { + # GSS-API enabled + $has_gssapi=1; + } + if($feat =~ /Kerberos/i) { + # Kerberos enabled + $has_kerberos=1; + + # Use this as a proxy for any cryptographic authentication + $has_crypto=1; + } + if($feat =~ /SPNEGO/i) { + # SPNEGO enabled + $has_spnego=1; + + # Use this as a proxy for any cryptographic authentication + $has_crypto=1; + } + if($feat =~ /CharConv/i) { + # CharConv enabled + $has_charconv=1; + } + if($feat =~ /TLS-SRP/i) { + # TLS-SRP enabled + $has_tls_srp=1; + } + if($feat =~ /Metalink/i) { + # Metalink enabled + $has_metalink=1; + } + if($feat =~ /PSL/i) { + # PSL enabled + $has_psl=1; + } + if($feat =~ /AsynchDNS/i) { + if(!$has_cares) { + # this means threaded resolver + $has_threadedres=1; + $resolver="threaded"; + } + } + if($feat =~ /HTTP2/) { + # http2 enabled + $has_http2=1; + + push @protocols, 'http/2'; + } + } + # + # Test harness currently uses a non-stunnel server in order to + # run HTTP TLS-SRP tests required when curl is built with https + # protocol support and TLS-SRP feature enabled. For convenience + # 'httptls' may be included in the test harness protocols array + # to differentiate this from classic stunnel based 'https' test + # harness server. + # + if($has_tls_srp) { + my $add_httptls; + for(@protocols) { + if($_ =~ /^https(-ipv6|)$/) { + $add_httptls=1; + last; + } + } + if($add_httptls && (! grep /^httptls$/, @protocols)) { + push @protocols, 'httptls'; + push @protocols, 'httptls-ipv6'; + } + } + } + if(!$curl) { + logmsg "unable to get curl's version, further details are:\n"; + logmsg "issued command: \n"; + logmsg "$versioncmd \n"; + if ($versretval == -1) { + logmsg "command failed with: \n"; + logmsg "$versnoexec \n"; + } + elsif ($versretval & 127) { + logmsg sprintf("command died with signal %d, and %s coredump.\n", + ($versretval & 127), ($versretval & 128)?"a":"no"); + } + else { + logmsg sprintf("command exited with value %d \n", $versretval >> 8); + } + logmsg "contents of $curlverout: \n"; + displaylogcontent("$curlverout"); + logmsg "contents of $curlvererr: \n"; + displaylogcontent("$curlvererr"); + die "couldn't get curl's version"; + } + + if(-r "../lib/curl_config.h") { + open(CONF, "<../lib/curl_config.h"); + while() { + if($_ =~ /^\#define HAVE_GETRLIMIT/) { + $has_getrlimit = 1; + } + } + close(CONF); + } + + if($has_ipv6) { + # client has IPv6 support + + # check if the HTTP server has it! + my @sws = `server/sws --version`; + if($sws[0] =~ /IPv6/) { + # HTTP server has IPv6 support! + $http_ipv6 = 1; + $gopher_ipv6 = 1; + } + + # check if the FTP server has it! + @sws = `server/sockfilt --version`; + if($sws[0] =~ /IPv6/) { + # FTP server has IPv6 support! + $ftp_ipv6 = 1; + } + } + + if($has_unix) { + # client has Unix sockets support, check whether the HTTP server has it + my @sws = `server/sws --version`; + $http_unix = 1 if($sws[0] =~ /unix/); + } + + if(!$has_memory_tracking && $torture) { + die "can't run torture tests since curl was built without ". + "TrackMemory feature (--enable-curldebug)"; + } + + $has_shared = `sh $CURLCONFIG --built-shared`; + chomp $has_shared; + + my $hostname=join(' ', runclientoutput("hostname")); + my $hosttype=join(' ', runclientoutput("uname -a")); + + logmsg ("********* System characteristics ******** \n", + "* $curl\n", + "* $libcurl\n", + "* Features: $feat\n", + "* Host: $hostname", + "* System: $hosttype"); + + if($has_memory_tracking && $has_threadedres) { + $has_memory_tracking = 0; + logmsg("*\n", + "*** DISABLES memory tracking when using threaded resolver\n", + "*\n"); + } + + logmsg sprintf("* Servers: %s", $stunnel?"SSL ":""); + logmsg sprintf("%s", $http_ipv6?"HTTP-IPv6 ":""); + logmsg sprintf("%s", $http_unix?"HTTP-unix ":""); + logmsg sprintf("%s\n", $ftp_ipv6?"FTP-IPv6 ":"OFF"); + + logmsg sprintf("* Env: %s%s", $valgrind?"Valgrind ":"", + $run_event_based?"event-based ":""); + logmsg sprintf("%s\n", $libtool?"Libtool ":""); + + if($verbose) { + logmsg "* Ports:\n"; + + logmsg sprintf("* HTTP/%d ", $HTTPPORT); + logmsg sprintf("FTP/%d ", $FTPPORT); + logmsg sprintf("FTP2/%d ", $FTP2PORT); + logmsg sprintf("RTSP/%d ", $RTSPPORT); + if($stunnel) { + logmsg sprintf("FTPS/%d ", $FTPSPORT); + logmsg sprintf("HTTPS/%d ", $HTTPSPORT); + } + logmsg sprintf("\n* TFTP/%d ", $TFTPPORT); + if($http_ipv6) { + logmsg sprintf("HTTP-IPv6/%d ", $HTTP6PORT); + logmsg sprintf("RTSP-IPv6/%d ", $RTSP6PORT); + } + if($ftp_ipv6) { + logmsg sprintf("FTP-IPv6/%d ", $FTP6PORT); + } + if($tftp_ipv6) { + logmsg sprintf("TFTP-IPv6/%d ", $TFTP6PORT); + } + logmsg sprintf("\n* GOPHER/%d ", $GOPHERPORT); + if($gopher_ipv6) { + logmsg sprintf("GOPHER-IPv6/%d", $GOPHERPORT); + } + logmsg sprintf("\n* SSH/%d ", $SSHPORT); + logmsg sprintf("SOCKS/%d ", $SOCKSPORT); + logmsg sprintf("POP3/%d ", $POP3PORT); + logmsg sprintf("IMAP/%d ", $IMAPPORT); + logmsg sprintf("SMTP/%d\n", $SMTPPORT); + if($ftp_ipv6) { + logmsg sprintf("* POP3-IPv6/%d ", $POP36PORT); + logmsg sprintf("IMAP-IPv6/%d ", $IMAP6PORT); + logmsg sprintf("SMTP-IPv6/%d\n", $SMTP6PORT); + } + if($httptlssrv) { + logmsg sprintf("* HTTPTLS/%d ", $HTTPTLSPORT); + if($has_ipv6) { + logmsg sprintf("HTTPTLS-IPv6/%d ", $HTTPTLS6PORT); + } + logmsg "\n"; + } + logmsg sprintf("* HTTP-PIPE/%d \n", $HTTPPIPEPORT); + + if($has_unix) { + logmsg "* Unix socket paths:\n"; + if($http_unix) { + logmsg sprintf("* HTTP-Unix:%s\n", $HTTPUNIXPATH); + } + } + } + $has_textaware = ($^O eq 'MSWin32') || ($^O eq 'msys'); + + logmsg "***************************************** \n"; +} + +####################################################################### +# substitute the variable stuff into either a joined up file or +# a command, in either case passed by reference +# +sub subVariables { + my ($thing) = @_; + + # ports + + $$thing =~ s/%FTP6PORT/$FTP6PORT/g; + $$thing =~ s/%FTP2PORT/$FTP2PORT/g; + $$thing =~ s/%FTPSPORT/$FTPSPORT/g; + $$thing =~ s/%FTPPORT/$FTPPORT/g; + + $$thing =~ s/%GOPHER6PORT/$GOPHER6PORT/g; + $$thing =~ s/%GOPHERPORT/$GOPHERPORT/g; + + $$thing =~ s/%HTTPTLS6PORT/$HTTPTLS6PORT/g; + $$thing =~ s/%HTTPTLSPORT/$HTTPTLSPORT/g; + $$thing =~ s/%HTTP6PORT/$HTTP6PORT/g; + $$thing =~ s/%HTTPSPORT/$HTTPSPORT/g; + $$thing =~ s/%HTTP2PORT/$HTTP2PORT/g; + $$thing =~ s/%HTTPPORT/$HTTPPORT/g; + $$thing =~ s/%HTTPPIPEPORT/$HTTPPIPEPORT/g; + $$thing =~ s/%PROXYPORT/$HTTPPROXYPORT/g; + + $$thing =~ s/%IMAP6PORT/$IMAP6PORT/g; + $$thing =~ s/%IMAPPORT/$IMAPPORT/g; + + $$thing =~ s/%POP36PORT/$POP36PORT/g; + $$thing =~ s/%POP3PORT/$POP3PORT/g; + + $$thing =~ s/%RTSP6PORT/$RTSP6PORT/g; + $$thing =~ s/%RTSPPORT/$RTSPPORT/g; + + $$thing =~ s/%SMTP6PORT/$SMTP6PORT/g; + $$thing =~ s/%SMTPPORT/$SMTPPORT/g; + + $$thing =~ s/%SOCKSPORT/$SOCKSPORT/g; + $$thing =~ s/%SSHPORT/$SSHPORT/g; + + $$thing =~ s/%TFTP6PORT/$TFTP6PORT/g; + $$thing =~ s/%TFTPPORT/$TFTPPORT/g; + + # server Unix domain socket paths + + $$thing =~ s/%HTTPUNIXPATH/$HTTPUNIXPATH/g; + + # client IP addresses + + $$thing =~ s/%CLIENT6IP/$CLIENT6IP/g; + $$thing =~ s/%CLIENTIP/$CLIENTIP/g; + + # server IP addresses + + $$thing =~ s/%HOST6IP/$HOST6IP/g; + $$thing =~ s/%HOSTIP/$HOSTIP/g; + + # misc + + $$thing =~ s/%CURL/$CURL/g; + $$thing =~ s/%PWD/$pwd/g; + $$thing =~ s/%SRCDIR/$srcdir/g; + $$thing =~ s/%USER/$USER/g; + + # The purpose of FTPTIME2 and FTPTIME3 is to provide times that can be + # used for time-out tests and that whould work on most hosts as these + # adjust for the startup/check time for this particular host. We needed + # to do this to make the test suite run better on very slow hosts. + + my $ftp2 = $ftpchecktime * 2; + my $ftp3 = $ftpchecktime * 3; + + $$thing =~ s/%FTPTIME2/$ftp2/g; + $$thing =~ s/%FTPTIME3/$ftp3/g; + + # HTTP2 + + $$thing =~ s/%H2CVER/$h2cver/g; +} + +sub fixarray { + my @in = @_; + + for(@in) { + subVariables \$_; + } + return @in; +} + +####################################################################### +# Provide time stamps for single test skipped events +# +sub timestampskippedevents { + my $testnum = $_[0]; + + return if((not defined($testnum)) || ($testnum < 1)); + + if($timestats) { + + if($timevrfyend{$testnum}) { + return; + } + elsif($timesrvrlog{$testnum}) { + $timevrfyend{$testnum} = $timesrvrlog{$testnum}; + return; + } + elsif($timetoolend{$testnum}) { + $timevrfyend{$testnum} = $timetoolend{$testnum}; + $timesrvrlog{$testnum} = $timetoolend{$testnum}; + } + elsif($timetoolini{$testnum}) { + $timevrfyend{$testnum} = $timetoolini{$testnum}; + $timesrvrlog{$testnum} = $timetoolini{$testnum}; + $timetoolend{$testnum} = $timetoolini{$testnum}; + } + elsif($timesrvrend{$testnum}) { + $timevrfyend{$testnum} = $timesrvrend{$testnum}; + $timesrvrlog{$testnum} = $timesrvrend{$testnum}; + $timetoolend{$testnum} = $timesrvrend{$testnum}; + $timetoolini{$testnum} = $timesrvrend{$testnum}; + } + elsif($timesrvrini{$testnum}) { + $timevrfyend{$testnum} = $timesrvrini{$testnum}; + $timesrvrlog{$testnum} = $timesrvrini{$testnum}; + $timetoolend{$testnum} = $timesrvrini{$testnum}; + $timetoolini{$testnum} = $timesrvrini{$testnum}; + $timesrvrend{$testnum} = $timesrvrini{$testnum}; + } + elsif($timeprepini{$testnum}) { + $timevrfyend{$testnum} = $timeprepini{$testnum}; + $timesrvrlog{$testnum} = $timeprepini{$testnum}; + $timetoolend{$testnum} = $timeprepini{$testnum}; + $timetoolini{$testnum} = $timeprepini{$testnum}; + $timesrvrend{$testnum} = $timeprepini{$testnum}; + $timesrvrini{$testnum} = $timeprepini{$testnum}; + } + } +} + +####################################################################### +# Run a single specified test case +# +sub singletest { + my ($evbased, # 1 means switch on if possible (and "curl" is tested) + # returns "not a test" if it can't be used for this test + $testnum, + $count, + $total)=@_; + + my @what; + my $why; + my %feature; + my $cmd; + my $disablevalgrind; + + # copy test number to a global scope var, this allows + # testnum checking when starting test harness servers. + $testnumcheck = $testnum; + + # timestamp test preparation start + $timeprepini{$testnum} = Time::HiRes::time() if($timestats); + + if($disttests !~ /test$testnum\W/ ) { + logmsg "Warning: test$testnum not present in tests/data/Makefile.inc\n"; + } + if($disabled{$testnum}) { + logmsg "Warning: test$testnum is explicitly disabled\n"; + } + + # load the test case file definition + if(loadtest("${TESTDIR}/test${testnum}")) { + if($verbose) { + # this is not a test + logmsg "RUN: $testnum doesn't look like a test case\n"; + } + $why = "no test"; + } + else { + @what = getpart("client", "features"); + } + + # We require a feature to be present + for(@what) { + my $f = $_; + $f =~ s/\s//g; + + if($f =~ /^([^!].*)$/) { + # Store the feature for later + $feature{$1} = $1; + + if($1 eq "SSL") { + if($has_ssl) { + next; + } + } + elsif($1 eq "SSLpinning") { + if($has_sslpinning) { + next; + } + } + elsif($1 eq "OpenSSL") { + if($has_openssl) { + next; + } + } + elsif($1 eq "GnuTLS") { + if($has_gnutls) { + next; + } + } + elsif($1 eq "NSS") { + if($has_nss) { + next; + } + } + elsif($1 eq "axTLS") { + if($has_axtls) { + next; + } + } + elsif($1 eq "WinSSL") { + if($has_winssl) { + next; + } + } + elsif($1 eq "DarwinSSL") { + if($has_darwinssl) { + next; + } + } + elsif($1 eq "unittest") { + if($debug_build) { + next; + } + } + elsif($1 eq "debug") { + if($debug_build) { + next; + } + } + elsif($1 eq "TrackMemory") { + if($has_memory_tracking) { + next; + } + } + elsif($1 eq "large_file") { + if($has_largefile) { + next; + } + } + elsif($1 eq "idn") { + if($has_idn) { + next; + } + } + elsif($1 eq "ipv6") { + if($has_ipv6) { + next; + } + } + elsif($1 eq "libz") { + if($has_libz) { + next; + } + } + elsif($1 eq "NTLM") { + if($has_ntlm) { + next; + } + } + elsif($1 eq "NTLM_WB") { + if($has_ntlm_wb) { + next; + } + } + elsif($1 eq "SSPI") { + if($has_sspi) { + next; + } + } + elsif($1 eq "GSS-API") { + if($has_gssapi) { + next; + } + } + elsif($1 eq "Kerberos") { + if($has_kerberos) { + next; + } + } + elsif($1 eq "SPNEGO") { + if($has_spnego) { + next; + } + } + elsif($1 eq "getrlimit") { + if($has_getrlimit) { + next; + } + } + elsif($1 eq "crypto") { + if($has_crypto) { + next; + } + } + elsif($1 eq "TLS-SRP") { + if($has_tls_srp) { + next; + } + } + elsif($1 eq "Metalink") { + if($has_metalink) { + next; + } + } + elsif($1 eq "http/2") { + if($has_http2) { + next; + } + } + elsif($1 eq "PSL") { + if($has_psl) { + next; + } + } + elsif($1 eq "socks") { + next; + } + elsif($1 eq "unix-sockets") { + next if $has_unix; + } + # See if this "feature" is in the list of supported protocols + elsif (grep /^\Q$1\E$/i, @protocols) { + next; + } + + $why = "curl lacks $1 support"; + last; + } + } + + # We require a feature to not be present + if(!$why) { + for(@what) { + my $f = $_; + $f =~ s/\s//g; + + if($f =~ /^!(.*)$/) { + if($1 eq "SSL") { + if(!$has_ssl) { + next; + } + } + elsif($1 eq "OpenSSL") { + if(!$has_openssl) { + next; + } + } + elsif($1 eq "GnuTLS") { + if(!$has_gnutls) { + next; + } + } + elsif($1 eq "NSS") { + if(!$has_nss) { + next; + } + } + elsif($1 eq "axTLS") { + if(!$has_axtls) { + next; + } + } + elsif($1 eq "WinSSL") { + if(!$has_winssl) { + next; + } + } + elsif($1 eq "DarwinSSL") { + if(!$has_darwinssl) { + next; + } + } + elsif($1 eq "TrackMemory") { + if(!$has_memory_tracking) { + next; + } + } + elsif($1 eq "large_file") { + if(!$has_largefile) { + next; + } + } + elsif($1 eq "idn") { + if(!$has_idn) { + next; + } + } + elsif($1 eq "ipv6") { + if(!$has_ipv6) { + next; + } + } + elsif($1 eq "unix-sockets") { + next if !$has_unix; + } + elsif($1 eq "libz") { + if(!$has_libz) { + next; + } + } + elsif($1 eq "NTLM") { + if(!$has_ntlm) { + next; + } + } + elsif($1 eq "NTLM_WB") { + if(!$has_ntlm_wb) { + next; + } + } + elsif($1 eq "SSPI") { + if(!$has_sspi) { + next; + } + } + elsif($1 eq "GSS-API") { + if(!$has_gssapi) { + next; + } + } + elsif($1 eq "Kerberos") { + if(!$has_kerberos) { + next; + } + } + elsif($1 eq "SPNEGO") { + if(!$has_spnego) { + next; + } + } + elsif($1 eq "getrlimit") { + if(!$has_getrlimit) { + next; + } + } + elsif($1 eq "crypto") { + if(!$has_crypto) { + next; + } + } + elsif($1 eq "TLS-SRP") { + if(!$has_tls_srp) { + next; + } + } + elsif($1 eq "Metalink") { + if(!$has_metalink) { + next; + } + } + elsif($1 eq "PSL") { + if(!$has_psl) { + next; + } + } + else { + next; + } + } + else { + next; + } + + $why = "curl has $1 support"; + last; + } + } + + if(!$why) { + my @keywords = getpart("info", "keywords"); + my $match; + my $k; + + if(!$keywords[0]) { + $why = "missing the section!"; + } + + for $k (@keywords) { + chomp $k; + if ($disabled_keywords{$k}) { + $why = "disabled by keyword"; + } elsif ($enabled_keywords{$k}) { + $match = 1; + } + } + + if(!$why && !$match && %enabled_keywords) { + $why = "disabled by missing keyword"; + } + } + + # test definition may instruct to (un)set environment vars + # this is done this early, so that the precheck can use environment + # variables and still bail out fine on errors + + # restore environment variables that were modified in a previous run + foreach my $var (keys %oldenv) { + if($oldenv{$var} eq 'notset') { + delete $ENV{$var} if($ENV{$var}); + } + else { + $ENV{$var} = $oldenv{$var}; + } + delete $oldenv{$var}; + } + + # remove test server commands file before servers are started/verified + unlink($FTPDCMD) if(-f $FTPDCMD); + + # timestamp required servers verification start + $timesrvrini{$testnum} = Time::HiRes::time() if($timestats); + + if(!$why) { + $why = serverfortest($testnum); + } + + # timestamp required servers verification end + $timesrvrend{$testnum} = Time::HiRes::time() if($timestats); + + my @setenv = getpart("client", "setenv"); + if(@setenv) { + foreach my $s (@setenv) { + chomp $s; + subVariables \$s; + if($s =~ /([^=]*)=(.*)/) { + my ($var, $content) = ($1, $2); + # remember current setting, to restore it once test runs + $oldenv{$var} = ($ENV{$var})?"$ENV{$var}":'notset'; + # set new value + if(!$content) { + delete $ENV{$var} if($ENV{$var}); + } + else { + if($var =~ /^LD_PRELOAD/) { + if(exe_ext() && (exe_ext() eq '.exe')) { + # print "Skipping LD_PRELOAD due to lack of OS support\n"; + next; + } + if($debug_build || ($has_shared ne "yes")) { + # print "Skipping LD_PRELOAD due to no release shared build\n"; + next; + } + } + $ENV{$var} = "$content"; + } + } + } + } + + if(!$why) { + # TODO: + # Add a precheck cache. If a precheck command was already invoked + # exactly like this, then use the previous result to speed up + # successive test invokes! + + my @precheck = getpart("client", "precheck"); + if(@precheck) { + $cmd = $precheck[0]; + chomp $cmd; + subVariables \$cmd; + if($cmd) { + my @p = split(/ /, $cmd); + if($p[0] !~ /\//) { + # the first word, the command, does not contain a slash so + # we will scan the "improved" PATH to find the command to + # be able to run it + my $fullp = checktestcmd($p[0]); + + if($fullp) { + $p[0] = $fullp; + } + $cmd = join(" ", @p); + } + + my @o = `$cmd 2>/dev/null`; + if($o[0]) { + $why = $o[0]; + chomp $why; + } elsif($?) { + $why = "precheck command error"; + } + logmsg "prechecked $cmd\n" if($verbose); + } + } + } + + if($why && !$listonly) { + # there's a problem, count it as "skipped" + $skipped++; + $skipped{$why}++; + $teststat[$testnum]=$why; # store reason for this test case + + if(!$short) { + if($skipped{$why} <= 3) { + # show only the first three skips for each reason + logmsg sprintf("test %04d SKIPPED: $why\n", $testnum); + } + } + + timestampskippedevents($testnum); + return -1; + } + logmsg sprintf("test %04d...", $testnum) if(!$automakestyle); + + # extract the reply data + my @reply = getpart("reply", "data"); + my @replycheck = getpart("reply", "datacheck"); + + my %replyattr = getpartattr("reply", "data"); + my %replycheckattr = getpartattr("reply", "datacheck"); + + if (@replycheck) { + # we use this file instead to check the final output against + # get the mode attribute + my $filemode=$replycheckattr{'mode'}; + if($filemode && ($filemode eq "text") && $has_textaware) { + # text mode when running on windows: fix line endings + map s/\r\n/\n/g, @replycheck; + map s/\n/\r\n/g, @replycheck; + } + if($replycheckattr{'nonewline'}) { + # Yes, we must cut off the final newline from the final line + # of the datacheck + chomp($replycheck[$#replycheck]); + } + + for my $partsuffix (('1', '2', '3', '4')) { + my @replycheckpart = getpart("reply", "datacheck".$partsuffix); + if(@replycheckpart || partexists("reply", "datacheck".$partsuffix) ) { + my %replycheckpartattr = getpartattr("reply", "datacheck".$partsuffix); + # get the mode attribute + my $filemode=$replycheckpartattr{'mode'}; + if($filemode && ($filemode eq "text") && $has_textaware) { + # text mode when running on windows: fix line endings + map s/\r\n/\n/g, @replycheckpart; + map s/\n/\r\n/g, @replycheckpart; + } + if($replycheckpartattr{'nonewline'}) { + # Yes, we must cut off the final newline from the final line + # of the datacheck + chomp($replycheckpart[$#replycheckpart]); + } + push(@replycheck, @replycheckpart); + } + } + + @reply=@replycheck; + } + else { + # get the mode attribute + my $filemode=$replyattr{'mode'}; + if($filemode && ($filemode eq "text") && $has_textaware) { + # text mode when running on windows: fix line endings + map s/\r\n/\n/g, @reply; + map s/\n/\r\n/g, @reply; + } + } + + # this is the valid protocol blurb curl should generate + my @protocol= fixarray ( getpart("verify", "protocol") ); + + # this is the valid protocol blurb curl should generate to a proxy + my @proxyprot = fixarray ( getpart("verify", "proxy") ); + + # redirected stdout/stderr to these files + $STDOUT="$LOGDIR/stdout$testnum"; + $STDERR="$LOGDIR/stderr$testnum"; + + # if this section exists, we verify that the stdout contained this: + my @validstdout = fixarray ( getpart("verify", "stdout") ); + + # if this section exists, we verify upload + my @upload = getpart("verify", "upload"); + + # if this section exists, it might be FTP server instructions: + my @ftpservercmd = getpart("reply", "servercmd"); + + my $CURLOUT="$LOGDIR/curl$testnum.out"; # curl output if not stdout + + # name of the test + my @testname= getpart("client", "name"); + my $testname = $testname[0]; + $testname =~ s/\n//g; + logmsg "[$testname]\n" if(!$short); + + if($listonly) { + timestampskippedevents($testnum); + return 0; # look successful + } + + my @codepieces = getpart("client", "tool"); + + my $tool=""; + if(@codepieces) { + $tool = $codepieces[0]; + chomp $tool; + } + + # remove server output logfile + unlink($SERVERIN); + unlink($SERVER2IN); + unlink($PROXYIN); + + if(@ftpservercmd) { + # write the instructions to file + writearray($FTPDCMD, \@ftpservercmd); + } + + # get the command line options to use + my @blaha; + ($cmd, @blaha)= getpart("client", "command"); + + if($cmd) { + # make some nice replace operations + $cmd =~ s/\n//g; # no newlines please + # substitute variables in the command line + subVariables \$cmd; + } + else { + # there was no command given, use something silly + $cmd="-"; + } + if($has_memory_tracking) { + unlink($memdump); + } + + # create a (possibly-empty) file before starting the test + my @inputfile=getpart("client", "file"); + my %fileattr = getpartattr("client", "file"); + my $filename=$fileattr{'name'}; + if(@inputfile || $filename) { + if(!$filename) { + logmsg "ERROR: section client=>file has no name attribute\n"; + timestampskippedevents($testnum); + return -1; + } + my $fileContent = join('', @inputfile); + subVariables \$fileContent; +# logmsg "DEBUG: writing file " . $filename . "\n"; + open(OUTFILE, ">$filename"); + binmode OUTFILE; # for crapage systems, use binary + print OUTFILE $fileContent; + close(OUTFILE); + } + + my %cmdhash = getpartattr("client", "command"); + + my $out=""; + + if((!$cmdhash{'option'}) || ($cmdhash{'option'} !~ /no-output/)) { + #We may slap on --output! + if (!@validstdout) { + $out=" --output $CURLOUT "; + } + } + + my $serverlogslocktimeout = $defserverlogslocktimeout; + if($cmdhash{'timeout'}) { + # test is allowed to override default server logs lock timeout + if($cmdhash{'timeout'} =~ /(\d+)/) { + $serverlogslocktimeout = $1 if($1 >= 0); + } + } + + my $postcommanddelay = $defpostcommanddelay; + if($cmdhash{'delay'}) { + # test is allowed to specify a delay after command is executed + if($cmdhash{'delay'} =~ /(\d+)/) { + $postcommanddelay = $1 if($1 > 0); + } + } + + my $CMDLINE; + my $cmdargs; + my $cmdtype = $cmdhash{'type'} || "default"; + my $fail_due_event_based = $evbased; + if($cmdtype eq "perl") { + # run the command line prepended with "perl" + $cmdargs ="$cmd"; + $CMDLINE = "perl "; + $tool=$CMDLINE; + $disablevalgrind=1; + } + elsif($cmdtype eq "shell") { + # run the command line prepended with "/bin/sh" + $cmdargs ="$cmd"; + $CMDLINE = "/bin/sh "; + $tool=$CMDLINE; + $disablevalgrind=1; + } + elsif(!$tool) { + # run curl, add suitable command line options + $cmd = "-1 ".$cmd if(exists $feature{"SSL"} && ($has_axtls)); + + my $inc=""; + if((!$cmdhash{'option'}) || ($cmdhash{'option'} !~ /no-include/)) { + $inc = " --include"; + } + + $cmdargs = "$out$inc "; + $cmdargs .= "--trace-ascii log/trace$testnum "; + $cmdargs .= "--trace-time "; + if($evbased) { + $cmdargs .= "--test-event "; + $fail_due_event_based--; + } + $cmdargs .= $cmd; + } + else { + $cmdargs = " $cmd"; # $cmd is the command line for the test file + $CURLOUT = $STDOUT; # sends received data to stdout + + if($tool =~ /^lib/) { + $CMDLINE="$LIBDIR/$tool"; + } + elsif($tool =~ /^unit/) { + $CMDLINE="$UNITDIR/$tool"; + } + + if(! -f $CMDLINE) { + logmsg "The tool set in the test case for this: '$tool' does not exist\n"; + timestampskippedevents($testnum); + return -1; + } + $DBGCURL=$CMDLINE; + } + + if($gdbthis) { + # gdb is incompatible with valgrind, so disable it when debugging + # Perhaps a better approach would be to run it under valgrind anyway + # with --db-attach=yes or --vgdb=yes. + $disablevalgrind=1; + } + + if($fail_due_event_based) { + logmsg "This test cannot run event based\n"; + return -1; + } + + my @stdintest = getpart("client", "stdin"); + + if(@stdintest) { + my $stdinfile="$LOGDIR/stdin-for-$testnum"; + + my %hash = getpartattr("client", "stdin"); + if($hash{'nonewline'}) { + # cut off the final newline from the final line of the stdin data + chomp($stdintest[$#stdintest]); + } + + writearray($stdinfile, \@stdintest); + + $cmdargs .= " <$stdinfile"; + } + + if(!$tool) { + $CMDLINE="$CURL"; + } + + my $usevalgrind; + if($valgrind && !$disablevalgrind) { + my @valgrindoption = getpart("verify", "valgrind"); + if((!@valgrindoption) || ($valgrindoption[0] !~ /disable/)) { + $usevalgrind = 1; + my $valgrindcmd = "$valgrind "; + $valgrindcmd .= "$valgrind_tool " if($valgrind_tool); + $valgrindcmd .= "--leak-check=yes "; + $valgrindcmd .= "--suppressions=$srcdir/valgrind.supp "; + # $valgrindcmd .= "--gen-suppressions=all "; + $valgrindcmd .= "--num-callers=16 "; + $valgrindcmd .= "${valgrind_logfile}=$LOGDIR/valgrind$testnum"; + $CMDLINE = "$valgrindcmd $CMDLINE"; + } + } + + $CMDLINE .= "$cmdargs >$STDOUT 2>$STDERR"; + + if($verbose) { + logmsg "$CMDLINE\n"; + } + + print CMDLOG "$CMDLINE\n"; + + unlink("core"); + + my $dumped_core; + my $cmdres; + + # Apr 2007: precommand isn't being used and could be removed + my @precommand= getpart("client", "precommand"); + if($precommand[0]) { + # this is pure perl to eval! + my $code = join("", @precommand); + eval $code; + if($@) { + logmsg "perl: $code\n"; + logmsg "precommand: $@"; + stopservers($verbose); + timestampskippedevents($testnum); + return -1; + } + } + + if($gdbthis) { + my $gdbinit = "$TESTDIR/gdbinit$testnum"; + open(GDBCMD, ">$LOGDIR/gdbcmd"); + print GDBCMD "set args $cmdargs\n"; + print GDBCMD "show args\n"; + print GDBCMD "source $gdbinit\n" if -e $gdbinit; + close(GDBCMD); + } + + # timestamp starting of test command + $timetoolini{$testnum} = Time::HiRes::time() if($timestats); + + # run the command line we built + if ($torture) { + $cmdres = torture($CMDLINE, + "$gdb --directory libtest $DBGCURL -x $LOGDIR/gdbcmd"); + } + elsif($gdbthis) { + my $GDBW = ($gdbxwin) ? "-w" : ""; + runclient("$gdb --directory libtest $DBGCURL $GDBW -x $LOGDIR/gdbcmd"); + $cmdres=0; # makes it always continue after a debugged run + } + else { + $cmdres = runclient("$CMDLINE"); + my $signal_num = $cmdres & 127; + $dumped_core = $cmdres & 128; + + if(!$anyway && ($signal_num || $dumped_core)) { + $cmdres = 1000; + } + else { + $cmdres >>= 8; + $cmdres = (2000 + $signal_num) if($signal_num && !$cmdres); + } + } + + # timestamp finishing of test command + $timetoolend{$testnum} = Time::HiRes::time() if($timestats); + + if(!$dumped_core) { + if(-r "core") { + # there's core file present now! + $dumped_core = 1; + } + } + + if($dumped_core) { + logmsg "core dumped\n"; + if(0 && $gdb) { + logmsg "running gdb for post-mortem analysis:\n"; + open(GDBCMD, ">$LOGDIR/gdbcmd2"); + print GDBCMD "bt\n"; + close(GDBCMD); + runclient("$gdb --directory libtest -x $LOGDIR/gdbcmd2 -batch $DBGCURL core "); + # unlink("$LOGDIR/gdbcmd2"); + } + } + + # If a server logs advisor read lock file exists, it is an indication + # that the server has not yet finished writing out all its log files, + # including server request log files used for protocol verification. + # So, if the lock file exists the script waits here a certain amount + # of time until the server removes it, or the given time expires. + + if($serverlogslocktimeout) { + my $lockretry = $serverlogslocktimeout * 20; + while((-f $SERVERLOGS_LOCK) && $lockretry--) { + select(undef, undef, undef, 0.05); + } + if(($lockretry < 0) && + ($serverlogslocktimeout >= $defserverlogslocktimeout)) { + logmsg "Warning: server logs lock timeout ", + "($serverlogslocktimeout seconds) expired\n"; + } + } + + # Test harness ssh server does not have this synchronization mechanism, + # this implies that some ssh server based tests might need a small delay + # once that the client command has run to avoid false test failures. + # + # gnutls-serv also lacks this synchronization mechanism, so gnutls-serv + # based tests might need a small delay once that the client command has + # run to avoid false test failures. + + sleep($postcommanddelay) if($postcommanddelay); + + # timestamp removal of server logs advisor read lock + $timesrvrlog{$testnum} = Time::HiRes::time() if($timestats); + + # test definition might instruct to stop some servers + # stop also all servers relative to the given one + + my @killtestservers = getpart("client", "killserver"); + if(@killtestservers) { + # + # All servers relative to the given one must be stopped also + # + my @killservers; + foreach my $server (@killtestservers) { + chomp $server; + if($server =~ /^(ftp|http|imap|pop3|smtp)s((\d*)(-ipv6|-unix|))$/) { + # given a stunnel ssl server, also kill non-ssl underlying one + push @killservers, "${1}${2}"; + } + elsif($server =~ /^(ftp|http|imap|pop3|smtp)((\d*)(-ipv6|-unix|))$/) { + # given a non-ssl server, also kill stunnel piggybacking one + push @killservers, "${1}s${2}"; + } + elsif($server =~ /^(socks)((\d*)(-ipv6|))$/) { + # given a socks server, also kill ssh underlying one + push @killservers, "ssh${2}"; + } + elsif($server =~ /^(ssh)((\d*)(-ipv6|))$/) { + # given a ssh server, also kill socks piggybacking one + push @killservers, "socks${2}"; + } + push @killservers, $server; + } + # + # kill sockfilter processes for pingpong relative servers + # + foreach my $server (@killservers) { + if($server =~ /^(ftp|imap|pop3|smtp)s?(\d*)(-ipv6|)$/) { + my $proto = $1; + my $idnum = ($2 && ($2 > 1)) ? $2 : 1; + my $ipvnum = ($3 && ($3 =~ /6$/)) ? 6 : 4; + killsockfilters($proto, $ipvnum, $idnum, $verbose); + } + } + # + # kill server relative pids clearing them in %run hash + # + my $pidlist; + foreach my $server (@killservers) { + if($run{$server}) { + $pidlist .= "$run{$server} "; + $run{$server} = 0; + } + $runcert{$server} = 0 if($runcert{$server}); + } + killpid($verbose, $pidlist); + # + # cleanup server pid files + # + foreach my $server (@killservers) { + my $pidfile = $serverpidfile{$server}; + my $pid = processexists($pidfile); + if($pid > 0) { + logmsg "Warning: $server server unexpectedly alive\n"; + killpid($verbose, $pid); + } + unlink($pidfile) if(-f $pidfile); + } + } + + # remove the test server commands file after each test + unlink($FTPDCMD) if(-f $FTPDCMD); + + # run the postcheck command + my @postcheck= getpart("client", "postcheck"); + if(@postcheck) { + $cmd = $postcheck[0]; + chomp $cmd; + subVariables \$cmd; + if($cmd) { + logmsg "postcheck $cmd\n" if($verbose); + my $rc = runclient("$cmd"); + # Must run the postcheck command in torture mode in order + # to clean up, but the result can't be relied upon. + if($rc != 0 && !$torture) { + logmsg " postcheck FAILED\n"; + # timestamp test result verification end + $timevrfyend{$testnum} = Time::HiRes::time() if($timestats); + return 1; + } + } + } + + # restore environment variables that were modified + if(%oldenv) { + foreach my $var (keys %oldenv) { + if($oldenv{$var} eq 'notset') { + delete $ENV{$var} if($ENV{$var}); + } + else { + $ENV{$var} = "$oldenv{$var}"; + } + } + } + + # Skip all the verification on torture tests + if ($torture) { + if(!$cmdres && !$keepoutfiles) { + cleardir($LOGDIR); + } + # timestamp test result verification end + $timevrfyend{$testnum} = Time::HiRes::time() if($timestats); + return $cmdres; + } + + my @err = getpart("verify", "errorcode"); + my $errorcode = $err[0] || "0"; + my $ok=""; + my $res; + chomp $errorcode; + if (@validstdout) { + # verify redirected stdout + my @actual = loadarray($STDOUT); + + # what parts to cut off from stdout + my @stripfile = getpart("verify", "stripfile"); + + foreach my $strip (@stripfile) { + chomp $strip; + my @newgen; + for(@actual) { + eval $strip; + if($_) { + push @newgen, $_; + } + } + # this is to get rid of array entries that vanished (zero + # length) because of replacements + @actual = @newgen; + } + + # variable-replace in the stdout we have from the test case file + @validstdout = fixarray(@validstdout); + + # get all attributes + my %hash = getpartattr("verify", "stdout"); + + # get the mode attribute + my $filemode=$hash{'mode'}; + if($filemode && ($filemode eq "text") && $has_textaware) { + # text mode when running on windows: fix line endings + map s/\r\n/\n/g, @validstdout; + map s/\n/\r\n/g, @validstdout; + } + + if($hash{'nonewline'}) { + # Yes, we must cut off the final newline from the final line + # of the protocol data + chomp($validstdout[$#validstdout]); + } + + $res = compare($testnum, $testname, "stdout", \@actual, \@validstdout); + if($res) { + return 1; + } + $ok .= "s"; + } + else { + $ok .= "-"; # stdout not checked + } + + if(@protocol) { + # Verify the sent request + my @out = loadarray($SERVERIN); + + # what to cut off from the live protocol sent by curl + my @strip = getpart("verify", "strip"); + + my @protstrip=@protocol; + + # check if there's any attributes on the verify/protocol section + my %hash = getpartattr("verify", "protocol"); + + if($hash{'nonewline'}) { + # Yes, we must cut off the final newline from the final line + # of the protocol data + chomp($protstrip[$#protstrip]); + } + + for(@strip) { + # strip off all lines that match the patterns from both arrays + chomp $_; + @out = striparray( $_, \@out); + @protstrip= striparray( $_, \@protstrip); + } + + # what parts to cut off from the protocol + my @strippart = getpart("verify", "strippart"); + my $strip; + for $strip (@strippart) { + chomp $strip; + for(@out) { + eval $strip; + } + } + + $res = compare($testnum, $testname, "protocol", \@out, \@protstrip); + if($res) { + return 1; + } + + $ok .= "p"; + + } + else { + $ok .= "-"; # protocol not checked + } + + if(!$replyattr{'nocheck'} && (@reply || $replyattr{'sendzero'})) { + # verify the received data + my @out = loadarray($CURLOUT); + $res = compare($testnum, $testname, "data", \@out, \@reply); + if ($res) { + return 1; + } + $ok .= "d"; + } + else { + $ok .= "-"; # data not checked + } + + if(@upload) { + # verify uploaded data + my @out = loadarray("$LOGDIR/upload.$testnum"); + $res = compare($testnum, $testname, "upload", \@out, \@upload); + if ($res) { + return 1; + } + $ok .= "u"; + } + else { + $ok .= "-"; # upload not checked + } + + if(@proxyprot) { + # Verify the sent proxy request + my @out = loadarray($PROXYIN); + + # what to cut off from the live protocol sent by curl, we use the + # same rules as for + my @strip = getpart("verify", "strip"); + + my @protstrip=@proxyprot; + + # check if there's any attributes on the verify/protocol section + my %hash = getpartattr("verify", "proxy"); + + if($hash{'nonewline'}) { + # Yes, we must cut off the final newline from the final line + # of the protocol data + chomp($protstrip[$#protstrip]); + } + + for(@strip) { + # strip off all lines that match the patterns from both arrays + chomp $_; + @out = striparray( $_, \@out); + @protstrip= striparray( $_, \@protstrip); + } + + # what parts to cut off from the protocol + my @strippart = getpart("verify", "strippart"); + my $strip; + for $strip (@strippart) { + chomp $strip; + for(@out) { + eval $strip; + } + } + + $res = compare($testnum, $testname, "proxy", \@out, \@protstrip); + if($res) { + return 1; + } + + $ok .= "P"; + + } + else { + $ok .= "-"; # protocol not checked + } + + my $outputok; + for my $partsuffix (('', '1', '2', '3', '4')) { + my @outfile=getpart("verify", "file".$partsuffix); + if(@outfile || partexists("verify", "file".$partsuffix) ) { + # we're supposed to verify a dynamically generated file! + my %hash = getpartattr("verify", "file".$partsuffix); + + my $filename=$hash{'name'}; + if(!$filename) { + logmsg "ERROR: section verify=>file$partsuffix ". + "has no name attribute\n"; + stopservers($verbose); + # timestamp test result verification end + $timevrfyend{$testnum} = Time::HiRes::time() if($timestats); + return -1; + } + my @generated=loadarray($filename); + + # what parts to cut off from the file + my @stripfile = getpart("verify", "stripfile".$partsuffix); + + my $filemode=$hash{'mode'}; + if($filemode && ($filemode eq "text") && $has_textaware) { + # text mode when running on windows: fix line endings + map s/\r\n/\n/g, @outfile; + map s/\n/\r\n/g, @outfile; + } + + my $strip; + for $strip (@stripfile) { + chomp $strip; + my @newgen; + for(@generated) { + eval $strip; + if($_) { + push @newgen, $_; + } + } + # this is to get rid of array entries that vanished (zero + # length) because of replacements + @generated = @newgen; + } + + @outfile = fixarray(@outfile); + + $res = compare($testnum, $testname, "output ($filename)", + \@generated, \@outfile); + if($res) { + return 1; + } + + $outputok = 1; # output checked + } + } + $ok .= ($outputok) ? "o" : "-"; # output checked or not + + # accept multiple comma-separated error codes + my @splerr = split(/ *, */, $errorcode); + my $errok; + foreach my $e (@splerr) { + if($e == $cmdres) { + # a fine error code + $errok = 1; + last; + } + } + + if($errok) { + $ok .= "e"; + } + else { + if(!$short) { + logmsg sprintf("\n%s returned $cmdres, when expecting %s\n", + (!$tool)?"curl":$tool, $errorcode); + } + logmsg " exit FAILED\n"; + # timestamp test result verification end + $timevrfyend{$testnum} = Time::HiRes::time() if($timestats); + return 1; + } + + if($has_memory_tracking) { + if(! -f $memdump) { + logmsg "\n** ALERT! memory tracking with no output file?\n" + if(!$cmdtype eq "perl"); + } + else { + my @memdata=`$memanalyze $memdump`; + my $leak=0; + for(@memdata) { + if($_ ne "") { + # well it could be other memory problems as well, but + # we call it leak for short here + $leak=1; + } + } + if($leak) { + logmsg "\n** MEMORY FAILURE\n"; + logmsg @memdata; + # timestamp test result verification end + $timevrfyend{$testnum} = Time::HiRes::time() if($timestats); + return 1; + } + else { + $ok .= "m"; + } + } + } + else { + $ok .= "-"; # memory not checked + } + + if($valgrind) { + if($usevalgrind) { + unless(opendir(DIR, "$LOGDIR")) { + logmsg "ERROR: unable to read $LOGDIR\n"; + # timestamp test result verification end + $timevrfyend{$testnum} = Time::HiRes::time() if($timestats); + return 1; + } + my @files = readdir(DIR); + closedir(DIR); + my $vgfile; + foreach my $file (@files) { + if($file =~ /^valgrind$testnum(\..*|)$/) { + $vgfile = $file; + last; + } + } + if(!$vgfile) { + logmsg "ERROR: valgrind log file missing for test $testnum\n"; + # timestamp test result verification end + $timevrfyend{$testnum} = Time::HiRes::time() if($timestats); + return 1; + } + my @e = valgrindparse($srcdir, $feature{'SSL'}, "$LOGDIR/$vgfile"); + if(@e && $e[0]) { + if($automakestyle) { + logmsg "FAIL: $testnum - $testname - valgrind\n"; + } + else { + logmsg " valgrind ERROR "; + logmsg @e; + } + # timestamp test result verification end + $timevrfyend{$testnum} = Time::HiRes::time() if($timestats); + return 1; + } + $ok .= "v"; + } + else { + if(!$short && !$disablevalgrind) { + logmsg " valgrind SKIPPED\n"; + } + $ok .= "-"; # skipped + } + } + else { + $ok .= "-"; # valgrind not checked + } + # add 'E' for event-based + $ok .= $evbased ? "E" : "-"; + + logmsg "$ok " if(!$short); + + my $sofar= time()-$start; + my $esttotal = $sofar/$count * $total; + my $estleft = $esttotal - $sofar; + my $left=sprintf("remaining: %02d:%02d", + $estleft/60, + $estleft%60); + + if(!$automakestyle) { + logmsg sprintf("OK (%-3d out of %-3d, %s)\n", $count, $total, $left); + } + else { + logmsg "PASS: $testnum - $testname\n"; + } + + # the test succeeded, remove all log files + if(!$keepoutfiles) { + cleardir($LOGDIR); + } + + # timestamp test result verification end + $timevrfyend{$testnum} = Time::HiRes::time() if($timestats); + + return 0; +} + +####################################################################### +# Stop all running test servers +# +sub stopservers { + my $verbose = $_[0]; + # + # kill sockfilter processes for all pingpong servers + # + killallsockfilters($verbose); + # + # kill all server pids from %run hash clearing them + # + my $pidlist; + foreach my $server (keys %run) { + if($run{$server}) { + if($verbose) { + my $prev = 0; + my $pids = $run{$server}; + foreach my $pid (split(' ', $pids)) { + if($pid != $prev) { + logmsg sprintf("* kill pid for %s => %d\n", + $server, $pid); + $prev = $pid; + } + } + } + $pidlist .= "$run{$server} "; + $run{$server} = 0; + } + $runcert{$server} = 0 if($runcert{$server}); + } + killpid($verbose, $pidlist); + # + # cleanup all server pid files + # + foreach my $server (keys %serverpidfile) { + my $pidfile = $serverpidfile{$server}; + my $pid = processexists($pidfile); + if($pid > 0) { + logmsg "Warning: $server server unexpectedly alive\n"; + killpid($verbose, $pid); + } + unlink($pidfile) if(-f $pidfile); + } +} + +####################################################################### +# startservers() starts all the named servers +# +# Returns: string with error reason or blank for success +# +sub startservers { + my @what = @_; + my ($pid, $pid2); + for(@what) { + my (@whatlist) = split(/\s+/,$_); + my $what = lc($whatlist[0]); + $what =~ s/[^a-z0-9\/-]//g; + + my $certfile; + if($what =~ /^(ftp|http|imap|pop3|smtp)s((\d*)(-ipv6|-unix|))$/) { + $certfile = ($whatlist[1]) ? $whatlist[1] : 'stunnel.pem'; + } + + if(($what eq "pop3") || + ($what eq "ftp") || + ($what eq "imap") || + ($what eq "smtp")) { + if($torture && $run{$what} && + !responsive_pingpong_server($what, "", $verbose)) { + stopserver($what); + } + if(!$run{$what}) { + ($pid, $pid2) = runpingpongserver($what, "", $verbose); + if($pid <= 0) { + return "failed starting ". uc($what) ." server"; + } + printf ("* pid $what => %d %d\n", $pid, $pid2) if($verbose); + $run{$what}="$pid $pid2"; + } + } + elsif($what eq "ftp2") { + if($torture && $run{'ftp2'} && + !responsive_pingpong_server("ftp", "2", $verbose)) { + stopserver('ftp2'); + } + if(!$run{'ftp2'}) { + ($pid, $pid2) = runpingpongserver("ftp", "2", $verbose); + if($pid <= 0) { + return "failed starting FTP2 server"; + } + printf ("* pid ftp2 => %d %d\n", $pid, $pid2) if($verbose); + $run{'ftp2'}="$pid $pid2"; + } + } + elsif($what eq "ftp-ipv6") { + if($torture && $run{'ftp-ipv6'} && + !responsive_pingpong_server("ftp", "", $verbose, "ipv6")) { + stopserver('ftp-ipv6'); + } + if(!$run{'ftp-ipv6'}) { + ($pid, $pid2) = runpingpongserver("ftp", "", $verbose, "ipv6"); + if($pid <= 0) { + return "failed starting FTP-IPv6 server"; + } + logmsg sprintf("* pid ftp-ipv6 => %d %d\n", $pid, + $pid2) if($verbose); + $run{'ftp-ipv6'}="$pid $pid2"; + } + } + elsif($what eq "gopher") { + if($torture && $run{'gopher'} && + !responsive_http_server("gopher", $verbose, 0, $GOPHERPORT)) { + stopserver('gopher'); + } + if(!$run{'gopher'}) { + ($pid, $pid2) = runhttpserver("gopher", $verbose, 0, + $GOPHERPORT); + if($pid <= 0) { + return "failed starting GOPHER server"; + } + logmsg sprintf ("* pid gopher => %d %d\n", $pid, $pid2) + if($verbose); + $run{'gopher'}="$pid $pid2"; + } + } + elsif($what eq "gopher-ipv6") { + if($torture && $run{'gopher-ipv6'} && + !responsive_http_server("gopher", $verbose, "ipv6", + $GOPHER6PORT)) { + stopserver('gopher-ipv6'); + } + if(!$run{'gopher-ipv6'}) { + ($pid, $pid2) = runhttpserver("gopher", $verbose, "ipv6", + $GOPHER6PORT); + if($pid <= 0) { + return "failed starting GOPHER-IPv6 server"; + } + logmsg sprintf("* pid gopher-ipv6 => %d %d\n", $pid, + $pid2) if($verbose); + $run{'gopher-ipv6'}="$pid $pid2"; + } + } + elsif($what eq "http/2") { + if(!$run{'http/2'}) { + ($pid, $pid2) = runhttp2server($verbose, $HTTP2PORT); + if($pid <= 0) { + return "failed starting HTTP/2 server"; + } + logmsg sprintf ("* pid http/2 => %d %d\n", $pid, $pid2) + if($verbose); + $run{'http/2'}="$pid $pid2"; + } + } + elsif($what eq "http") { + if($torture && $run{'http'} && + !responsive_http_server("http", $verbose, 0, $HTTPPORT)) { + stopserver('http'); + } + if(!$run{'http'}) { + ($pid, $pid2) = runhttpserver("http", $verbose, 0, + $HTTPPORT); + if($pid <= 0) { + return "failed starting HTTP server"; + } + logmsg sprintf ("* pid http => %d %d\n", $pid, $pid2) + if($verbose); + $run{'http'}="$pid $pid2"; + } + } + elsif($what eq "http-proxy") { + if($torture && $run{'http-proxy'} && + !responsive_http_server("http", $verbose, "proxy", + $HTTPPROXYPORT)) { + stopserver('http-proxy'); + } + if(!$run{'http-proxy'}) { + ($pid, $pid2) = runhttpserver("http", $verbose, "proxy", + $HTTPPROXYPORT); + if($pid <= 0) { + return "failed starting HTTP-proxy server"; + } + logmsg sprintf ("* pid http-proxy => %d %d\n", $pid, $pid2) + if($verbose); + $run{'http-proxy'}="$pid $pid2"; + } + } + elsif($what eq "http-ipv6") { + if($torture && $run{'http-ipv6'} && + !responsive_http_server("http", $verbose, "ipv6", $HTTP6PORT)) { + stopserver('http-ipv6'); + } + if(!$run{'http-ipv6'}) { + ($pid, $pid2) = runhttpserver("http", $verbose, "ipv6", + $HTTP6PORT); + if($pid <= 0) { + return "failed starting HTTP-IPv6 server"; + } + logmsg sprintf("* pid http-ipv6 => %d %d\n", $pid, $pid2) + if($verbose); + $run{'http-ipv6'}="$pid $pid2"; + } + } + elsif($what eq "http-pipe") { + if($torture && $run{'http-pipe'} && + !responsive_http_server("http", $verbose, "pipe", + $HTTPPIPEPORT)) { + stopserver('http-pipe'); + } + if(!$run{'http-pipe'}) { + ($pid, $pid2) = runhttpserver("http", $verbose, "pipe", + $HTTPPIPEPORT); + if($pid <= 0) { + return "failed starting HTTP-pipe server"; + } + logmsg sprintf ("* pid http-pipe => %d %d\n", $pid, $pid2) + if($verbose); + $run{'http-pipe'}="$pid $pid2"; + } + } + elsif($what eq "rtsp") { + if($torture && $run{'rtsp'} && + !responsive_rtsp_server($verbose)) { + stopserver('rtsp'); + } + if(!$run{'rtsp'}) { + ($pid, $pid2) = runrtspserver($verbose); + if($pid <= 0) { + return "failed starting RTSP server"; + } + printf ("* pid rtsp => %d %d\n", $pid, $pid2) if($verbose); + $run{'rtsp'}="$pid $pid2"; + } + } + elsif($what eq "rtsp-ipv6") { + if($torture && $run{'rtsp-ipv6'} && + !responsive_rtsp_server($verbose, "ipv6")) { + stopserver('rtsp-ipv6'); + } + if(!$run{'rtsp-ipv6'}) { + ($pid, $pid2) = runrtspserver($verbose, "ipv6"); + if($pid <= 0) { + return "failed starting RTSP-IPv6 server"; + } + logmsg sprintf("* pid rtsp-ipv6 => %d %d\n", $pid, $pid2) + if($verbose); + $run{'rtsp-ipv6'}="$pid $pid2"; + } + } + elsif($what eq "ftps") { + if(!$stunnel) { + # we can't run ftps tests without stunnel + return "no stunnel"; + } + if(!$has_ssl) { + # we can't run ftps tests if libcurl is SSL-less + return "curl lacks SSL support"; + } + if($runcert{'ftps'} && ($runcert{'ftps'} ne $certfile)) { + # stop server when running and using a different cert + stopserver('ftps'); + } + if($torture && $run{'ftp'} && + !responsive_pingpong_server("ftp", "", $verbose)) { + stopserver('ftp'); + } + if(!$run{'ftp'}) { + ($pid, $pid2) = runpingpongserver("ftp", "", $verbose); + if($pid <= 0) { + return "failed starting FTP server"; + } + printf ("* pid ftp => %d %d\n", $pid, $pid2) if($verbose); + $run{'ftp'}="$pid $pid2"; + } + if(!$run{'ftps'}) { + ($pid, $pid2) = runftpsserver($verbose, "", $certfile); + if($pid <= 0) { + return "failed starting FTPS server (stunnel)"; + } + logmsg sprintf("* pid ftps => %d %d\n", $pid, $pid2) + if($verbose); + $run{'ftps'}="$pid $pid2"; + } + } + elsif($what eq "file") { + # we support it but have no server! + } + elsif($what eq "https") { + if(!$stunnel) { + # we can't run https tests without stunnel + return "no stunnel"; + } + if(!$has_ssl) { + # we can't run https tests if libcurl is SSL-less + return "curl lacks SSL support"; + } + if($runcert{'https'} && ($runcert{'https'} ne $certfile)) { + # stop server when running and using a different cert + stopserver('https'); + } + if($torture && $run{'http'} && + !responsive_http_server("http", $verbose, 0, $HTTPPORT)) { + stopserver('http'); + } + if(!$run{'http'}) { + ($pid, $pid2) = runhttpserver("http", $verbose, 0, + $HTTPPORT); + if($pid <= 0) { + return "failed starting HTTP server"; + } + printf ("* pid http => %d %d\n", $pid, $pid2) if($verbose); + $run{'http'}="$pid $pid2"; + } + if(!$run{'https'}) { + ($pid, $pid2) = runhttpsserver($verbose, "", $certfile); + if($pid <= 0) { + return "failed starting HTTPS server (stunnel)"; + } + logmsg sprintf("* pid https => %d %d\n", $pid, $pid2) + if($verbose); + $run{'https'}="$pid $pid2"; + } + } + elsif($what eq "httptls") { + if(!$httptlssrv) { + # for now, we can't run http TLS-EXT tests without gnutls-serv + return "no gnutls-serv"; + } + if($torture && $run{'httptls'} && + !responsive_httptls_server($verbose, "IPv4")) { + stopserver('httptls'); + } + if(!$run{'httptls'}) { + ($pid, $pid2) = runhttptlsserver($verbose, "IPv4"); + if($pid <= 0) { + return "failed starting HTTPTLS server (gnutls-serv)"; + } + logmsg sprintf("* pid httptls => %d %d\n", $pid, $pid2) + if($verbose); + $run{'httptls'}="$pid $pid2"; + } + } + elsif($what eq "httptls-ipv6") { + if(!$httptlssrv) { + # for now, we can't run http TLS-EXT tests without gnutls-serv + return "no gnutls-serv"; + } + if($torture && $run{'httptls-ipv6'} && + !responsive_httptls_server($verbose, "ipv6")) { + stopserver('httptls-ipv6'); + } + if(!$run{'httptls-ipv6'}) { + ($pid, $pid2) = runhttptlsserver($verbose, "ipv6"); + if($pid <= 0) { + return "failed starting HTTPTLS-IPv6 server (gnutls-serv)"; + } + logmsg sprintf("* pid httptls-ipv6 => %d %d\n", $pid, $pid2) + if($verbose); + $run{'httptls-ipv6'}="$pid $pid2"; + } + } + elsif($what eq "tftp") { + if($torture && $run{'tftp'} && + !responsive_tftp_server("", $verbose)) { + stopserver('tftp'); + } + if(!$run{'tftp'}) { + ($pid, $pid2) = runtftpserver("", $verbose); + if($pid <= 0) { + return "failed starting TFTP server"; + } + printf ("* pid tftp => %d %d\n", $pid, $pid2) if($verbose); + $run{'tftp'}="$pid $pid2"; + } + } + elsif($what eq "tftp-ipv6") { + if($torture && $run{'tftp-ipv6'} && + !responsive_tftp_server("", $verbose, "ipv6")) { + stopserver('tftp-ipv6'); + } + if(!$run{'tftp-ipv6'}) { + ($pid, $pid2) = runtftpserver("", $verbose, "ipv6"); + if($pid <= 0) { + return "failed starting TFTP-IPv6 server"; + } + printf("* pid tftp-ipv6 => %d %d\n", $pid, $pid2) if($verbose); + $run{'tftp-ipv6'}="$pid $pid2"; + } + } + elsif($what eq "sftp" || $what eq "scp" || $what eq "socks4" || $what eq "socks5" ) { + if(!$run{'ssh'}) { + ($pid, $pid2) = runsshserver("", $verbose); + if($pid <= 0) { + return "failed starting SSH server"; + } + printf ("* pid ssh => %d %d\n", $pid, $pid2) if($verbose); + $run{'ssh'}="$pid $pid2"; + } + if($what eq "socks4" || $what eq "socks5") { + if(!$run{'socks'}) { + ($pid, $pid2) = runsocksserver("", $verbose); + if($pid <= 0) { + return "failed starting socks server"; + } + printf ("* pid socks => %d %d\n", $pid, $pid2) if($verbose); + $run{'socks'}="$pid $pid2"; + } + } + if($what eq "socks5") { + if(!$sshdid) { + # Not an OpenSSH or SunSSH ssh daemon + logmsg "Not OpenSSH or SunSSH; socks5 tests need at least OpenSSH 3.7\n"; + return "failed starting socks5 server"; + } + elsif(($sshdid =~ /OpenSSH/) && ($sshdvernum < 370)) { + # Need OpenSSH 3.7 for socks5 - http://www.openssh.com/txt/release-3.7 + logmsg "$sshdverstr insufficient; socks5 tests need at least OpenSSH 3.7\n"; + return "failed starting socks5 server"; + } + elsif(($sshdid =~ /SunSSH/) && ($sshdvernum < 100)) { + # Need SunSSH 1.0 for socks5 + logmsg "$sshdverstr insufficient; socks5 tests need at least SunSSH 1.0\n"; + return "failed starting socks5 server"; + } + } + } + elsif($what eq "http-unix") { + if($torture && $run{'http-unix'} && + !responsive_http_server("http", $verbose, "unix", $HTTPUNIXPATH)) { + stopserver('http-unix'); + } + if(!$run{'http-unix'}) { + ($pid, $pid2) = runhttpserver("http", $verbose, "unix", + $HTTPUNIXPATH); + if($pid <= 0) { + return "failed starting HTTP-unix server"; + } + logmsg sprintf("* pid http-unix => %d %d\n", $pid, $pid2) + if($verbose); + $run{'http-unix'}="$pid $pid2"; + } + } + elsif($what eq "none") { + logmsg "* starts no server\n" if ($verbose); + } + else { + warn "we don't support a server for $what"; + return "no server for $what"; + } + } + return 0; +} + +############################################################################## +# This function makes sure the right set of server is running for the +# specified test case. This is a useful design when we run single tests as not +# all servers need to run then! +# +# Returns: a string, blank if everything is fine or a reason why it failed +# +sub serverfortest { + my ($testnum)=@_; + + my @what = getpart("client", "server"); + + if(!$what[0]) { + warn "Test case $testnum has no server(s) specified"; + return "no server specified"; + } + + for(my $i = scalar(@what) - 1; $i >= 0; $i--) { + my $srvrline = $what[$i]; + chomp $srvrline if($srvrline); + if($srvrline =~ /^(\S+)((\s*)(.*))/) { + my $server = "${1}"; + my $lnrest = "${2}"; + my $tlsext; + if($server =~ /^(httptls)(\+)(ext|srp)(\d*)(-ipv6|)$/) { + $server = "${1}${4}${5}"; + $tlsext = uc("TLS-${3}"); + } + if(! grep /^\Q$server\E$/, @protocols) { + if(substr($server,0,5) ne "socks") { + if($tlsext) { + return "curl lacks $tlsext support"; + } + else { + return "curl lacks $server server support"; + } + } + } + $what[$i] = "$server$lnrest" if($tlsext); + } + } + + return &startservers(@what); +} + +####################################################################### +# runtimestats displays test-suite run time statistics +# +sub runtimestats { + my $lasttest = $_[0]; + + return if(not $timestats); + + logmsg "\nTest suite total running time breakdown per task...\n\n"; + + my @timesrvr; + my @timeprep; + my @timetool; + my @timelock; + my @timevrfy; + my @timetest; + my $timesrvrtot = 0.0; + my $timepreptot = 0.0; + my $timetooltot = 0.0; + my $timelocktot = 0.0; + my $timevrfytot = 0.0; + my $timetesttot = 0.0; + my $counter; + + for my $testnum (1 .. $lasttest) { + if($timesrvrini{$testnum}) { + $timesrvrtot += $timesrvrend{$testnum} - $timesrvrini{$testnum}; + $timepreptot += + (($timetoolini{$testnum} - $timeprepini{$testnum}) - + ($timesrvrend{$testnum} - $timesrvrini{$testnum})); + $timetooltot += $timetoolend{$testnum} - $timetoolini{$testnum}; + $timelocktot += $timesrvrlog{$testnum} - $timetoolend{$testnum}; + $timevrfytot += $timevrfyend{$testnum} - $timesrvrlog{$testnum}; + $timetesttot += $timevrfyend{$testnum} - $timeprepini{$testnum}; + push @timesrvr, sprintf("%06.3f %04d", + $timesrvrend{$testnum} - $timesrvrini{$testnum}, $testnum); + push @timeprep, sprintf("%06.3f %04d", + ($timetoolini{$testnum} - $timeprepini{$testnum}) - + ($timesrvrend{$testnum} - $timesrvrini{$testnum}), $testnum); + push @timetool, sprintf("%06.3f %04d", + $timetoolend{$testnum} - $timetoolini{$testnum}, $testnum); + push @timelock, sprintf("%06.3f %04d", + $timesrvrlog{$testnum} - $timetoolend{$testnum}, $testnum); + push @timevrfy, sprintf("%06.3f %04d", + $timevrfyend{$testnum} - $timesrvrlog{$testnum}, $testnum); + push @timetest, sprintf("%06.3f %04d", + $timevrfyend{$testnum} - $timeprepini{$testnum}, $testnum); + } + } + + { + no warnings 'numeric'; + @timesrvr = sort { $b <=> $a } @timesrvr; + @timeprep = sort { $b <=> $a } @timeprep; + @timetool = sort { $b <=> $a } @timetool; + @timelock = sort { $b <=> $a } @timelock; + @timevrfy = sort { $b <=> $a } @timevrfy; + @timetest = sort { $b <=> $a } @timetest; + } + + logmsg "Spent ". sprintf("%08.3f ", $timesrvrtot) . + "seconds starting and verifying test harness servers.\n"; + logmsg "Spent ". sprintf("%08.3f ", $timepreptot) . + "seconds reading definitions and doing test preparations.\n"; + logmsg "Spent ". sprintf("%08.3f ", $timetooltot) . + "seconds actually running test tools.\n"; + logmsg "Spent ". sprintf("%08.3f ", $timelocktot) . + "seconds awaiting server logs lock removal.\n"; + logmsg "Spent ". sprintf("%08.3f ", $timevrfytot) . + "seconds verifying test results.\n"; + logmsg "Spent ". sprintf("%08.3f ", $timetesttot) . + "seconds doing all of the above.\n"; + + $counter = 25; + logmsg "\nTest server starting and verification time per test ". + sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full"); + logmsg "-time- test\n"; + logmsg "------ ----\n"; + foreach my $txt (@timesrvr) { + last if((not $fullstats) && (not $counter--)); + logmsg "$txt\n"; + } + + $counter = 10; + logmsg "\nTest definition reading and preparation time per test ". + sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full"); + logmsg "-time- test\n"; + logmsg "------ ----\n"; + foreach my $txt (@timeprep) { + last if((not $fullstats) && (not $counter--)); + logmsg "$txt\n"; + } + + $counter = 25; + logmsg "\nTest tool execution time per test ". + sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full"); + logmsg "-time- test\n"; + logmsg "------ ----\n"; + foreach my $txt (@timetool) { + last if((not $fullstats) && (not $counter--)); + logmsg "$txt\n"; + } + + $counter = 15; + logmsg "\nTest server logs lock removal time per test ". + sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full"); + logmsg "-time- test\n"; + logmsg "------ ----\n"; + foreach my $txt (@timelock) { + last if((not $fullstats) && (not $counter--)); + logmsg "$txt\n"; + } + + $counter = 10; + logmsg "\nTest results verification time per test ". + sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full"); + logmsg "-time- test\n"; + logmsg "------ ----\n"; + foreach my $txt (@timevrfy) { + last if((not $fullstats) && (not $counter--)); + logmsg "$txt\n"; + } + + $counter = 50; + logmsg "\nTotal time per test ". + sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full"); + logmsg "-time- test\n"; + logmsg "------ ----\n"; + foreach my $txt (@timetest) { + last if((not $fullstats) && (not $counter--)); + logmsg "$txt\n"; + } + + logmsg "\n"; +} + +####################################################################### +# Check options to this test program +# + +my $number=0; +my $fromnum=-1; +my @testthis; +while(@ARGV) { + if ($ARGV[0] eq "-v") { + # verbose output + $verbose=1; + } + elsif($ARGV[0] =~ /^-b(.*)/) { + my $portno=$1; + if($portno =~ s/(\d+)$//) { + $base = int $1; + } + } + elsif ($ARGV[0] eq "-c") { + # use this path to curl instead of default + $DBGCURL=$CURL="\"$ARGV[1]\""; + shift @ARGV; + } + elsif ($ARGV[0] eq "-vc") { + # use this path to a curl used to verify servers + + # Particularly useful when you introduce a crashing bug somewhere in + # the development version as then it won't be able to run any tests + # since it can't verify the servers! + + $VCURL="\"$ARGV[1]\""; + shift @ARGV; + } + elsif ($ARGV[0] eq "-d") { + # have the servers display protocol output + $debugprotocol=1; + } + elsif ($ARGV[0] eq "-g") { + # run this test with gdb + $gdbthis=1; + } + elsif ($ARGV[0] eq "-gw") { + # run this test with windowed gdb + $gdbthis=1; + $gdbxwin=1; + } + elsif($ARGV[0] eq "-s") { + # short output + $short=1; + } + elsif($ARGV[0] eq "-am") { + # automake-style output + $short=1; + $automakestyle=1; + } + elsif($ARGV[0] eq "-n") { + # no valgrind + undef $valgrind; + } + elsif($ARGV[0] =~ /^-t(.*)/) { + # torture + $torture=1; + my $xtra = $1; + + if($xtra =~ s/(\d+)$//) { + $tortalloc = $1; + } + # we undef valgrind to make this fly in comparison + undef $valgrind; + } + elsif($ARGV[0] eq "-a") { + # continue anyway, even if a test fail + $anyway=1; + } + elsif($ARGV[0] eq "-e") { + # run the tests cases event based if possible + $run_event_based=1; + } + elsif($ARGV[0] eq "-p") { + $postmortem=1; + } + elsif($ARGV[0] eq "-l") { + # lists the test case names only + $listonly=1; + } + elsif($ARGV[0] eq "-k") { + # keep stdout and stderr files after tests + $keepoutfiles=1; + } + elsif($ARGV[0] eq "-r") { + # run time statistics needs Time::HiRes + if($Time::HiRes::VERSION) { + keys(%timeprepini) = 1000; + keys(%timesrvrini) = 1000; + keys(%timesrvrend) = 1000; + keys(%timetoolini) = 1000; + keys(%timetoolend) = 1000; + keys(%timesrvrlog) = 1000; + keys(%timevrfyend) = 1000; + $timestats=1; + $fullstats=0; + } + } + elsif($ARGV[0] eq "-rf") { + # run time statistics needs Time::HiRes + if($Time::HiRes::VERSION) { + keys(%timeprepini) = 1000; + keys(%timesrvrini) = 1000; + keys(%timesrvrend) = 1000; + keys(%timetoolini) = 1000; + keys(%timetoolend) = 1000; + keys(%timesrvrlog) = 1000; + keys(%timevrfyend) = 1000; + $timestats=1; + $fullstats=1; + } + } + elsif(($ARGV[0] eq "-h") || ($ARGV[0] eq "--help")) { + # show help text + print <= 0) { + for($fromnum .. $number) { + push @testthis, $_; + } + $fromnum = -1; + } + else { + push @testthis, $1; + } + } + elsif($ARGV[0] =~ /^to$/i) { + $fromnum = $number+1; + } + elsif($ARGV[0] =~ /^!(\d+)/) { + $fromnum = -1; + $disabled{$1}=$1; + } + elsif($ARGV[0] =~ /^!(.+)/) { + $disabled_keywords{$1}=$1; + } + elsif($ARGV[0] =~ /^([-[{a-zA-Z].*)/) { + $enabled_keywords{$1}=$1; + } + else { + print "Unknown option: $ARGV[0]\n"; + exit; + } + shift @ARGV; +} + +if(@testthis && ($testthis[0] ne "")) { + $TESTCASES=join(" ", @testthis); +} + +if($valgrind) { + # we have found valgrind on the host, use it + + # verify that we can invoke it fine + my $code = runclient("valgrind >/dev/null 2>&1"); + + if(($code>>8) != 1) { + #logmsg "Valgrind failure, disable it\n"; + undef $valgrind; + } else { + + # since valgrind 2.1.x, '--tool' option is mandatory + # use it, if it is supported by the version installed on the system + runclient("valgrind --help 2>&1 | grep -- --tool > /dev/null 2>&1"); + if (($? >> 8)==0) { + $valgrind_tool="--tool=memcheck"; + } + open(C, "<$CURL"); + my $l = ; + if($l =~ /^\#\!/) { + # A shell script. This is typically when built with libtool, + $valgrind="../libtool --mode=execute $valgrind"; + } + close(C); + + # valgrind 3 renamed the --logfile option to --log-file!!! + my $ver=join(' ', runclientoutput("valgrind --version")); + # cut off all but digits and dots + $ver =~ s/[^0-9.]//g; + + if($ver =~ /^(\d+)/) { + $ver = $1; + if($ver >= 3) { + $valgrind_logfile="--log-file"; + } + } + } +} + +if ($gdbthis) { + # open the executable curl and read the first 4 bytes of it + open(CHECK, "<$CURL"); + my $c; + sysread CHECK, $c, 4; + close(CHECK); + if($c eq "#! /") { + # A shell script. This is typically when built with libtool, + $libtool = 1; + $gdb = "../libtool --mode=execute gdb"; + } +} + +$HTTPPORT = $base++; # HTTP server port +$HTTPSPORT = $base++; # HTTPS (stunnel) server port +$FTPPORT = $base++; # FTP server port +$FTPSPORT = $base++; # FTPS (stunnel) server port +$HTTP6PORT = $base++; # HTTP IPv6 server port +$FTP2PORT = $base++; # FTP server 2 port +$FTP6PORT = $base++; # FTP IPv6 port +$TFTPPORT = $base++; # TFTP (UDP) port +$TFTP6PORT = $base++; # TFTP IPv6 (UDP) port +$SSHPORT = $base++; # SSH (SCP/SFTP) port +$SOCKSPORT = $base++; # SOCKS port +$POP3PORT = $base++; # POP3 server port +$POP36PORT = $base++; # POP3 IPv6 server port +$IMAPPORT = $base++; # IMAP server port +$IMAP6PORT = $base++; # IMAP IPv6 server port +$SMTPPORT = $base++; # SMTP server port +$SMTP6PORT = $base++; # SMTP IPv6 server port +$RTSPPORT = $base++; # RTSP server port +$RTSP6PORT = $base++; # RTSP IPv6 server port +$GOPHERPORT = $base++; # Gopher IPv4 server port +$GOPHER6PORT = $base++; # Gopher IPv6 server port +$HTTPTLSPORT = $base++; # HTTP TLS (non-stunnel) server port +$HTTPTLS6PORT = $base++; # HTTP TLS (non-stunnel) IPv6 server port +$HTTPPROXYPORT = $base++; # HTTP proxy port, when using CONNECT +$HTTPPIPEPORT = $base++; # HTTP pipelining port +$HTTP2PORT = $base++; # HTTP/2 port +$HTTPUNIXPATH = 'http.sock'; # HTTP server Unix domain socket path + +####################################################################### +# clear and create logging directory: +# + +cleardir($LOGDIR); +mkdir($LOGDIR, 0777); + +####################################################################### +# initialize some variables +# + +get_disttests(); +init_serverpidfile_hash(); + +####################################################################### +# Output curl version and host info being tested +# + +if(!$listonly) { + checksystem(); +} + +####################################################################### +# Fetch all disabled tests, if there are any +# + +sub disabledtests { + my ($file) = @_; + + if(open(D, "<$file")) { + while() { + if(/^ *\#/) { + # allow comments + next; + } + if($_ =~ /(\d+)/) { + $disabled{$1}=$1; # disable this test number + } + } + close(D); + } +} + +# globally disabled tests +disabledtests("$TESTDIR/DISABLED"); + +# locally disabled tests, ignored by git etc +disabledtests("$TESTDIR/DISABLED.local"); + +####################################################################### +# If 'all' tests are requested, find out all test numbers +# + +if ( $TESTCASES eq "all") { + # Get all commands and find out their test numbers + opendir(DIR, $TESTDIR) || die "can't opendir $TESTDIR: $!"; + my @cmds = grep { /^test([0-9]+)$/ && -f "$TESTDIR/$_" } readdir(DIR); + closedir(DIR); + + $TESTCASES=""; # start with no test cases + + # cut off everything but the digits + for(@cmds) { + $_ =~ s/[a-z\/\.]*//g; + } + # sort the numbers from low to high + foreach my $n (sort { $a <=> $b } @cmds) { + if($disabled{$n}) { + # skip disabled test cases + my $why = "configured as DISABLED"; + $skipped++; + $skipped{$why}++; + $teststat[$n]=$why; # store reason for this test case + next; + } + $TESTCASES .= " $n"; + } +} +else { + my $verified=""; + map { + if (-e "$TESTDIR/test$_") { + $verified.="$_ "; + } + } split(" ", $TESTCASES); + if($verified eq "") { + print "No existing test cases were specified\n"; + exit; + } + $TESTCASES = $verified; +} + +####################################################################### +# Start the command line log +# +open(CMDLOG, ">$CURLLOG") || + logmsg "can't log command lines to $CURLLOG\n"; + +####################################################################### + +# Display the contents of the given file. Line endings are canonicalized +# and excessively long files are elided +sub displaylogcontent { + my ($file)=@_; + if(open(SINGLE, "<$file")) { + my $linecount = 0; + my $truncate; + my @tail; + while(my $string = ) { + $string =~ s/\r\n/\n/g; + $string =~ s/[\r\f\032]/\n/g; + $string .= "\n" unless ($string =~ /\n$/); + $string =~ tr/\n//; + for my $line (split("\n", $string)) { + $line =~ s/\s*\!$//; + if ($truncate) { + push @tail, " $line\n"; + } else { + logmsg " $line\n"; + } + $linecount++; + $truncate = $linecount > 1000; + } + } + if(@tail) { + my $tailshow = 200; + my $tailskip = 0; + my $tailtotal = scalar @tail; + if($tailtotal > $tailshow) { + $tailskip = $tailtotal - $tailshow; + logmsg "=== File too long: $tailskip lines omitted here\n"; + } + for($tailskip .. $tailtotal-1) { + logmsg "$tail[$_]"; + } + } + close(SINGLE); + } +} + +sub displaylogs { + my ($testnum)=@_; + opendir(DIR, "$LOGDIR") || + die "can't open dir: $!"; + my @logs = readdir(DIR); + closedir(DIR); + + logmsg "== Contents of files in the $LOGDIR/ dir after test $testnum\n"; + foreach my $log (sort @logs) { + if($log =~ /\.(\.|)$/) { + next; # skip "." and ".." + } + if($log =~ /^\.nfs/) { + next; # skip ".nfs" + } + if(($log eq "memdump") || ($log eq "core")) { + next; # skip "memdump" and "core" + } + if((-d "$LOGDIR/$log") || (! -s "$LOGDIR/$log")) { + next; # skip directory and empty files + } + if(($log =~ /^stdout\d+/) && ($log !~ /^stdout$testnum/)) { + next; # skip stdoutNnn of other tests + } + if(($log =~ /^stderr\d+/) && ($log !~ /^stderr$testnum/)) { + next; # skip stderrNnn of other tests + } + if(($log =~ /^upload\d+/) && ($log !~ /^upload$testnum/)) { + next; # skip uploadNnn of other tests + } + if(($log =~ /^curl\d+\.out/) && ($log !~ /^curl$testnum\.out/)) { + next; # skip curlNnn.out of other tests + } + if(($log =~ /^test\d+\.txt/) && ($log !~ /^test$testnum\.txt/)) { + next; # skip testNnn.txt of other tests + } + if(($log =~ /^file\d+\.txt/) && ($log !~ /^file$testnum\.txt/)) { + next; # skip fileNnn.txt of other tests + } + if(($log =~ /^netrc\d+/) && ($log !~ /^netrc$testnum/)) { + next; # skip netrcNnn of other tests + } + if(($log =~ /^trace\d+/) && ($log !~ /^trace$testnum/)) { + next; # skip traceNnn of other tests + } + if(($log =~ /^valgrind\d+/) && ($log !~ /^valgrind$testnum(\..*|)$/)) { + next; # skip valgrindNnn of other tests + } + logmsg "=== Start of file $log\n"; + displaylogcontent("$LOGDIR/$log"); + logmsg "=== End of file $log\n"; + } +} + +####################################################################### +# The main test-loop +# + +my $failed; +my $testnum; +my $ok=0; +my $total=0; +my $lasttest=0; +my @at = split(" ", $TESTCASES); +my $count=0; + +$start = time(); + +foreach $testnum (@at) { + + $lasttest = $testnum if($testnum > $lasttest); + $count++; + + my $error = singletest($run_event_based, $testnum, $count, scalar(@at)); + if($error < 0) { + # not a test we can run + next; + } + + $total++; # number of tests we've run + + if($error>0) { + $failed.= "$testnum "; + if($postmortem) { + # display all files in log/ in a nice way + displaylogs($testnum); + } + if(!$anyway) { + # a test failed, abort + logmsg "\n - abort tests\n"; + last; + } + } + elsif(!$error) { + $ok++; # successful test counter + } + + # loop for next test +} + +my $sofar = time() - $start; + +####################################################################### +# Close command log +# +close(CMDLOG); + +# Tests done, stop the servers +stopservers($verbose); + +my $all = $total + $skipped; + +runtimestats($lasttest); + +if($total) { + logmsg sprintf("TESTDONE: $ok tests out of $total reported OK: %d%%\n", + $ok/$total*100); + + if($ok != $total) { + logmsg "TESTFAIL: These test cases failed: $failed\n"; + } +} +else { + logmsg "TESTFAIL: No tests were performed\n"; +} + +if($all) { + logmsg "TESTDONE: $all tests were considered during ". + sprintf("%.0f", $sofar) ." seconds.\n"; +} + +if($skipped && !$short) { + my $s=0; + logmsg "TESTINFO: $skipped tests were skipped due to these restraints:\n"; + + for(keys %skipped) { + my $r = $_; + printf "TESTINFO: \"%s\" %d times (", $r, $skipped{$_}; + + # now show all test case numbers that had this reason for being + # skipped + my $c=0; + my $max = 9; + for(0 .. scalar @teststat) { + my $t = $_; + if($teststat[$_] && ($teststat[$_] eq $r)) { + if($c < $max) { + logmsg ", " if($c); + logmsg $_; + } + $c++; + } + } + if($c > $max) { + logmsg " and ".($c-$max)." more"; + } + logmsg ")\n"; + } +} + +if($total && ($ok != $total)) { + exit 1; +} diff --git a/deps/curl-7.51.0/tests/secureserver.pl b/deps/curl-7.51.0/tests/secureserver.pl new file mode 100644 index 0000000000..3a7443c403 --- /dev/null +++ b/deps/curl-7.51.0/tests/secureserver.pl @@ -0,0 +1,356 @@ +#!/usr/bin/env perl +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +#*************************************************************************** + +# This is the HTTPS, FTPS, POP3S, IMAPS, SMTPS, server used for curl test +# harness. Actually just a layer that runs stunnel properly using the +# non-secure test harness servers. + +BEGIN { + push(@INC, $ENV{'srcdir'}) if(defined $ENV{'srcdir'}); + push(@INC, "."); +} + +use strict; +use warnings; +use Cwd; +use Cwd 'abs_path'; + +use serverhelp qw( + server_pidfilename + server_logfilename + ); + +use pathhelp; + +my $stunnel = "stunnel"; + +my $verbose=0; # set to 1 for debugging + +my $accept_port = 8991; # just our default, weird enough +my $target_port = 8999; # default test http-server port + +my $stuncert; + +my $ver_major; +my $ver_minor; +my $fips_support; +my $stunnel_version; +my $tstunnel_windows; +my $socketopt; +my $cmd; + +my $pidfile; # stunnel pid file +my $logfile; # stunnel log file +my $loglevel = 5; # stunnel log level +my $ipvnum = 4; # default IP version of stunneled server +my $idnum = 1; # dafault stunneled server instance number +my $proto = 'https'; # default secure server protocol +my $conffile; # stunnel configuration file +my $capath; # certificate chain PEM folder +my $certfile; # certificate chain PEM file + +#*************************************************************************** +# stunnel requires full path specification for several files. +# +my $path = getcwd(); +my $srcdir = $path; +my $logdir = $path .'/log'; + +#*************************************************************************** +# Signal handler to remove our stunnel 4.00 and newer configuration file. +# +sub exit_signal_handler { + my $signame = shift; + local $!; # preserve errno + local $?; # preserve exit status + unlink($conffile) if($conffile && (-f $conffile)); + exit; +} + +#*************************************************************************** +# Process command line options +# +while(@ARGV) { + if($ARGV[0] eq '--verbose') { + $verbose = 1; + } + elsif($ARGV[0] eq '--proto') { + if($ARGV[1]) { + $proto = $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--accept') { + if($ARGV[1]) { + if($ARGV[1] =~ /^(\d+)$/) { + $accept_port = $1; + shift @ARGV; + } + } + } + elsif($ARGV[0] eq '--connect') { + if($ARGV[1]) { + if($ARGV[1] =~ /^(\d+)$/) { + $target_port = $1; + shift @ARGV; + } + } + } + elsif($ARGV[0] eq '--stunnel') { + if($ARGV[1]) { + if($ARGV[1] =~ /^([\w\/]+)$/) { + $stunnel = $ARGV[1]; + } + else { + $stunnel = "\"". $ARGV[1] ."\""; + } + shift @ARGV; + } + } + elsif($ARGV[0] eq '--srcdir') { + if($ARGV[1]) { + $srcdir = $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--certfile') { + if($ARGV[1]) { + $stuncert = $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--id') { + if($ARGV[1]) { + if($ARGV[1] =~ /^(\d+)$/) { + $idnum = $1 if($1 > 0); + shift @ARGV; + } + } + } + elsif($ARGV[0] eq '--ipv4') { + $ipvnum = 4; + } + elsif($ARGV[0] eq '--ipv6') { + $ipvnum = 6; + } + elsif($ARGV[0] eq '--pidfile') { + if($ARGV[1]) { + $pidfile = "$path/". $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--logfile') { + if($ARGV[1]) { + $logfile = "$path/". $ARGV[1]; + shift @ARGV; + } + } + else { + print STDERR "\nWarning: secureserver.pl unknown parameter: $ARGV[0]\n"; + } + shift @ARGV; +} + +#*************************************************************************** +# Initialize command line option dependant variables +# +if(!$pidfile) { + $pidfile = "$path/". server_pidfilename($proto, $ipvnum, $idnum); +} +if(!$logfile) { + $logfile = server_logfilename($logdir, $proto, $ipvnum, $idnum); +} + +$conffile = "$path/stunnel.conf"; + +$capath = abs_path($path); +$certfile = "$srcdir/". ($stuncert?"certs/$stuncert":"stunnel.pem"); +$certfile = abs_path($certfile); + +my $ssltext = uc($proto) ." SSL/TLS:"; + +#*************************************************************************** +# Find out version info for the given stunnel binary +# +foreach my $veropt (('-version', '-V')) { + foreach my $verstr (qx($stunnel $veropt 2>&1)) { + if($verstr =~ /^stunnel (\d+)\.(\d+) on /) { + $ver_major = $1; + $ver_minor = $2; + } + elsif($verstr =~ /^sslVersion.*fips *= *yes/) { + # the fips option causes an error if stunnel doesn't support it + $fips_support = 1; + last + } + } + last if($ver_major); +} +if((!$ver_major) || (!$ver_minor)) { + if(-x "$stunnel" && ! -d "$stunnel") { + print "$ssltext Unknown stunnel version\n"; + } + else { + print "$ssltext No stunnel\n"; + } + exit 1; +} +$stunnel_version = (100*$ver_major) + $ver_minor; + +#*************************************************************************** +# Verify minimum stunnel required version +# +if($stunnel_version < 310) { + print "$ssltext Unsupported stunnel version $ver_major.$ver_minor\n"; + exit 1; +} + +#*************************************************************************** +# Find out if we are running on Windows using the tstunnel binary +# +if($stunnel =~ /tstunnel(\.exe)?"?$/) { + $tstunnel_windows = 1; + + # convert Cygwin/MinGW paths to Win32 format + $capath = pathhelp::sys_native_abs_path($capath); + $certfile = pathhelp::sys_native_abs_path($certfile); +} + +#*************************************************************************** +# Build command to execute for stunnel 3.X versions +# +if($stunnel_version < 400) { + if($stunnel_version >= 319) { + $socketopt = "-O a:SO_REUSEADDR=1"; + } + $cmd = "$stunnel -p $certfile -P $pidfile "; + $cmd .= "-d $accept_port -r $target_port -f -D $loglevel "; + $cmd .= ($socketopt) ? "$socketopt " : ""; + $cmd .= ">$logfile 2>&1"; + if($verbose) { + print uc($proto) ." server (stunnel $ver_major.$ver_minor)\n"; + print "cmd: $cmd\n"; + print "pem cert file: $certfile\n"; + print "pid file: $pidfile\n"; + print "log file: $logfile\n"; + print "log level: $loglevel\n"; + print "listen on port: $accept_port\n"; + print "connect to port: $target_port\n"; + } +} + +#*************************************************************************** +# Build command to execute for stunnel 4.00 and newer +# +if($stunnel_version >= 400) { + $socketopt = "a:SO_REUSEADDR=1"; + $cmd = "$stunnel $conffile "; + $cmd .= ">$logfile 2>&1"; + # setup signal handler + $SIG{INT} = \&exit_signal_handler; + $SIG{TERM} = \&exit_signal_handler; + # stunnel configuration file + if(open(STUNCONF, ">$conffile")) { + print STUNCONF "CApath = $capath\n"; + print STUNCONF "cert = $certfile\n"; + print STUNCONF "debug = $loglevel\n"; + print STUNCONF "socket = $socketopt\n"; + if($fips_support) { + # disable fips in case OpenSSL doesn't support it + print STUNCONF "fips = no\n"; + } + if(!$tstunnel_windows) { + # do not use Linux-specific options on Windows + print STUNCONF "output = $logfile\n"; + print STUNCONF "pid = $pidfile\n"; + print STUNCONF "foreground = yes\n"; + } + print STUNCONF "\n"; + print STUNCONF "[curltest]\n"; + print STUNCONF "accept = $accept_port\n"; + print STUNCONF "connect = $target_port\n"; + if(!close(STUNCONF)) { + print "$ssltext Error closing file $conffile\n"; + exit 1; + } + } + else { + print "$ssltext Error writing file $conffile\n"; + exit 1; + } + if($verbose) { + print uc($proto) ." server (stunnel $ver_major.$ver_minor)\n"; + print "cmd: $cmd\n"; + print "CApath = $capath\n"; + print "cert = $certfile\n"; + print "debug = $loglevel\n"; + print "socket = $socketopt\n"; + if($fips_support) { + print "fips = no\n"; + } + if(!$tstunnel_windows) { + print "pid = $pidfile\n"; + print "output = $logfile\n"; + print "foreground = yes\n"; + } + print "\n"; + print "[curltest]\n"; + print "accept = $accept_port\n"; + print "connect = $target_port\n"; + } +} + +#*************************************************************************** +# Set file permissions on certificate pem file. +# +chmod(0600, $certfile) if(-f $certfile); + +#*************************************************************************** +# Run tstunnel on Windows. +# +if($tstunnel_windows) { + # Fake pidfile for tstunnel on Windows. + if(open(OUT, ">$pidfile")) { + print OUT $$ . "\n"; + close(OUT); + } + + # Put an "exec" in front of the command so that the child process + # keeps this child's process ID. + exec("exec $cmd") || die "Can't exec() $cmd: $!"; + + # exec() should never return back here to this process. We protect + # ourselves by calling die() just in case something goes really bad. + die "error: exec() has returned"; +} + +#*************************************************************************** +# Run stunnel. +# +my $rc = system($cmd); + +$rc >>= 8; + +unlink($conffile) if($conffile && -f $conffile); + +exit $rc; diff --git a/deps/curl-7.51.0/tests/server/CMakeLists.txt b/deps/curl-7.51.0/tests/server/CMakeLists.txt new file mode 100644 index 0000000000..00f5242f92 --- /dev/null +++ b/deps/curl-7.51.0/tests/server/CMakeLists.txt @@ -0,0 +1,62 @@ +set(TARGET_LABEL_PREFIX "Test server ") + +function(SETUP_EXECUTABLE TEST_NAME) # ARGN are the files in the test + add_executable( ${TEST_NAME} ${ARGN} ) + string(TOUPPER ${TEST_NAME} UPPER_TEST_NAME) + + include_directories( + ${CURL_SOURCE_DIR}/lib # To be able to reach "curl_setup_once.h" + ${CURL_BINARY_DIR}/lib # To be able to reach "curl_config.h" + ${CURL_BINARY_DIR}/include # To be able to reach "curl/curlbuild.h" + ) + if(USE_ARES) + include_directories(${CARES_INCLUDE_DIR}) + endif() + + target_link_libraries(${TEST_NAME} ${CURL_LIBS}) + + # Test servers simply are standalone programs that do not use libcurl + # library. For convinience and to ease portability of these servers, + # some source code files from the libcurl subdirectory are also used + # to build the servers. In order to achieve proper linkage of these + # files on Win32 targets it is necessary to build the test servers + # with CURL_STATICLIB defined, independently of how libcurl is built. + if(NOT CURL_STATICLIB) + set_target_properties(${TEST_NAME} PROPERTIES + COMPILE_DEFINITIONS CURL_STATICLIB) # ${UPPER_TEST_NAME} + endif() + set_target_properties(${TEST_NAME} PROPERTIES + PROJECT_LABEL "${TARGET_LABEL_PREFIX}${TEST_NAME}") + + # Add the postfix to the executable since it is not added + # automatically as for modules and shared libraries + set_target_properties(${TEST_NAME} PROPERTIES + DEBUG_POSTFIX "${CMAKE_DEBUG_POSTFIX}") + +endfunction() + + +transform_makefile_inc("Makefile.inc" + "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") +include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake) + +foreach(EXECUTABLE_NAME ${noinst_PROGRAMS}) + setup_executable(${EXECUTABLE_NAME} ${${EXECUTABLE_NAME}_SOURCES}) +endforeach() + + +# SET(useful +# getpart.c getpart.h +# ${CURL_SOURCE_DIR}/lib/strequal.c +# ${CURL_SOURCE_DIR}/lib/base64.c +# ${CURL_SOURCE_DIR}/lib/mprintf.c +# ${CURL_SOURCE_DIR}/lib/memdebug.c +# ${CURL_SOURCE_DIR}/lib/timeval.c +# ) + +# SETUP_EXECUTABLE(sws sws.c util.c util.h ${useful}) +# SETUP_EXECUTABLE(resolve resolve.c util.c util.h ${useful}) +# SETUP_EXECUTABLE(sockfilt sockfilt.c util.c util.h ${useful} ${CURL_SOURCE_DIR}/lib/inet_pton.c) +# SETUP_EXECUTABLE(getpart testpart.c ${useful}) +# SETUP_EXECUTABLE(tftpd tftpd.c util.c util.h ${useful} tftp.h) + diff --git a/deps/curl-7.51.0/tests/server/Makefile.am b/deps/curl-7.51.0/tests/server/Makefile.am new file mode 100644 index 0000000000..e274c01ab0 --- /dev/null +++ b/deps/curl-7.51.0/tests/server/Makefile.am @@ -0,0 +1,66 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### +AUTOMAKE_OPTIONS = foreign nostdinc + +# Specify our include paths here, and do it relative to $(top_srcdir) and +# $(top_builddir), to ensure that these paths which belong to the library +# being currently built and tested are searched before the library which +# might possibly already be installed in the system. +# +# $(top_builddir)/include/curl for generated curlbuild.h included from curl.h +# $(top_builddir)/include for generated curlbuild.h inc. from lib/curl_setup.h +# $(top_srcdir)/include is for libcurl's external include files +# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file +# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "borrowed" files +# $(top_builddir)/ares is for in-tree c-ares's generated ares_build.h file +# $(top_srcdir)/ares is for in-tree c-ares's external include files + +if USE_EMBEDDED_ARES +AM_CPPFLAGS = -I$(top_builddir)/include/curl \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/lib \ + -I$(top_srcdir)/lib \ + -I$(top_builddir)/ares \ + -I$(top_srcdir)/ares +else +AM_CPPFLAGS = -I$(top_builddir)/include/curl \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/lib \ + -I$(top_srcdir)/lib +endif + +# Prevent LIBS from being used for all link targets +LIBS = $(BLANK_AT_MAKETIME) + +if DOING_NATIVE_WINDOWS +AM_CPPFLAGS += -DCURL_STATICLIB +endif + +# Makefile.inc provides neat definitions +include Makefile.inc + +EXTRA_DIST = base64.pl Makefile.inc CMakeLists.txt + +checksrc: + @PERL@ $(top_srcdir)/lib/checksrc.pl $(srcdir)/*.c diff --git a/deps/curl-7.51.0/tests/server/Makefile.in b/deps/curl-7.51.0/tests/server/Makefile.in new file mode 100644 index 0000000000..798197527a --- /dev/null +++ b/deps/curl-7.51.0/tests/server/Makefile.in @@ -0,0 +1,2118 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@DOING_NATIVE_WINDOWS_TRUE@am__append_1 = -DCURL_STATICLIB +noinst_PROGRAMS = getpart$(EXEEXT) resolve$(EXEEXT) rtspd$(EXEEXT) \ + sockfilt$(EXEEXT) sws$(EXEEXT) tftpd$(EXEEXT) \ + fake_ntlm$(EXEEXT) +subdir = tests/server +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/curl-compilers.m4 \ + $(top_srcdir)/m4/curl-confopts.m4 \ + $(top_srcdir)/m4/curl-functions.m4 \ + $(top_srcdir)/m4/curl-openssl.m4 \ + $(top_srcdir)/m4/curl-override.m4 \ + $(top_srcdir)/m4/curl-reentrant.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/xc-am-iface.m4 \ + $(top_srcdir)/m4/xc-cc-check.m4 \ + $(top_srcdir)/m4/xc-lt-iface.m4 \ + $(top_srcdir)/m4/xc-translit.m4 \ + $(top_srcdir)/m4/xc-val-flgs.m4 \ + $(top_srcdir)/m4/zz40-xc-ovr.m4 \ + $(top_srcdir)/m4/zz50-xc-ovr.m4 \ + $(top_srcdir)/m4/zz60-xc-ovr.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/lib/curl_config.h \ + $(top_builddir)/include/curl/curlbuild.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +PROGRAMS = $(noinst_PROGRAMS) +am__dirstamp = $(am__leading_dot)dirstamp +am__objects_1 = ../../lib/fake_ntlm-mprintf.$(OBJEXT) \ + ../../lib/fake_ntlm-nonblock.$(OBJEXT) \ + ../../lib/fake_ntlm-strtoofft.$(OBJEXT) \ + ../../lib/fake_ntlm-timeval.$(OBJEXT) \ + ../../lib/fake_ntlm-warnless.$(OBJEXT) +am__objects_2 = +am__objects_3 = fake_ntlm-getpart.$(OBJEXT) \ + ../../lib/fake_ntlm-base64.$(OBJEXT) \ + ../../lib/fake_ntlm-memdebug.$(OBJEXT) +am__objects_4 = fake_ntlm-util.$(OBJEXT) +am_fake_ntlm_OBJECTS = $(am__objects_1) $(am__objects_2) \ + $(am__objects_3) $(am__objects_4) \ + fake_ntlm-fake_ntlm.$(OBJEXT) +fake_ntlm_OBJECTS = $(am_fake_ntlm_OBJECTS) +fake_ntlm_DEPENDENCIES = +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +fake_ntlm_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(fake_ntlm_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am__objects_5 = ../../lib/getpart-mprintf.$(OBJEXT) \ + ../../lib/getpart-nonblock.$(OBJEXT) \ + ../../lib/getpart-strtoofft.$(OBJEXT) \ + ../../lib/getpart-timeval.$(OBJEXT) \ + ../../lib/getpart-warnless.$(OBJEXT) +am__objects_6 = getpart-getpart.$(OBJEXT) \ + ../../lib/getpart-base64.$(OBJEXT) \ + ../../lib/getpart-memdebug.$(OBJEXT) +am_getpart_OBJECTS = $(am__objects_5) $(am__objects_2) \ + $(am__objects_6) getpart-testpart.$(OBJEXT) +getpart_OBJECTS = $(am_getpart_OBJECTS) +getpart_DEPENDENCIES = +getpart_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(getpart_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am__objects_7 = ../../lib/resolve-mprintf.$(OBJEXT) \ + ../../lib/resolve-nonblock.$(OBJEXT) \ + ../../lib/resolve-strtoofft.$(OBJEXT) \ + ../../lib/resolve-timeval.$(OBJEXT) \ + ../../lib/resolve-warnless.$(OBJEXT) +am__objects_8 = resolve-getpart.$(OBJEXT) \ + ../../lib/resolve-base64.$(OBJEXT) \ + ../../lib/resolve-memdebug.$(OBJEXT) +am__objects_9 = resolve-util.$(OBJEXT) +am_resolve_OBJECTS = $(am__objects_7) $(am__objects_2) \ + $(am__objects_8) $(am__objects_9) resolve-resolve.$(OBJEXT) +resolve_OBJECTS = $(am_resolve_OBJECTS) +resolve_DEPENDENCIES = +resolve_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(resolve_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am__objects_10 = ../../lib/rtspd-mprintf.$(OBJEXT) \ + ../../lib/rtspd-nonblock.$(OBJEXT) \ + ../../lib/rtspd-strtoofft.$(OBJEXT) \ + ../../lib/rtspd-timeval.$(OBJEXT) \ + ../../lib/rtspd-warnless.$(OBJEXT) +am__objects_11 = rtspd-getpart.$(OBJEXT) \ + ../../lib/rtspd-base64.$(OBJEXT) \ + ../../lib/rtspd-memdebug.$(OBJEXT) +am__objects_12 = rtspd-util.$(OBJEXT) +am_rtspd_OBJECTS = $(am__objects_10) $(am__objects_2) \ + $(am__objects_11) $(am__objects_12) rtspd-rtspd.$(OBJEXT) +rtspd_OBJECTS = $(am_rtspd_OBJECTS) +rtspd_DEPENDENCIES = +rtspd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(rtspd_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am__objects_13 = ../../lib/sockfilt-mprintf.$(OBJEXT) \ + ../../lib/sockfilt-nonblock.$(OBJEXT) \ + ../../lib/sockfilt-strtoofft.$(OBJEXT) \ + ../../lib/sockfilt-timeval.$(OBJEXT) \ + ../../lib/sockfilt-warnless.$(OBJEXT) +am__objects_14 = sockfilt-getpart.$(OBJEXT) \ + ../../lib/sockfilt-base64.$(OBJEXT) \ + ../../lib/sockfilt-memdebug.$(OBJEXT) +am__objects_15 = sockfilt-util.$(OBJEXT) +am_sockfilt_OBJECTS = $(am__objects_13) $(am__objects_2) \ + $(am__objects_14) $(am__objects_15) \ + sockfilt-sockfilt.$(OBJEXT) \ + ../../lib/sockfilt-inet_pton.$(OBJEXT) +sockfilt_OBJECTS = $(am_sockfilt_OBJECTS) +sockfilt_DEPENDENCIES = +sockfilt_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(sockfilt_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am__objects_16 = ../../lib/sws-mprintf.$(OBJEXT) \ + ../../lib/sws-nonblock.$(OBJEXT) \ + ../../lib/sws-strtoofft.$(OBJEXT) \ + ../../lib/sws-timeval.$(OBJEXT) \ + ../../lib/sws-warnless.$(OBJEXT) +am__objects_17 = sws-getpart.$(OBJEXT) ../../lib/sws-base64.$(OBJEXT) \ + ../../lib/sws-memdebug.$(OBJEXT) +am__objects_18 = sws-util.$(OBJEXT) +am_sws_OBJECTS = $(am__objects_16) $(am__objects_2) $(am__objects_17) \ + $(am__objects_18) sws-sws.$(OBJEXT) \ + ../../lib/sws-inet_pton.$(OBJEXT) +sws_OBJECTS = $(am_sws_OBJECTS) +sws_DEPENDENCIES = +sws_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(sws_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am__objects_19 = ../../lib/tftpd-mprintf.$(OBJEXT) \ + ../../lib/tftpd-nonblock.$(OBJEXT) \ + ../../lib/tftpd-strtoofft.$(OBJEXT) \ + ../../lib/tftpd-timeval.$(OBJEXT) \ + ../../lib/tftpd-warnless.$(OBJEXT) +am__objects_20 = tftpd-getpart.$(OBJEXT) \ + ../../lib/tftpd-base64.$(OBJEXT) \ + ../../lib/tftpd-memdebug.$(OBJEXT) +am__objects_21 = tftpd-util.$(OBJEXT) +am_tftpd_OBJECTS = $(am__objects_19) $(am__objects_2) \ + $(am__objects_20) $(am__objects_21) tftpd-tftpd.$(OBJEXT) +tftpd_OBJECTS = $(am_tftpd_OBJECTS) +tftpd_DEPENDENCIES = +tftpd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(tftpd_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(fake_ntlm_SOURCES) $(getpart_SOURCES) $(resolve_SOURCES) \ + $(rtspd_SOURCES) $(sockfilt_SOURCES) $(sws_SOURCES) \ + $(tftpd_SOURCES) +DIST_SOURCES = $(fake_ntlm_SOURCES) $(getpart_SOURCES) \ + $(resolve_SOURCES) $(rtspd_SOURCES) $(sockfilt_SOURCES) \ + $(sws_SOURCES) $(tftpd_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.inc \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLANK_AT_MAKETIME = @BLANK_AT_MAKETIME@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CFLAG_CURL_SYMBOL_HIDING = @CFLAG_CURL_SYMBOL_HIDING@ +CONFIGURE_OPTIONS = @CONFIGURE_OPTIONS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAG_CURL_STATICLIB = @CPPFLAG_CURL_STATICLIB@ +CURLVERSION = @CURLVERSION@ +CURL_CA_BUNDLE = @CURL_CA_BUNDLE@ +CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@ +CURL_DISABLE_DICT = @CURL_DISABLE_DICT@ +CURL_DISABLE_FILE = @CURL_DISABLE_FILE@ +CURL_DISABLE_FTP = @CURL_DISABLE_FTP@ +CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@ +CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@ +CURL_DISABLE_IMAP = @CURL_DISABLE_IMAP@ +CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@ +CURL_DISABLE_LDAPS = @CURL_DISABLE_LDAPS@ +CURL_DISABLE_POP3 = @CURL_DISABLE_POP3@ +CURL_DISABLE_PROXY = @CURL_DISABLE_PROXY@ +CURL_DISABLE_RTSP = @CURL_DISABLE_RTSP@ +CURL_DISABLE_SMB = @CURL_DISABLE_SMB@ +CURL_DISABLE_SMTP = @CURL_DISABLE_SMTP@ +CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@ +CURL_DISABLE_TFTP = @CURL_DISABLE_TFTP@ +CURL_LT_SHLIB_VERSIONED_FLAVOUR = @CURL_LT_SHLIB_VERSIONED_FLAVOUR@ +CURL_NETWORK_AND_TIME_LIBS = @CURL_NETWORK_AND_TIME_LIBS@ +CURL_NETWORK_LIBS = @CURL_NETWORK_LIBS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_SHARED = @ENABLE_SHARED@ +ENABLE_STATIC = @ENABLE_STATIC@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@ +HAVE_LDAP_SSL = @HAVE_LDAP_SSL@ +HAVE_LIBZ = @HAVE_LIBZ@ +HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@ +IDN_ENABLED = @IDN_ENABLED@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPV6_ENABLED = @IPV6_ENABLED@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCURL_LIBS = @LIBCURL_LIBS@ +LIBMETALINK_CPPFLAGS = @LIBMETALINK_CPPFLAGS@ +LIBMETALINK_LDFLAGS = @LIBMETALINK_LDFLAGS@ +LIBMETALINK_LIBS = @LIBMETALINK_LIBS@ +LIBOBJS = @LIBOBJS@ + +# Prevent LIBS from being used for all link targets +LIBS = $(BLANK_AT_MAKETIME) +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MANOPT = @MANOPT@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NROFF = @NROFF@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKGADD_NAME = @PKGADD_NAME@ +PKGADD_PKG = @PKGADD_PKG@ +PKGADD_VENDOR = @PKGADD_VENDOR@ +PKGCONFIG = @PKGCONFIG@ +RANDOM_FILE = @RANDOM_FILE@ +RANLIB = @RANLIB@ +REQUIRE_LIB_DEPS = @REQUIRE_LIB_DEPS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SSL_ENABLED = @SSL_ENABLED@ +SSL_LIBS = @SSL_LIBS@ +STRIP = @STRIP@ +SUPPORT_FEATURES = @SUPPORT_FEATURES@ +SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@ +USE_ARES = @USE_ARES@ +USE_AXTLS = @USE_AXTLS@ +USE_CYASSL = @USE_CYASSL@ +USE_DARWINSSL = @USE_DARWINSSL@ +USE_GNUTLS = @USE_GNUTLS@ +USE_GNUTLS_NETTLE = @USE_GNUTLS_NETTLE@ +USE_LIBRTMP = @USE_LIBRTMP@ +USE_LIBSSH2 = @USE_LIBSSH2@ +USE_MBEDTLS = @USE_MBEDTLS@ +USE_NGHTTP2 = @USE_NGHTTP2@ +USE_NSS = @USE_NSS@ +USE_OPENLDAP = @USE_OPENLDAP@ +USE_POLARSSL = @USE_POLARSSL@ +USE_SCHANNEL = @USE_SCHANNEL@ +USE_UNIX_SOCKETS = @USE_UNIX_SOCKETS@ +USE_WINDOWS_SSPI = @USE_WINDOWS_SSPI@ +VERSION = @VERSION@ +VERSIONNUM = @VERSIONNUM@ +ZLIB_LIBS = @ZLIB_LIBS@ +ZSH_FUNCTIONS_DIR = @ZSH_FUNCTIONS_DIR@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libext = @libext@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### +AUTOMAKE_OPTIONS = foreign nostdinc +@USE_EMBEDDED_ARES_FALSE@AM_CPPFLAGS = -I$(top_builddir)/include/curl \ +@USE_EMBEDDED_ARES_FALSE@ -I$(top_builddir)/include \ +@USE_EMBEDDED_ARES_FALSE@ -I$(top_srcdir)/include \ +@USE_EMBEDDED_ARES_FALSE@ -I$(top_builddir)/lib \ +@USE_EMBEDDED_ARES_FALSE@ -I$(top_srcdir)/lib $(am__append_1) + +# Specify our include paths here, and do it relative to $(top_srcdir) and +# $(top_builddir), to ensure that these paths which belong to the library +# being currently built and tested are searched before the library which +# might possibly already be installed in the system. +# +# $(top_builddir)/include/curl for generated curlbuild.h included from curl.h +# $(top_builddir)/include for generated curlbuild.h inc. from lib/curl_setup.h +# $(top_srcdir)/include is for libcurl's external include files +# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file +# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "borrowed" files +# $(top_builddir)/ares is for in-tree c-ares's generated ares_build.h file +# $(top_srcdir)/ares is for in-tree c-ares's external include files +@USE_EMBEDDED_ARES_TRUE@AM_CPPFLAGS = -I$(top_builddir)/include/curl \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_builddir)/include \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_srcdir)/include \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_builddir)/lib \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_srcdir)/lib \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_builddir)/ares \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_srcdir)/ares $(am__append_1) +CURLX_SRCS = \ + ../../lib/mprintf.c \ + ../../lib/nonblock.c \ + ../../lib/strtoofft.c \ + ../../lib/timeval.c \ + ../../lib/warnless.c + +CURLX_HDRS = \ + ../../lib/curlx.h \ + ../../lib/nonblock.h \ + ../../lib/strtoofft.h \ + ../../lib/timeval.h \ + ../../lib/warnless.h + +USEFUL = \ + getpart.c \ + getpart.h \ + server_setup.h \ + ../../lib/base64.c \ + ../../lib/curl_base64.h \ + ../../lib/memdebug.c \ + ../../lib/memdebug.h + +UTIL = \ + util.c \ + util.h + +getpart_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) \ + testpart.c + +getpart_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +getpart_CFLAGS = $(AM_CFLAGS) +resolve_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \ + resolve.c + +resolve_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +resolve_CFLAGS = $(AM_CFLAGS) +rtspd_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \ + server_sockaddr.h \ + rtspd.c + +rtspd_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +rtspd_CFLAGS = $(AM_CFLAGS) +sockfilt_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \ + server_sockaddr.h \ + sockfilt.c \ + ../../lib/inet_pton.c + +sockfilt_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +sockfilt_CFLAGS = $(AM_CFLAGS) +sws_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \ + server_sockaddr.h \ + sws.c \ + ../../lib/inet_pton.c + +sws_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +sws_CFLAGS = $(AM_CFLAGS) +tftpd_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \ + server_sockaddr.h \ + tftpd.c \ + tftp.h + +tftpd_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +tftpd_CFLAGS = $(AM_CFLAGS) +fake_ntlm_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \ + fake_ntlm.c + +fake_ntlm_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +fake_ntlm_CFLAGS = $(AM_CFLAGS) + +# Makefile.inc provides neat definitions +EXTRA_DIST = base64.pl Makefile.inc CMakeLists.txt +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/Makefile.inc $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tests/server/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign tests/server/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; +$(srcdir)/Makefile.inc $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +../../lib/$(am__dirstamp): + @$(MKDIR_P) ../../lib + @: > ../../lib/$(am__dirstamp) +../../lib/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) ../../lib/$(DEPDIR) + @: > ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/fake_ntlm-mprintf.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/fake_ntlm-nonblock.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/fake_ntlm-strtoofft.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/fake_ntlm-timeval.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/fake_ntlm-warnless.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/fake_ntlm-base64.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/fake_ntlm-memdebug.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) + +fake_ntlm$(EXEEXT): $(fake_ntlm_OBJECTS) $(fake_ntlm_DEPENDENCIES) $(EXTRA_fake_ntlm_DEPENDENCIES) + @rm -f fake_ntlm$(EXEEXT) + $(AM_V_CCLD)$(fake_ntlm_LINK) $(fake_ntlm_OBJECTS) $(fake_ntlm_LDADD) $(LIBS) +../../lib/getpart-mprintf.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/getpart-nonblock.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/getpart-strtoofft.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/getpart-timeval.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/getpart-warnless.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/getpart-base64.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/getpart-memdebug.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) + +getpart$(EXEEXT): $(getpart_OBJECTS) $(getpart_DEPENDENCIES) $(EXTRA_getpart_DEPENDENCIES) + @rm -f getpart$(EXEEXT) + $(AM_V_CCLD)$(getpart_LINK) $(getpart_OBJECTS) $(getpart_LDADD) $(LIBS) +../../lib/resolve-mprintf.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/resolve-nonblock.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/resolve-strtoofft.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/resolve-timeval.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/resolve-warnless.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/resolve-base64.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/resolve-memdebug.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) + +resolve$(EXEEXT): $(resolve_OBJECTS) $(resolve_DEPENDENCIES) $(EXTRA_resolve_DEPENDENCIES) + @rm -f resolve$(EXEEXT) + $(AM_V_CCLD)$(resolve_LINK) $(resolve_OBJECTS) $(resolve_LDADD) $(LIBS) +../../lib/rtspd-mprintf.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/rtspd-nonblock.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/rtspd-strtoofft.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/rtspd-timeval.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/rtspd-warnless.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/rtspd-base64.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/rtspd-memdebug.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) + +rtspd$(EXEEXT): $(rtspd_OBJECTS) $(rtspd_DEPENDENCIES) $(EXTRA_rtspd_DEPENDENCIES) + @rm -f rtspd$(EXEEXT) + $(AM_V_CCLD)$(rtspd_LINK) $(rtspd_OBJECTS) $(rtspd_LDADD) $(LIBS) +../../lib/sockfilt-mprintf.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sockfilt-nonblock.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sockfilt-strtoofft.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sockfilt-timeval.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sockfilt-warnless.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sockfilt-base64.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sockfilt-memdebug.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sockfilt-inet_pton.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) + +sockfilt$(EXEEXT): $(sockfilt_OBJECTS) $(sockfilt_DEPENDENCIES) $(EXTRA_sockfilt_DEPENDENCIES) + @rm -f sockfilt$(EXEEXT) + $(AM_V_CCLD)$(sockfilt_LINK) $(sockfilt_OBJECTS) $(sockfilt_LDADD) $(LIBS) +../../lib/sws-mprintf.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sws-nonblock.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sws-strtoofft.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sws-timeval.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sws-warnless.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sws-base64.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sws-memdebug.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/sws-inet_pton.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) + +sws$(EXEEXT): $(sws_OBJECTS) $(sws_DEPENDENCIES) $(EXTRA_sws_DEPENDENCIES) + @rm -f sws$(EXEEXT) + $(AM_V_CCLD)$(sws_LINK) $(sws_OBJECTS) $(sws_LDADD) $(LIBS) +../../lib/tftpd-mprintf.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/tftpd-nonblock.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/tftpd-strtoofft.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/tftpd-timeval.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/tftpd-warnless.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/tftpd-base64.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) +../../lib/tftpd-memdebug.$(OBJEXT): ../../lib/$(am__dirstamp) \ + ../../lib/$(DEPDIR)/$(am__dirstamp) + +tftpd$(EXEEXT): $(tftpd_OBJECTS) $(tftpd_DEPENDENCIES) $(EXTRA_tftpd_DEPENDENCIES) + @rm -f tftpd$(EXEEXT) + $(AM_V_CCLD)$(tftpd_LINK) $(tftpd_OBJECTS) $(tftpd_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f ../../lib/*.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/fake_ntlm-base64.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/fake_ntlm-memdebug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/fake_ntlm-mprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/fake_ntlm-nonblock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/fake_ntlm-strtoofft.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/fake_ntlm-timeval.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/fake_ntlm-warnless.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/getpart-base64.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/getpart-memdebug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/getpart-mprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/getpart-nonblock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/getpart-strtoofft.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/getpart-timeval.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/getpart-warnless.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/resolve-base64.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/resolve-memdebug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/resolve-mprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/resolve-nonblock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/resolve-strtoofft.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/resolve-timeval.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/resolve-warnless.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/rtspd-base64.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/rtspd-memdebug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/rtspd-mprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/rtspd-nonblock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/rtspd-strtoofft.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/rtspd-timeval.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/rtspd-warnless.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sockfilt-base64.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sockfilt-inet_pton.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sockfilt-memdebug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sockfilt-mprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sockfilt-nonblock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sockfilt-strtoofft.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sockfilt-timeval.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sockfilt-warnless.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sws-base64.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sws-inet_pton.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sws-memdebug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sws-mprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sws-nonblock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sws-strtoofft.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sws-timeval.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/sws-warnless.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/tftpd-base64.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/tftpd-memdebug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/tftpd-mprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/tftpd-nonblock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/tftpd-strtoofft.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/tftpd-timeval.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../../lib/$(DEPDIR)/tftpd-warnless.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fake_ntlm-fake_ntlm.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fake_ntlm-getpart.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fake_ntlm-util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getpart-getpart.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getpart-testpart.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolve-getpart.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolve-resolve.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolve-util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rtspd-getpart.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rtspd-rtspd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rtspd-util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sockfilt-getpart.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sockfilt-sockfilt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sockfilt-util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sws-getpart.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sws-sws.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sws-util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tftpd-getpart.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tftpd-tftpd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tftpd-util.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +../../lib/fake_ntlm-mprintf.o: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-mprintf.o -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-mprintf.Tpo -c -o ../../lib/fake_ntlm-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-mprintf.Tpo ../../lib/$(DEPDIR)/fake_ntlm-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/fake_ntlm-mprintf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c + +../../lib/fake_ntlm-mprintf.obj: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-mprintf.obj -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-mprintf.Tpo -c -o ../../lib/fake_ntlm-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-mprintf.Tpo ../../lib/$(DEPDIR)/fake_ntlm-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/fake_ntlm-mprintf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` + +../../lib/fake_ntlm-nonblock.o: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-nonblock.o -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-nonblock.Tpo -c -o ../../lib/fake_ntlm-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-nonblock.Tpo ../../lib/$(DEPDIR)/fake_ntlm-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/fake_ntlm-nonblock.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c + +../../lib/fake_ntlm-nonblock.obj: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-nonblock.obj -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-nonblock.Tpo -c -o ../../lib/fake_ntlm-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-nonblock.Tpo ../../lib/$(DEPDIR)/fake_ntlm-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/fake_ntlm-nonblock.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` + +../../lib/fake_ntlm-strtoofft.o: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-strtoofft.o -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-strtoofft.Tpo -c -o ../../lib/fake_ntlm-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-strtoofft.Tpo ../../lib/$(DEPDIR)/fake_ntlm-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/fake_ntlm-strtoofft.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c + +../../lib/fake_ntlm-strtoofft.obj: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-strtoofft.obj -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-strtoofft.Tpo -c -o ../../lib/fake_ntlm-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-strtoofft.Tpo ../../lib/$(DEPDIR)/fake_ntlm-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/fake_ntlm-strtoofft.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` + +../../lib/fake_ntlm-timeval.o: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-timeval.o -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-timeval.Tpo -c -o ../../lib/fake_ntlm-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-timeval.Tpo ../../lib/$(DEPDIR)/fake_ntlm-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/fake_ntlm-timeval.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c + +../../lib/fake_ntlm-timeval.obj: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-timeval.obj -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-timeval.Tpo -c -o ../../lib/fake_ntlm-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-timeval.Tpo ../../lib/$(DEPDIR)/fake_ntlm-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/fake_ntlm-timeval.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` + +../../lib/fake_ntlm-warnless.o: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-warnless.o -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-warnless.Tpo -c -o ../../lib/fake_ntlm-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-warnless.Tpo ../../lib/$(DEPDIR)/fake_ntlm-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/fake_ntlm-warnless.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c + +../../lib/fake_ntlm-warnless.obj: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-warnless.obj -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-warnless.Tpo -c -o ../../lib/fake_ntlm-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-warnless.Tpo ../../lib/$(DEPDIR)/fake_ntlm-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/fake_ntlm-warnless.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` + +fake_ntlm-getpart.o: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT fake_ntlm-getpart.o -MD -MP -MF $(DEPDIR)/fake_ntlm-getpart.Tpo -c -o fake_ntlm-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fake_ntlm-getpart.Tpo $(DEPDIR)/fake_ntlm-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='fake_ntlm-getpart.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o fake_ntlm-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c + +fake_ntlm-getpart.obj: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT fake_ntlm-getpart.obj -MD -MP -MF $(DEPDIR)/fake_ntlm-getpart.Tpo -c -o fake_ntlm-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fake_ntlm-getpart.Tpo $(DEPDIR)/fake_ntlm-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='fake_ntlm-getpart.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o fake_ntlm-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` + +../../lib/fake_ntlm-base64.o: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-base64.o -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-base64.Tpo -c -o ../../lib/fake_ntlm-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-base64.Tpo ../../lib/$(DEPDIR)/fake_ntlm-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/fake_ntlm-base64.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c + +../../lib/fake_ntlm-base64.obj: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-base64.obj -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-base64.Tpo -c -o ../../lib/fake_ntlm-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-base64.Tpo ../../lib/$(DEPDIR)/fake_ntlm-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/fake_ntlm-base64.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` + +../../lib/fake_ntlm-memdebug.o: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-memdebug.o -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-memdebug.Tpo -c -o ../../lib/fake_ntlm-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-memdebug.Tpo ../../lib/$(DEPDIR)/fake_ntlm-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/fake_ntlm-memdebug.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c + +../../lib/fake_ntlm-memdebug.obj: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT ../../lib/fake_ntlm-memdebug.obj -MD -MP -MF ../../lib/$(DEPDIR)/fake_ntlm-memdebug.Tpo -c -o ../../lib/fake_ntlm-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/fake_ntlm-memdebug.Tpo ../../lib/$(DEPDIR)/fake_ntlm-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/fake_ntlm-memdebug.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o ../../lib/fake_ntlm-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` + +fake_ntlm-util.o: util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT fake_ntlm-util.o -MD -MP -MF $(DEPDIR)/fake_ntlm-util.Tpo -c -o fake_ntlm-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fake_ntlm-util.Tpo $(DEPDIR)/fake_ntlm-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='fake_ntlm-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o fake_ntlm-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c + +fake_ntlm-util.obj: util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT fake_ntlm-util.obj -MD -MP -MF $(DEPDIR)/fake_ntlm-util.Tpo -c -o fake_ntlm-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fake_ntlm-util.Tpo $(DEPDIR)/fake_ntlm-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='fake_ntlm-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o fake_ntlm-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` + +fake_ntlm-fake_ntlm.o: fake_ntlm.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT fake_ntlm-fake_ntlm.o -MD -MP -MF $(DEPDIR)/fake_ntlm-fake_ntlm.Tpo -c -o fake_ntlm-fake_ntlm.o `test -f 'fake_ntlm.c' || echo '$(srcdir)/'`fake_ntlm.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fake_ntlm-fake_ntlm.Tpo $(DEPDIR)/fake_ntlm-fake_ntlm.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fake_ntlm.c' object='fake_ntlm-fake_ntlm.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o fake_ntlm-fake_ntlm.o `test -f 'fake_ntlm.c' || echo '$(srcdir)/'`fake_ntlm.c + +fake_ntlm-fake_ntlm.obj: fake_ntlm.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -MT fake_ntlm-fake_ntlm.obj -MD -MP -MF $(DEPDIR)/fake_ntlm-fake_ntlm.Tpo -c -o fake_ntlm-fake_ntlm.obj `if test -f 'fake_ntlm.c'; then $(CYGPATH_W) 'fake_ntlm.c'; else $(CYGPATH_W) '$(srcdir)/fake_ntlm.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fake_ntlm-fake_ntlm.Tpo $(DEPDIR)/fake_ntlm-fake_ntlm.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fake_ntlm.c' object='fake_ntlm-fake_ntlm.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(fake_ntlm_CFLAGS) $(CFLAGS) -c -o fake_ntlm-fake_ntlm.obj `if test -f 'fake_ntlm.c'; then $(CYGPATH_W) 'fake_ntlm.c'; else $(CYGPATH_W) '$(srcdir)/fake_ntlm.c'; fi` + +../../lib/getpart-mprintf.o: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-mprintf.o -MD -MP -MF ../../lib/$(DEPDIR)/getpart-mprintf.Tpo -c -o ../../lib/getpart-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-mprintf.Tpo ../../lib/$(DEPDIR)/getpart-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/getpart-mprintf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c + +../../lib/getpart-mprintf.obj: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-mprintf.obj -MD -MP -MF ../../lib/$(DEPDIR)/getpart-mprintf.Tpo -c -o ../../lib/getpart-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-mprintf.Tpo ../../lib/$(DEPDIR)/getpart-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/getpart-mprintf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` + +../../lib/getpart-nonblock.o: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-nonblock.o -MD -MP -MF ../../lib/$(DEPDIR)/getpart-nonblock.Tpo -c -o ../../lib/getpart-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-nonblock.Tpo ../../lib/$(DEPDIR)/getpart-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/getpart-nonblock.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c + +../../lib/getpart-nonblock.obj: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-nonblock.obj -MD -MP -MF ../../lib/$(DEPDIR)/getpart-nonblock.Tpo -c -o ../../lib/getpart-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-nonblock.Tpo ../../lib/$(DEPDIR)/getpart-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/getpart-nonblock.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` + +../../lib/getpart-strtoofft.o: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-strtoofft.o -MD -MP -MF ../../lib/$(DEPDIR)/getpart-strtoofft.Tpo -c -o ../../lib/getpart-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-strtoofft.Tpo ../../lib/$(DEPDIR)/getpart-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/getpart-strtoofft.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c + +../../lib/getpart-strtoofft.obj: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-strtoofft.obj -MD -MP -MF ../../lib/$(DEPDIR)/getpart-strtoofft.Tpo -c -o ../../lib/getpart-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-strtoofft.Tpo ../../lib/$(DEPDIR)/getpart-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/getpart-strtoofft.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` + +../../lib/getpart-timeval.o: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-timeval.o -MD -MP -MF ../../lib/$(DEPDIR)/getpart-timeval.Tpo -c -o ../../lib/getpart-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-timeval.Tpo ../../lib/$(DEPDIR)/getpart-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/getpart-timeval.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c + +../../lib/getpart-timeval.obj: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-timeval.obj -MD -MP -MF ../../lib/$(DEPDIR)/getpart-timeval.Tpo -c -o ../../lib/getpart-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-timeval.Tpo ../../lib/$(DEPDIR)/getpart-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/getpart-timeval.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` + +../../lib/getpart-warnless.o: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-warnless.o -MD -MP -MF ../../lib/$(DEPDIR)/getpart-warnless.Tpo -c -o ../../lib/getpart-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-warnless.Tpo ../../lib/$(DEPDIR)/getpart-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/getpart-warnless.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c + +../../lib/getpart-warnless.obj: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-warnless.obj -MD -MP -MF ../../lib/$(DEPDIR)/getpart-warnless.Tpo -c -o ../../lib/getpart-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-warnless.Tpo ../../lib/$(DEPDIR)/getpart-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/getpart-warnless.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` + +getpart-getpart.o: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT getpart-getpart.o -MD -MP -MF $(DEPDIR)/getpart-getpart.Tpo -c -o getpart-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getpart-getpart.Tpo $(DEPDIR)/getpart-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='getpart-getpart.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o getpart-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c + +getpart-getpart.obj: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT getpart-getpart.obj -MD -MP -MF $(DEPDIR)/getpart-getpart.Tpo -c -o getpart-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getpart-getpart.Tpo $(DEPDIR)/getpart-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='getpart-getpart.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o getpart-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` + +../../lib/getpart-base64.o: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-base64.o -MD -MP -MF ../../lib/$(DEPDIR)/getpart-base64.Tpo -c -o ../../lib/getpart-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-base64.Tpo ../../lib/$(DEPDIR)/getpart-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/getpart-base64.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c + +../../lib/getpart-base64.obj: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-base64.obj -MD -MP -MF ../../lib/$(DEPDIR)/getpart-base64.Tpo -c -o ../../lib/getpart-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-base64.Tpo ../../lib/$(DEPDIR)/getpart-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/getpart-base64.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` + +../../lib/getpart-memdebug.o: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-memdebug.o -MD -MP -MF ../../lib/$(DEPDIR)/getpart-memdebug.Tpo -c -o ../../lib/getpart-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-memdebug.Tpo ../../lib/$(DEPDIR)/getpart-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/getpart-memdebug.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c + +../../lib/getpart-memdebug.obj: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT ../../lib/getpart-memdebug.obj -MD -MP -MF ../../lib/$(DEPDIR)/getpart-memdebug.Tpo -c -o ../../lib/getpart-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/getpart-memdebug.Tpo ../../lib/$(DEPDIR)/getpart-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/getpart-memdebug.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o ../../lib/getpart-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` + +getpart-testpart.o: testpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT getpart-testpart.o -MD -MP -MF $(DEPDIR)/getpart-testpart.Tpo -c -o getpart-testpart.o `test -f 'testpart.c' || echo '$(srcdir)/'`testpart.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getpart-testpart.Tpo $(DEPDIR)/getpart-testpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='testpart.c' object='getpart-testpart.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o getpart-testpart.o `test -f 'testpart.c' || echo '$(srcdir)/'`testpart.c + +getpart-testpart.obj: testpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -MT getpart-testpart.obj -MD -MP -MF $(DEPDIR)/getpart-testpart.Tpo -c -o getpart-testpart.obj `if test -f 'testpart.c'; then $(CYGPATH_W) 'testpart.c'; else $(CYGPATH_W) '$(srcdir)/testpart.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getpart-testpart.Tpo $(DEPDIR)/getpart-testpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='testpart.c' object='getpart-testpart.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getpart_CFLAGS) $(CFLAGS) -c -o getpart-testpart.obj `if test -f 'testpart.c'; then $(CYGPATH_W) 'testpart.c'; else $(CYGPATH_W) '$(srcdir)/testpart.c'; fi` + +../../lib/resolve-mprintf.o: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-mprintf.o -MD -MP -MF ../../lib/$(DEPDIR)/resolve-mprintf.Tpo -c -o ../../lib/resolve-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-mprintf.Tpo ../../lib/$(DEPDIR)/resolve-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/resolve-mprintf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c + +../../lib/resolve-mprintf.obj: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-mprintf.obj -MD -MP -MF ../../lib/$(DEPDIR)/resolve-mprintf.Tpo -c -o ../../lib/resolve-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-mprintf.Tpo ../../lib/$(DEPDIR)/resolve-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/resolve-mprintf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` + +../../lib/resolve-nonblock.o: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-nonblock.o -MD -MP -MF ../../lib/$(DEPDIR)/resolve-nonblock.Tpo -c -o ../../lib/resolve-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-nonblock.Tpo ../../lib/$(DEPDIR)/resolve-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/resolve-nonblock.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c + +../../lib/resolve-nonblock.obj: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-nonblock.obj -MD -MP -MF ../../lib/$(DEPDIR)/resolve-nonblock.Tpo -c -o ../../lib/resolve-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-nonblock.Tpo ../../lib/$(DEPDIR)/resolve-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/resolve-nonblock.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` + +../../lib/resolve-strtoofft.o: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-strtoofft.o -MD -MP -MF ../../lib/$(DEPDIR)/resolve-strtoofft.Tpo -c -o ../../lib/resolve-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-strtoofft.Tpo ../../lib/$(DEPDIR)/resolve-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/resolve-strtoofft.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c + +../../lib/resolve-strtoofft.obj: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-strtoofft.obj -MD -MP -MF ../../lib/$(DEPDIR)/resolve-strtoofft.Tpo -c -o ../../lib/resolve-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-strtoofft.Tpo ../../lib/$(DEPDIR)/resolve-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/resolve-strtoofft.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` + +../../lib/resolve-timeval.o: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-timeval.o -MD -MP -MF ../../lib/$(DEPDIR)/resolve-timeval.Tpo -c -o ../../lib/resolve-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-timeval.Tpo ../../lib/$(DEPDIR)/resolve-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/resolve-timeval.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c + +../../lib/resolve-timeval.obj: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-timeval.obj -MD -MP -MF ../../lib/$(DEPDIR)/resolve-timeval.Tpo -c -o ../../lib/resolve-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-timeval.Tpo ../../lib/$(DEPDIR)/resolve-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/resolve-timeval.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` + +../../lib/resolve-warnless.o: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-warnless.o -MD -MP -MF ../../lib/$(DEPDIR)/resolve-warnless.Tpo -c -o ../../lib/resolve-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-warnless.Tpo ../../lib/$(DEPDIR)/resolve-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/resolve-warnless.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c + +../../lib/resolve-warnless.obj: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-warnless.obj -MD -MP -MF ../../lib/$(DEPDIR)/resolve-warnless.Tpo -c -o ../../lib/resolve-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-warnless.Tpo ../../lib/$(DEPDIR)/resolve-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/resolve-warnless.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` + +resolve-getpart.o: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT resolve-getpart.o -MD -MP -MF $(DEPDIR)/resolve-getpart.Tpo -c -o resolve-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/resolve-getpart.Tpo $(DEPDIR)/resolve-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='resolve-getpart.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o resolve-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c + +resolve-getpart.obj: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT resolve-getpart.obj -MD -MP -MF $(DEPDIR)/resolve-getpart.Tpo -c -o resolve-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/resolve-getpart.Tpo $(DEPDIR)/resolve-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='resolve-getpart.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o resolve-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` + +../../lib/resolve-base64.o: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-base64.o -MD -MP -MF ../../lib/$(DEPDIR)/resolve-base64.Tpo -c -o ../../lib/resolve-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-base64.Tpo ../../lib/$(DEPDIR)/resolve-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/resolve-base64.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c + +../../lib/resolve-base64.obj: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-base64.obj -MD -MP -MF ../../lib/$(DEPDIR)/resolve-base64.Tpo -c -o ../../lib/resolve-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-base64.Tpo ../../lib/$(DEPDIR)/resolve-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/resolve-base64.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` + +../../lib/resolve-memdebug.o: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-memdebug.o -MD -MP -MF ../../lib/$(DEPDIR)/resolve-memdebug.Tpo -c -o ../../lib/resolve-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-memdebug.Tpo ../../lib/$(DEPDIR)/resolve-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/resolve-memdebug.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c + +../../lib/resolve-memdebug.obj: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT ../../lib/resolve-memdebug.obj -MD -MP -MF ../../lib/$(DEPDIR)/resolve-memdebug.Tpo -c -o ../../lib/resolve-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/resolve-memdebug.Tpo ../../lib/$(DEPDIR)/resolve-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/resolve-memdebug.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o ../../lib/resolve-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` + +resolve-util.o: util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT resolve-util.o -MD -MP -MF $(DEPDIR)/resolve-util.Tpo -c -o resolve-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/resolve-util.Tpo $(DEPDIR)/resolve-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='resolve-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o resolve-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c + +resolve-util.obj: util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT resolve-util.obj -MD -MP -MF $(DEPDIR)/resolve-util.Tpo -c -o resolve-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/resolve-util.Tpo $(DEPDIR)/resolve-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='resolve-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o resolve-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` + +resolve-resolve.o: resolve.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT resolve-resolve.o -MD -MP -MF $(DEPDIR)/resolve-resolve.Tpo -c -o resolve-resolve.o `test -f 'resolve.c' || echo '$(srcdir)/'`resolve.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/resolve-resolve.Tpo $(DEPDIR)/resolve-resolve.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resolve.c' object='resolve-resolve.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o resolve-resolve.o `test -f 'resolve.c' || echo '$(srcdir)/'`resolve.c + +resolve-resolve.obj: resolve.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -MT resolve-resolve.obj -MD -MP -MF $(DEPDIR)/resolve-resolve.Tpo -c -o resolve-resolve.obj `if test -f 'resolve.c'; then $(CYGPATH_W) 'resolve.c'; else $(CYGPATH_W) '$(srcdir)/resolve.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/resolve-resolve.Tpo $(DEPDIR)/resolve-resolve.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resolve.c' object='resolve-resolve.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(resolve_CFLAGS) $(CFLAGS) -c -o resolve-resolve.obj `if test -f 'resolve.c'; then $(CYGPATH_W) 'resolve.c'; else $(CYGPATH_W) '$(srcdir)/resolve.c'; fi` + +../../lib/rtspd-mprintf.o: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-mprintf.o -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-mprintf.Tpo -c -o ../../lib/rtspd-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-mprintf.Tpo ../../lib/$(DEPDIR)/rtspd-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/rtspd-mprintf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c + +../../lib/rtspd-mprintf.obj: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-mprintf.obj -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-mprintf.Tpo -c -o ../../lib/rtspd-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-mprintf.Tpo ../../lib/$(DEPDIR)/rtspd-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/rtspd-mprintf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` + +../../lib/rtspd-nonblock.o: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-nonblock.o -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-nonblock.Tpo -c -o ../../lib/rtspd-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-nonblock.Tpo ../../lib/$(DEPDIR)/rtspd-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/rtspd-nonblock.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c + +../../lib/rtspd-nonblock.obj: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-nonblock.obj -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-nonblock.Tpo -c -o ../../lib/rtspd-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-nonblock.Tpo ../../lib/$(DEPDIR)/rtspd-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/rtspd-nonblock.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` + +../../lib/rtspd-strtoofft.o: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-strtoofft.o -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-strtoofft.Tpo -c -o ../../lib/rtspd-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-strtoofft.Tpo ../../lib/$(DEPDIR)/rtspd-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/rtspd-strtoofft.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c + +../../lib/rtspd-strtoofft.obj: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-strtoofft.obj -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-strtoofft.Tpo -c -o ../../lib/rtspd-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-strtoofft.Tpo ../../lib/$(DEPDIR)/rtspd-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/rtspd-strtoofft.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` + +../../lib/rtspd-timeval.o: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-timeval.o -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-timeval.Tpo -c -o ../../lib/rtspd-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-timeval.Tpo ../../lib/$(DEPDIR)/rtspd-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/rtspd-timeval.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c + +../../lib/rtspd-timeval.obj: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-timeval.obj -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-timeval.Tpo -c -o ../../lib/rtspd-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-timeval.Tpo ../../lib/$(DEPDIR)/rtspd-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/rtspd-timeval.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` + +../../lib/rtspd-warnless.o: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-warnless.o -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-warnless.Tpo -c -o ../../lib/rtspd-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-warnless.Tpo ../../lib/$(DEPDIR)/rtspd-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/rtspd-warnless.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c + +../../lib/rtspd-warnless.obj: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-warnless.obj -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-warnless.Tpo -c -o ../../lib/rtspd-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-warnless.Tpo ../../lib/$(DEPDIR)/rtspd-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/rtspd-warnless.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` + +rtspd-getpart.o: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT rtspd-getpart.o -MD -MP -MF $(DEPDIR)/rtspd-getpart.Tpo -c -o rtspd-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rtspd-getpart.Tpo $(DEPDIR)/rtspd-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='rtspd-getpart.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o rtspd-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c + +rtspd-getpart.obj: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT rtspd-getpart.obj -MD -MP -MF $(DEPDIR)/rtspd-getpart.Tpo -c -o rtspd-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rtspd-getpart.Tpo $(DEPDIR)/rtspd-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='rtspd-getpart.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o rtspd-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` + +../../lib/rtspd-base64.o: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-base64.o -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-base64.Tpo -c -o ../../lib/rtspd-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-base64.Tpo ../../lib/$(DEPDIR)/rtspd-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/rtspd-base64.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c + +../../lib/rtspd-base64.obj: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-base64.obj -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-base64.Tpo -c -o ../../lib/rtspd-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-base64.Tpo ../../lib/$(DEPDIR)/rtspd-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/rtspd-base64.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` + +../../lib/rtspd-memdebug.o: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-memdebug.o -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-memdebug.Tpo -c -o ../../lib/rtspd-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-memdebug.Tpo ../../lib/$(DEPDIR)/rtspd-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/rtspd-memdebug.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c + +../../lib/rtspd-memdebug.obj: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT ../../lib/rtspd-memdebug.obj -MD -MP -MF ../../lib/$(DEPDIR)/rtspd-memdebug.Tpo -c -o ../../lib/rtspd-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/rtspd-memdebug.Tpo ../../lib/$(DEPDIR)/rtspd-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/rtspd-memdebug.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o ../../lib/rtspd-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` + +rtspd-util.o: util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT rtspd-util.o -MD -MP -MF $(DEPDIR)/rtspd-util.Tpo -c -o rtspd-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rtspd-util.Tpo $(DEPDIR)/rtspd-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='rtspd-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o rtspd-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c + +rtspd-util.obj: util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT rtspd-util.obj -MD -MP -MF $(DEPDIR)/rtspd-util.Tpo -c -o rtspd-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rtspd-util.Tpo $(DEPDIR)/rtspd-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='rtspd-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o rtspd-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` + +rtspd-rtspd.o: rtspd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT rtspd-rtspd.o -MD -MP -MF $(DEPDIR)/rtspd-rtspd.Tpo -c -o rtspd-rtspd.o `test -f 'rtspd.c' || echo '$(srcdir)/'`rtspd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rtspd-rtspd.Tpo $(DEPDIR)/rtspd-rtspd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rtspd.c' object='rtspd-rtspd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o rtspd-rtspd.o `test -f 'rtspd.c' || echo '$(srcdir)/'`rtspd.c + +rtspd-rtspd.obj: rtspd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -MT rtspd-rtspd.obj -MD -MP -MF $(DEPDIR)/rtspd-rtspd.Tpo -c -o rtspd-rtspd.obj `if test -f 'rtspd.c'; then $(CYGPATH_W) 'rtspd.c'; else $(CYGPATH_W) '$(srcdir)/rtspd.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rtspd-rtspd.Tpo $(DEPDIR)/rtspd-rtspd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rtspd.c' object='rtspd-rtspd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rtspd_CFLAGS) $(CFLAGS) -c -o rtspd-rtspd.obj `if test -f 'rtspd.c'; then $(CYGPATH_W) 'rtspd.c'; else $(CYGPATH_W) '$(srcdir)/rtspd.c'; fi` + +../../lib/sockfilt-mprintf.o: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-mprintf.o -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-mprintf.Tpo -c -o ../../lib/sockfilt-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-mprintf.Tpo ../../lib/$(DEPDIR)/sockfilt-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/sockfilt-mprintf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c + +../../lib/sockfilt-mprintf.obj: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-mprintf.obj -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-mprintf.Tpo -c -o ../../lib/sockfilt-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-mprintf.Tpo ../../lib/$(DEPDIR)/sockfilt-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/sockfilt-mprintf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` + +../../lib/sockfilt-nonblock.o: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-nonblock.o -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-nonblock.Tpo -c -o ../../lib/sockfilt-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-nonblock.Tpo ../../lib/$(DEPDIR)/sockfilt-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/sockfilt-nonblock.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c + +../../lib/sockfilt-nonblock.obj: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-nonblock.obj -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-nonblock.Tpo -c -o ../../lib/sockfilt-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-nonblock.Tpo ../../lib/$(DEPDIR)/sockfilt-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/sockfilt-nonblock.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` + +../../lib/sockfilt-strtoofft.o: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-strtoofft.o -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-strtoofft.Tpo -c -o ../../lib/sockfilt-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-strtoofft.Tpo ../../lib/$(DEPDIR)/sockfilt-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/sockfilt-strtoofft.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c + +../../lib/sockfilt-strtoofft.obj: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-strtoofft.obj -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-strtoofft.Tpo -c -o ../../lib/sockfilt-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-strtoofft.Tpo ../../lib/$(DEPDIR)/sockfilt-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/sockfilt-strtoofft.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` + +../../lib/sockfilt-timeval.o: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-timeval.o -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-timeval.Tpo -c -o ../../lib/sockfilt-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-timeval.Tpo ../../lib/$(DEPDIR)/sockfilt-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/sockfilt-timeval.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c + +../../lib/sockfilt-timeval.obj: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-timeval.obj -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-timeval.Tpo -c -o ../../lib/sockfilt-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-timeval.Tpo ../../lib/$(DEPDIR)/sockfilt-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/sockfilt-timeval.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` + +../../lib/sockfilt-warnless.o: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-warnless.o -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-warnless.Tpo -c -o ../../lib/sockfilt-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-warnless.Tpo ../../lib/$(DEPDIR)/sockfilt-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/sockfilt-warnless.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c + +../../lib/sockfilt-warnless.obj: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-warnless.obj -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-warnless.Tpo -c -o ../../lib/sockfilt-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-warnless.Tpo ../../lib/$(DEPDIR)/sockfilt-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/sockfilt-warnless.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` + +sockfilt-getpart.o: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT sockfilt-getpart.o -MD -MP -MF $(DEPDIR)/sockfilt-getpart.Tpo -c -o sockfilt-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sockfilt-getpart.Tpo $(DEPDIR)/sockfilt-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='sockfilt-getpart.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o sockfilt-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c + +sockfilt-getpart.obj: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT sockfilt-getpart.obj -MD -MP -MF $(DEPDIR)/sockfilt-getpart.Tpo -c -o sockfilt-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sockfilt-getpart.Tpo $(DEPDIR)/sockfilt-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='sockfilt-getpart.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o sockfilt-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` + +../../lib/sockfilt-base64.o: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-base64.o -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-base64.Tpo -c -o ../../lib/sockfilt-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-base64.Tpo ../../lib/$(DEPDIR)/sockfilt-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/sockfilt-base64.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c + +../../lib/sockfilt-base64.obj: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-base64.obj -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-base64.Tpo -c -o ../../lib/sockfilt-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-base64.Tpo ../../lib/$(DEPDIR)/sockfilt-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/sockfilt-base64.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` + +../../lib/sockfilt-memdebug.o: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-memdebug.o -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-memdebug.Tpo -c -o ../../lib/sockfilt-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-memdebug.Tpo ../../lib/$(DEPDIR)/sockfilt-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/sockfilt-memdebug.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c + +../../lib/sockfilt-memdebug.obj: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-memdebug.obj -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-memdebug.Tpo -c -o ../../lib/sockfilt-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-memdebug.Tpo ../../lib/$(DEPDIR)/sockfilt-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/sockfilt-memdebug.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` + +sockfilt-util.o: util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT sockfilt-util.o -MD -MP -MF $(DEPDIR)/sockfilt-util.Tpo -c -o sockfilt-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sockfilt-util.Tpo $(DEPDIR)/sockfilt-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='sockfilt-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o sockfilt-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c + +sockfilt-util.obj: util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT sockfilt-util.obj -MD -MP -MF $(DEPDIR)/sockfilt-util.Tpo -c -o sockfilt-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sockfilt-util.Tpo $(DEPDIR)/sockfilt-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='sockfilt-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o sockfilt-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` + +sockfilt-sockfilt.o: sockfilt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT sockfilt-sockfilt.o -MD -MP -MF $(DEPDIR)/sockfilt-sockfilt.Tpo -c -o sockfilt-sockfilt.o `test -f 'sockfilt.c' || echo '$(srcdir)/'`sockfilt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sockfilt-sockfilt.Tpo $(DEPDIR)/sockfilt-sockfilt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sockfilt.c' object='sockfilt-sockfilt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o sockfilt-sockfilt.o `test -f 'sockfilt.c' || echo '$(srcdir)/'`sockfilt.c + +sockfilt-sockfilt.obj: sockfilt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT sockfilt-sockfilt.obj -MD -MP -MF $(DEPDIR)/sockfilt-sockfilt.Tpo -c -o sockfilt-sockfilt.obj `if test -f 'sockfilt.c'; then $(CYGPATH_W) 'sockfilt.c'; else $(CYGPATH_W) '$(srcdir)/sockfilt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sockfilt-sockfilt.Tpo $(DEPDIR)/sockfilt-sockfilt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sockfilt.c' object='sockfilt-sockfilt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o sockfilt-sockfilt.obj `if test -f 'sockfilt.c'; then $(CYGPATH_W) 'sockfilt.c'; else $(CYGPATH_W) '$(srcdir)/sockfilt.c'; fi` + +../../lib/sockfilt-inet_pton.o: ../../lib/inet_pton.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-inet_pton.o -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-inet_pton.Tpo -c -o ../../lib/sockfilt-inet_pton.o `test -f '../../lib/inet_pton.c' || echo '$(srcdir)/'`../../lib/inet_pton.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-inet_pton.Tpo ../../lib/$(DEPDIR)/sockfilt-inet_pton.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/inet_pton.c' object='../../lib/sockfilt-inet_pton.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-inet_pton.o `test -f '../../lib/inet_pton.c' || echo '$(srcdir)/'`../../lib/inet_pton.c + +../../lib/sockfilt-inet_pton.obj: ../../lib/inet_pton.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -MT ../../lib/sockfilt-inet_pton.obj -MD -MP -MF ../../lib/$(DEPDIR)/sockfilt-inet_pton.Tpo -c -o ../../lib/sockfilt-inet_pton.obj `if test -f '../../lib/inet_pton.c'; then $(CYGPATH_W) '../../lib/inet_pton.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/inet_pton.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sockfilt-inet_pton.Tpo ../../lib/$(DEPDIR)/sockfilt-inet_pton.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/inet_pton.c' object='../../lib/sockfilt-inet_pton.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sockfilt_CFLAGS) $(CFLAGS) -c -o ../../lib/sockfilt-inet_pton.obj `if test -f '../../lib/inet_pton.c'; then $(CYGPATH_W) '../../lib/inet_pton.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/inet_pton.c'; fi` + +../../lib/sws-mprintf.o: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-mprintf.o -MD -MP -MF ../../lib/$(DEPDIR)/sws-mprintf.Tpo -c -o ../../lib/sws-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-mprintf.Tpo ../../lib/$(DEPDIR)/sws-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/sws-mprintf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c + +../../lib/sws-mprintf.obj: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-mprintf.obj -MD -MP -MF ../../lib/$(DEPDIR)/sws-mprintf.Tpo -c -o ../../lib/sws-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-mprintf.Tpo ../../lib/$(DEPDIR)/sws-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/sws-mprintf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` + +../../lib/sws-nonblock.o: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-nonblock.o -MD -MP -MF ../../lib/$(DEPDIR)/sws-nonblock.Tpo -c -o ../../lib/sws-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-nonblock.Tpo ../../lib/$(DEPDIR)/sws-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/sws-nonblock.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c + +../../lib/sws-nonblock.obj: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-nonblock.obj -MD -MP -MF ../../lib/$(DEPDIR)/sws-nonblock.Tpo -c -o ../../lib/sws-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-nonblock.Tpo ../../lib/$(DEPDIR)/sws-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/sws-nonblock.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` + +../../lib/sws-strtoofft.o: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-strtoofft.o -MD -MP -MF ../../lib/$(DEPDIR)/sws-strtoofft.Tpo -c -o ../../lib/sws-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-strtoofft.Tpo ../../lib/$(DEPDIR)/sws-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/sws-strtoofft.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c + +../../lib/sws-strtoofft.obj: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-strtoofft.obj -MD -MP -MF ../../lib/$(DEPDIR)/sws-strtoofft.Tpo -c -o ../../lib/sws-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-strtoofft.Tpo ../../lib/$(DEPDIR)/sws-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/sws-strtoofft.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` + +../../lib/sws-timeval.o: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-timeval.o -MD -MP -MF ../../lib/$(DEPDIR)/sws-timeval.Tpo -c -o ../../lib/sws-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-timeval.Tpo ../../lib/$(DEPDIR)/sws-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/sws-timeval.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c + +../../lib/sws-timeval.obj: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-timeval.obj -MD -MP -MF ../../lib/$(DEPDIR)/sws-timeval.Tpo -c -o ../../lib/sws-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-timeval.Tpo ../../lib/$(DEPDIR)/sws-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/sws-timeval.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` + +../../lib/sws-warnless.o: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-warnless.o -MD -MP -MF ../../lib/$(DEPDIR)/sws-warnless.Tpo -c -o ../../lib/sws-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-warnless.Tpo ../../lib/$(DEPDIR)/sws-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/sws-warnless.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c + +../../lib/sws-warnless.obj: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-warnless.obj -MD -MP -MF ../../lib/$(DEPDIR)/sws-warnless.Tpo -c -o ../../lib/sws-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-warnless.Tpo ../../lib/$(DEPDIR)/sws-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/sws-warnless.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` + +sws-getpart.o: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT sws-getpart.o -MD -MP -MF $(DEPDIR)/sws-getpart.Tpo -c -o sws-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sws-getpart.Tpo $(DEPDIR)/sws-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='sws-getpart.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o sws-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c + +sws-getpart.obj: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT sws-getpart.obj -MD -MP -MF $(DEPDIR)/sws-getpart.Tpo -c -o sws-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sws-getpart.Tpo $(DEPDIR)/sws-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='sws-getpart.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o sws-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` + +../../lib/sws-base64.o: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-base64.o -MD -MP -MF ../../lib/$(DEPDIR)/sws-base64.Tpo -c -o ../../lib/sws-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-base64.Tpo ../../lib/$(DEPDIR)/sws-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/sws-base64.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c + +../../lib/sws-base64.obj: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-base64.obj -MD -MP -MF ../../lib/$(DEPDIR)/sws-base64.Tpo -c -o ../../lib/sws-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-base64.Tpo ../../lib/$(DEPDIR)/sws-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/sws-base64.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` + +../../lib/sws-memdebug.o: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-memdebug.o -MD -MP -MF ../../lib/$(DEPDIR)/sws-memdebug.Tpo -c -o ../../lib/sws-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-memdebug.Tpo ../../lib/$(DEPDIR)/sws-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/sws-memdebug.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c + +../../lib/sws-memdebug.obj: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-memdebug.obj -MD -MP -MF ../../lib/$(DEPDIR)/sws-memdebug.Tpo -c -o ../../lib/sws-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-memdebug.Tpo ../../lib/$(DEPDIR)/sws-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/sws-memdebug.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` + +sws-util.o: util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT sws-util.o -MD -MP -MF $(DEPDIR)/sws-util.Tpo -c -o sws-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sws-util.Tpo $(DEPDIR)/sws-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='sws-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o sws-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c + +sws-util.obj: util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT sws-util.obj -MD -MP -MF $(DEPDIR)/sws-util.Tpo -c -o sws-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sws-util.Tpo $(DEPDIR)/sws-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='sws-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o sws-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` + +sws-sws.o: sws.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT sws-sws.o -MD -MP -MF $(DEPDIR)/sws-sws.Tpo -c -o sws-sws.o `test -f 'sws.c' || echo '$(srcdir)/'`sws.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sws-sws.Tpo $(DEPDIR)/sws-sws.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sws.c' object='sws-sws.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o sws-sws.o `test -f 'sws.c' || echo '$(srcdir)/'`sws.c + +sws-sws.obj: sws.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT sws-sws.obj -MD -MP -MF $(DEPDIR)/sws-sws.Tpo -c -o sws-sws.obj `if test -f 'sws.c'; then $(CYGPATH_W) 'sws.c'; else $(CYGPATH_W) '$(srcdir)/sws.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sws-sws.Tpo $(DEPDIR)/sws-sws.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sws.c' object='sws-sws.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o sws-sws.obj `if test -f 'sws.c'; then $(CYGPATH_W) 'sws.c'; else $(CYGPATH_W) '$(srcdir)/sws.c'; fi` + +../../lib/sws-inet_pton.o: ../../lib/inet_pton.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-inet_pton.o -MD -MP -MF ../../lib/$(DEPDIR)/sws-inet_pton.Tpo -c -o ../../lib/sws-inet_pton.o `test -f '../../lib/inet_pton.c' || echo '$(srcdir)/'`../../lib/inet_pton.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-inet_pton.Tpo ../../lib/$(DEPDIR)/sws-inet_pton.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/inet_pton.c' object='../../lib/sws-inet_pton.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-inet_pton.o `test -f '../../lib/inet_pton.c' || echo '$(srcdir)/'`../../lib/inet_pton.c + +../../lib/sws-inet_pton.obj: ../../lib/inet_pton.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -MT ../../lib/sws-inet_pton.obj -MD -MP -MF ../../lib/$(DEPDIR)/sws-inet_pton.Tpo -c -o ../../lib/sws-inet_pton.obj `if test -f '../../lib/inet_pton.c'; then $(CYGPATH_W) '../../lib/inet_pton.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/inet_pton.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/sws-inet_pton.Tpo ../../lib/$(DEPDIR)/sws-inet_pton.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/inet_pton.c' object='../../lib/sws-inet_pton.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sws_CFLAGS) $(CFLAGS) -c -o ../../lib/sws-inet_pton.obj `if test -f '../../lib/inet_pton.c'; then $(CYGPATH_W) '../../lib/inet_pton.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/inet_pton.c'; fi` + +../../lib/tftpd-mprintf.o: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-mprintf.o -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-mprintf.Tpo -c -o ../../lib/tftpd-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-mprintf.Tpo ../../lib/$(DEPDIR)/tftpd-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/tftpd-mprintf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-mprintf.o `test -f '../../lib/mprintf.c' || echo '$(srcdir)/'`../../lib/mprintf.c + +../../lib/tftpd-mprintf.obj: ../../lib/mprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-mprintf.obj -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-mprintf.Tpo -c -o ../../lib/tftpd-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-mprintf.Tpo ../../lib/$(DEPDIR)/tftpd-mprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/mprintf.c' object='../../lib/tftpd-mprintf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-mprintf.obj `if test -f '../../lib/mprintf.c'; then $(CYGPATH_W) '../../lib/mprintf.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/mprintf.c'; fi` + +../../lib/tftpd-nonblock.o: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-nonblock.o -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-nonblock.Tpo -c -o ../../lib/tftpd-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-nonblock.Tpo ../../lib/$(DEPDIR)/tftpd-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/tftpd-nonblock.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-nonblock.o `test -f '../../lib/nonblock.c' || echo '$(srcdir)/'`../../lib/nonblock.c + +../../lib/tftpd-nonblock.obj: ../../lib/nonblock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-nonblock.obj -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-nonblock.Tpo -c -o ../../lib/tftpd-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-nonblock.Tpo ../../lib/$(DEPDIR)/tftpd-nonblock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/nonblock.c' object='../../lib/tftpd-nonblock.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-nonblock.obj `if test -f '../../lib/nonblock.c'; then $(CYGPATH_W) '../../lib/nonblock.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/nonblock.c'; fi` + +../../lib/tftpd-strtoofft.o: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-strtoofft.o -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-strtoofft.Tpo -c -o ../../lib/tftpd-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-strtoofft.Tpo ../../lib/$(DEPDIR)/tftpd-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/tftpd-strtoofft.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-strtoofft.o `test -f '../../lib/strtoofft.c' || echo '$(srcdir)/'`../../lib/strtoofft.c + +../../lib/tftpd-strtoofft.obj: ../../lib/strtoofft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-strtoofft.obj -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-strtoofft.Tpo -c -o ../../lib/tftpd-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-strtoofft.Tpo ../../lib/$(DEPDIR)/tftpd-strtoofft.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/strtoofft.c' object='../../lib/tftpd-strtoofft.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-strtoofft.obj `if test -f '../../lib/strtoofft.c'; then $(CYGPATH_W) '../../lib/strtoofft.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/strtoofft.c'; fi` + +../../lib/tftpd-timeval.o: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-timeval.o -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-timeval.Tpo -c -o ../../lib/tftpd-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-timeval.Tpo ../../lib/$(DEPDIR)/tftpd-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/tftpd-timeval.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-timeval.o `test -f '../../lib/timeval.c' || echo '$(srcdir)/'`../../lib/timeval.c + +../../lib/tftpd-timeval.obj: ../../lib/timeval.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-timeval.obj -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-timeval.Tpo -c -o ../../lib/tftpd-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-timeval.Tpo ../../lib/$(DEPDIR)/tftpd-timeval.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/timeval.c' object='../../lib/tftpd-timeval.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-timeval.obj `if test -f '../../lib/timeval.c'; then $(CYGPATH_W) '../../lib/timeval.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/timeval.c'; fi` + +../../lib/tftpd-warnless.o: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-warnless.o -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-warnless.Tpo -c -o ../../lib/tftpd-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-warnless.Tpo ../../lib/$(DEPDIR)/tftpd-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/tftpd-warnless.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-warnless.o `test -f '../../lib/warnless.c' || echo '$(srcdir)/'`../../lib/warnless.c + +../../lib/tftpd-warnless.obj: ../../lib/warnless.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-warnless.obj -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-warnless.Tpo -c -o ../../lib/tftpd-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-warnless.Tpo ../../lib/$(DEPDIR)/tftpd-warnless.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/warnless.c' object='../../lib/tftpd-warnless.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-warnless.obj `if test -f '../../lib/warnless.c'; then $(CYGPATH_W) '../../lib/warnless.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/warnless.c'; fi` + +tftpd-getpart.o: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT tftpd-getpart.o -MD -MP -MF $(DEPDIR)/tftpd-getpart.Tpo -c -o tftpd-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tftpd-getpart.Tpo $(DEPDIR)/tftpd-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='tftpd-getpart.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o tftpd-getpart.o `test -f 'getpart.c' || echo '$(srcdir)/'`getpart.c + +tftpd-getpart.obj: getpart.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT tftpd-getpart.obj -MD -MP -MF $(DEPDIR)/tftpd-getpart.Tpo -c -o tftpd-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tftpd-getpart.Tpo $(DEPDIR)/tftpd-getpart.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getpart.c' object='tftpd-getpart.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o tftpd-getpart.obj `if test -f 'getpart.c'; then $(CYGPATH_W) 'getpart.c'; else $(CYGPATH_W) '$(srcdir)/getpart.c'; fi` + +../../lib/tftpd-base64.o: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-base64.o -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-base64.Tpo -c -o ../../lib/tftpd-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-base64.Tpo ../../lib/$(DEPDIR)/tftpd-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/tftpd-base64.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-base64.o `test -f '../../lib/base64.c' || echo '$(srcdir)/'`../../lib/base64.c + +../../lib/tftpd-base64.obj: ../../lib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-base64.obj -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-base64.Tpo -c -o ../../lib/tftpd-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-base64.Tpo ../../lib/$(DEPDIR)/tftpd-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/base64.c' object='../../lib/tftpd-base64.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-base64.obj `if test -f '../../lib/base64.c'; then $(CYGPATH_W) '../../lib/base64.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/base64.c'; fi` + +../../lib/tftpd-memdebug.o: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-memdebug.o -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-memdebug.Tpo -c -o ../../lib/tftpd-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-memdebug.Tpo ../../lib/$(DEPDIR)/tftpd-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/tftpd-memdebug.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-memdebug.o `test -f '../../lib/memdebug.c' || echo '$(srcdir)/'`../../lib/memdebug.c + +../../lib/tftpd-memdebug.obj: ../../lib/memdebug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT ../../lib/tftpd-memdebug.obj -MD -MP -MF ../../lib/$(DEPDIR)/tftpd-memdebug.Tpo -c -o ../../lib/tftpd-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../lib/$(DEPDIR)/tftpd-memdebug.Tpo ../../lib/$(DEPDIR)/tftpd-memdebug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../lib/memdebug.c' object='../../lib/tftpd-memdebug.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o ../../lib/tftpd-memdebug.obj `if test -f '../../lib/memdebug.c'; then $(CYGPATH_W) '../../lib/memdebug.c'; else $(CYGPATH_W) '$(srcdir)/../../lib/memdebug.c'; fi` + +tftpd-util.o: util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT tftpd-util.o -MD -MP -MF $(DEPDIR)/tftpd-util.Tpo -c -o tftpd-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tftpd-util.Tpo $(DEPDIR)/tftpd-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='tftpd-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o tftpd-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c + +tftpd-util.obj: util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT tftpd-util.obj -MD -MP -MF $(DEPDIR)/tftpd-util.Tpo -c -o tftpd-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tftpd-util.Tpo $(DEPDIR)/tftpd-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='tftpd-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o tftpd-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` + +tftpd-tftpd.o: tftpd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT tftpd-tftpd.o -MD -MP -MF $(DEPDIR)/tftpd-tftpd.Tpo -c -o tftpd-tftpd.o `test -f 'tftpd.c' || echo '$(srcdir)/'`tftpd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tftpd-tftpd.Tpo $(DEPDIR)/tftpd-tftpd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tftpd.c' object='tftpd-tftpd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o tftpd-tftpd.o `test -f 'tftpd.c' || echo '$(srcdir)/'`tftpd.c + +tftpd-tftpd.obj: tftpd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -MT tftpd-tftpd.obj -MD -MP -MF $(DEPDIR)/tftpd-tftpd.Tpo -c -o tftpd-tftpd.obj `if test -f 'tftpd.c'; then $(CYGPATH_W) 'tftpd.c'; else $(CYGPATH_W) '$(srcdir)/tftpd.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tftpd-tftpd.Tpo $(DEPDIR)/tftpd-tftpd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tftpd.c' object='tftpd-tftpd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tftpd_CFLAGS) $(CFLAGS) -c -o tftpd-tftpd.obj `if test -f 'tftpd.c'; then $(CYGPATH_W) 'tftpd.c'; else $(CYGPATH_W) '$(srcdir)/tftpd.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f ../../lib/$(DEPDIR)/$(am__dirstamp) + -rm -f ../../lib/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ../../lib/$(DEPDIR) ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ../../lib/$(DEPDIR) ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +checksrc: + @PERL@ $(top_srcdir)/lib/checksrc.pl $(srcdir)/*.c + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/deps/curl-7.51.0/tests/server/Makefile.inc b/deps/curl-7.51.0/tests/server/Makefile.inc new file mode 100644 index 0000000000..c3ea664b69 --- /dev/null +++ b/deps/curl-7.51.0/tests/server/Makefile.inc @@ -0,0 +1,70 @@ +noinst_PROGRAMS = getpart resolve rtspd sockfilt sws tftpd fake_ntlm + +CURLX_SRCS = \ + ../../lib/mprintf.c \ + ../../lib/nonblock.c \ + ../../lib/strtoofft.c \ + ../../lib/timeval.c \ + ../../lib/warnless.c + +CURLX_HDRS = \ + ../../lib/curlx.h \ + ../../lib/nonblock.h \ + ../../lib/strtoofft.h \ + ../../lib/timeval.h \ + ../../lib/warnless.h + +USEFUL = \ + getpart.c \ + getpart.h \ + server_setup.h \ + ../../lib/base64.c \ + ../../lib/curl_base64.h \ + ../../lib/memdebug.c \ + ../../lib/memdebug.h + +UTIL = \ + util.c \ + util.h + +getpart_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) \ + testpart.c +getpart_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +getpart_CFLAGS = $(AM_CFLAGS) + +resolve_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \ + resolve.c +resolve_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +resolve_CFLAGS = $(AM_CFLAGS) + +rtspd_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \ + server_sockaddr.h \ + rtspd.c +rtspd_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +rtspd_CFLAGS = $(AM_CFLAGS) + +sockfilt_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \ + server_sockaddr.h \ + sockfilt.c \ + ../../lib/inet_pton.c +sockfilt_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +sockfilt_CFLAGS = $(AM_CFLAGS) + +sws_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \ + server_sockaddr.h \ + sws.c \ + ../../lib/inet_pton.c +sws_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +sws_CFLAGS = $(AM_CFLAGS) + +tftpd_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \ + server_sockaddr.h \ + tftpd.c \ + tftp.h +tftpd_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +tftpd_CFLAGS = $(AM_CFLAGS) + +fake_ntlm_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \ + fake_ntlm.c +fake_ntlm_LDADD = @CURL_NETWORK_AND_TIME_LIBS@ +fake_ntlm_CFLAGS = $(AM_CFLAGS) diff --git a/deps/curl-7.51.0/tests/server/base64.pl b/deps/curl-7.51.0/tests/server/base64.pl new file mode 100644 index 0000000000..2eab1fa4df --- /dev/null +++ b/deps/curl-7.51.0/tests/server/base64.pl @@ -0,0 +1,9 @@ +#!/usr/bin/perl + +use MIME::Base64 qw(encode_base64); + +my $buf; +while(read(STDIN, $buf, 60*57)) { + my $enc = encode_base64($buf); + print "$enc"; +} diff --git a/deps/curl-7.51.0/tests/server/fake_ntlm.c b/deps/curl-7.51.0/tests/server/fake_ntlm.c new file mode 100644 index 0000000000..87118b3151 --- /dev/null +++ b/deps/curl-7.51.0/tests/server/fake_ntlm.c @@ -0,0 +1,280 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2010, Mandy Wu, + * Copyright (C) 2011 - 2013, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "server_setup.h" + +/* + * This is a fake ntlm_auth, which is used for testing NTLM single-sign-on. + * When DEBUGBUILD is defined, libcurl invoke this tool instead of real winbind + * daemon helper /usr/bin/ntlm_auth. This tool will accept commands and + * responses with a pre-written string saved in test case test2005. + */ + +#define ENABLE_CURLX_PRINTF +#include "curlx.h" /* from the private lib dir */ +#include "getpart.h" +#include "util.h" + +/* include memdebug.h last */ +#include "memdebug.h" + +#ifndef DEFAULT_LOGFILE +#define DEFAULT_LOGFILE "log/fake_ntlm.log" +#endif + +const char *serverlogfile = DEFAULT_LOGFILE; + +/* + * Returns an allocated buffer with printable representation of input + * buffer contents or returns NULL on out of memory condition. + */ +static char *printable(char *inbuf, size_t inlength) +{ + char *outbuf; + char *newbuf; + size_t newsize; + size_t outsize; + size_t outincr = 0; + size_t i, o = 0; + +#define HEX_FMT_STR "[0x%02X]" +#define HEX_STR_LEN 6 +#define NOTHING_STR "[NOTHING]" +#define NOTHING_LEN 9 + + if(!inlength) + inlength = strlen(inbuf); + + if(inlength) { + outincr = ((inlength/2) < (HEX_STR_LEN+1)) ? HEX_STR_LEN+1 : inlength/2; + outsize = inlength + outincr; + } + else + outsize = NOTHING_LEN + 1; + + outbuf = malloc(outsize); + if(!outbuf) + return NULL; + + if(!inlength) { + snprintf(&outbuf[0], outsize, "%s", NOTHING_STR); + return outbuf; + } + + for(i=0; i outsize - (HEX_STR_LEN + 1)) { + newsize = outsize + outincr; + newbuf = realloc(outbuf, newsize); + if(!newbuf) { + free(outbuf); + return NULL; + } + outbuf = newbuf; + outsize = newsize; + } + + if((inbuf[i] > 0x20) && (inbuf[i] < 0x7F)) { + outbuf[o] = inbuf[i]; + o++; + } + else { + snprintf(&outbuf[o], outsize - o, HEX_FMT_STR, inbuf[i]); + o += HEX_STR_LEN; + } + + } + outbuf[o] = '\0'; + + return outbuf; +} + +int main(int argc, char *argv[]) +{ + char buf[1024]; + FILE *stream; + char *filename; + int error; + char *type1_input = NULL, *type3_input = NULL; + char *type1_output = NULL, *type3_output = NULL; + size_t size = 0; + long testnum; + const char *env; + int arg = 1; + char *helper_user = (char *)"unknown"; + char *helper_proto = (char *)"unknown"; + char *helper_domain = (char *)"unknown"; + bool use_cached_creds = FALSE; + char *msgbuf; + + buf[0] = '\0'; + + while(argc > arg) { + if(!strcmp("--use-cached-creds", argv[arg])) { + use_cached_creds = TRUE; + arg++; + } + else if(!strcmp("--helper-protocol", argv[arg])) { + arg++; + if(argc > arg) + helper_proto = argv[arg++]; + } + else if(!strcmp("--username", argv[arg])) { + arg++; + if(argc > arg) + helper_user = argv[arg++]; + } + else if(!strcmp("--domain", argv[arg])) { + arg++; + if(argc > arg) + helper_domain = argv[arg++]; + } + else { + puts("Usage: fake_ntlm [option]\n" + " --use-cached-creds\n" + " --helper-protocol [protocol]\n" + " --username [username]\n" + " --domain [domain]"); + exit(1); + } + } + + logmsg("fake_ntlm (user: %s) (proto: %s) (domain: %s) (cached creds: %s)", + helper_user, helper_proto, helper_domain, + (use_cached_creds) ? "yes" : "no"); + + env = getenv("CURL_NTLM_AUTH_TESTNUM"); + if(env) { + char *endptr; + long lnum = strtol(env, &endptr, 10); + if((endptr != env + strlen(env)) || (lnum < 1L)) { + logmsg("Test number not valid in CURL_NTLM_AUTH_TESTNUM"); + exit(1); + } + testnum = lnum; + } + else { + logmsg("Test number not specified in CURL_NTLM_AUTH_TESTNUM"); + exit(1); + } + + env = getenv("CURL_NTLM_AUTH_SRCDIR"); + if(env) { + path = env; + } + + filename = test2file(testnum); + stream=fopen(filename, "rb"); + if(!stream) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg("Error opening file: %s", filename); + logmsg("Couldn't open test file %ld", testnum); + exit(1); + } + else { + /* get the ntlm_auth input/output */ + error = getpart(&type1_input, &size, "ntlm_auth_type1", "input", stream); + fclose(stream); + if(error || size == 0) { + logmsg("getpart() type 1 input failed with error: %d", error); + exit(1); + } + } + + stream=fopen(filename, "rb"); + if(!stream) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg("Error opening file: %s", filename); + logmsg("Couldn't open test file %ld", testnum); + exit(1); + } + else { + size = 0; + error = getpart(&type3_input, &size, "ntlm_auth_type3", "input", stream); + fclose(stream); + if(error || size == 0) { + logmsg("getpart() type 3 input failed with error: %d", error); + exit(1); + } + } + + while(fgets(buf, sizeof(buf), stdin)) { + if(strcmp(buf, type1_input) == 0) { + stream=fopen(filename, "rb"); + if(!stream) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg("Error opening file: %s", filename); + logmsg("Couldn't open test file %ld", testnum); + exit(1); + } + else { + size = 0; + error = getpart(&type1_output, &size, "ntlm_auth_type1", "output", + stream); + fclose(stream); + if(error || size == 0) { + logmsg("getpart() type 1 output failed with error: %d", error); + exit(1); + } + } + printf("%s", type1_output); + fflush(stdout); + } + else if(strncmp(buf, type3_input, strlen(type3_input)) == 0) { + stream=fopen(filename, "rb"); + if(!stream) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg("Error opening file: %s", filename); + logmsg("Couldn't open test file %ld", testnum); + exit(1); + } + else { + size = 0; + error = getpart(&type3_output, &size, "ntlm_auth_type3", "output", + stream); + fclose(stream); + if(error || size == 0) { + logmsg("getpart() type 3 output failed with error: %d", error); + exit(1); + } + } + printf("%s", type3_output); + fflush(stdout); + } + else { + printf("Unknown request\n"); + msgbuf = printable(buf, 0); + if(msgbuf) { + logmsg("invalid input: '%s'\n", msgbuf); + free(msgbuf); + } + else + logmsg("OOM formatting invalid input: '%s'\n", buf); + exit(1); + } + } + return 1; +} diff --git a/deps/curl-7.51.0/tests/server/getpart.c b/deps/curl-7.51.0/tests/server/getpart.c new file mode 100644 index 0000000000..1952fbbe5c --- /dev/null +++ b/deps/curl-7.51.0/tests/server/getpart.c @@ -0,0 +1,451 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "server_setup.h" + +#include "getpart.h" + +#define ENABLE_CURLX_PRINTF +/* make the curlx header define all printf() functions to use the curlx_* + versions instead */ +#include "curlx.h" /* from the private lib dir */ + +/* just to please curl_base64.h we create a fake struct */ +struct Curl_easy { + int fake; +}; + +#include "curl_base64.h" +#include "curl_memory.h" + +/* include memdebug.h last */ +#include "memdebug.h" + +#define EAT_SPACE(p) while(*(p) && ISSPACE(*(p))) (p)++ + +#define EAT_WORD(p) while(*(p) && !ISSPACE(*(p)) && ('>' != *(p))) (p)++ + +#ifdef DEBUG_GETPART +#define show(x) printf x +#else +#define show(x) Curl_nop_stmt +#endif + +#if defined(_MSC_VER) && defined(_DLL) +# pragma warning(disable:4232) /* MSVC extension, dllimport identity */ +#endif + +curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc; +curl_free_callback Curl_cfree = (curl_free_callback)free; +curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; +curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)strdup; +curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; +#if defined(WIN32) && defined(UNICODE) +curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; +#endif + +#if defined(_MSC_VER) && defined(_DLL) +# pragma warning(default:4232) /* MSVC extension, dllimport identity */ +#endif + +/* + * readline() + * + * Reads a complete line from a file into a dynamically allocated buffer. + * + * Calling function may call this multiple times with same 'buffer' + * and 'bufsize' pointers to avoid multiple buffer allocations. Buffer + * will be reallocated and 'bufsize' increased until whole line fits in + * buffer before returning it. + * + * Calling function is responsible to free allocated buffer. + * + * This function may return: + * GPE_OUT_OF_MEMORY + * GPE_END_OF_FILE + * GPE_OK + */ + +static int readline(char **buffer, size_t *bufsize, FILE *stream) +{ + size_t offset = 0; + size_t length; + char *newptr; + + if(!*buffer) { + *buffer = malloc(128); + if(!*buffer) + return GPE_OUT_OF_MEMORY; + *bufsize = 128; + } + + for(;;) { + int bytestoread = curlx_uztosi(*bufsize - offset); + + if(!fgets(*buffer + offset, bytestoread, stream)) + return (offset != 0) ? GPE_OK : GPE_END_OF_FILE; + + length = offset + strlen(*buffer + offset); + if(*(*buffer + length - 1) == '\n') + break; + offset = length; + if(length < *bufsize - 1) + continue; + + newptr = realloc(*buffer, *bufsize * 2); + if(!newptr) + return GPE_OUT_OF_MEMORY; + *buffer = newptr; + *bufsize *= 2; + } + + return GPE_OK; +} + +/* + * appenddata() + * + * This appends data from a given source buffer to the end of the used part of + * a destination buffer. Arguments relative to the destination buffer are, the + * address of a pointer to the destination buffer 'dst_buf', the length of data + * in destination buffer excluding potential null string termination 'dst_len', + * the allocated size of destination buffer 'dst_alloc'. All three destination + * buffer arguments may be modified by this function. Arguments relative to the + * source buffer are, a pointer to the source buffer 'src_buf' and indication + * whether the source buffer is base64 encoded or not 'src_b64'. + * + * If the source buffer is indicated to be base64 encoded, this appends the + * decoded data, binary or whatever, to the destination. The source buffer + * may not hold binary data, only a null terminated string is valid content. + * + * Destination buffer will be enlarged and relocated as needed. + * + * Calling function is responsible to provide preallocated destination + * buffer and also to deallocate it when no longer needed. + * + * This function may return: + * GPE_OUT_OF_MEMORY + * GPE_OK + */ + +static int appenddata(char **dst_buf, /* dest buffer */ + size_t *dst_len, /* dest buffer data length */ + size_t *dst_alloc, /* dest buffer allocated size */ + char *src_buf, /* source buffer */ + int src_b64) /* != 0 if source is base64 encoded */ +{ + size_t need_alloc = 0; + size_t src_len = strlen(src_buf); + + if(!src_len) + return GPE_OK; + + need_alloc = src_len + *dst_len + 1; + + if(src_b64) { + if(src_buf[src_len - 1] == '\r') + src_len--; + + if(src_buf[src_len - 1] == '\n') + src_len--; + } + + /* enlarge destination buffer if required */ + if(need_alloc > *dst_alloc) { + size_t newsize = need_alloc * 2; + char *newptr = realloc(*dst_buf, newsize); + if(!newptr) { + return GPE_OUT_OF_MEMORY; + } + *dst_alloc = newsize; + *dst_buf = newptr; + } + + /* memcpy to support binary blobs */ + memcpy(*dst_buf + *dst_len, src_buf, src_len); + *dst_len += src_len; + *(*dst_buf + *dst_len) = '\0'; + + return GPE_OK; +} + +static int decodedata(char **buf, /* dest buffer */ + size_t *len) /* dest buffer data length */ +{ + int error = 0; + unsigned char *buf64 = NULL; + size_t src_len = 0; + + if(!*len) + return GPE_OK; + + /* base64 decode the given buffer */ + error = (int) Curl_base64_decode(*buf, &buf64, &src_len); + if(error) + return GPE_OUT_OF_MEMORY; + + if(!src_len) { + /* + ** currently there is no way to tell apart an OOM condition in + ** Curl_base64_decode() from zero length decoded data. For now, + ** let's just assume it is an OOM condition, currently we have + ** no input for this function that decodes to zero length data. + */ + free(buf64); + + return GPE_OUT_OF_MEMORY; + } + + /* memcpy to support binary blobs */ + memcpy(*buf, buf64, src_len); + *len = src_len; + *(*buf + src_len) = '\0'; + + free(buf64); + + return GPE_OK; +} + +/* + * getpart() + * + * This returns whole contents of specified XML-like section and subsection + * from the given file. This is mostly used to retrieve a specific part from + * a test definition file for consumption by test suite servers. + * + * Data is returned in a dynamically allocated buffer, a pointer to this data + * and the size of the data is stored at the addresses that caller specifies. + * + * If the returned data is a string the returned size will be the length of + * the string excluding null termination. Otherwise it will just be the size + * of the returned binary data. + * + * Calling function is responsible to free returned buffer. + * + * This function may return: + * GPE_NO_BUFFER_SPACE + * GPE_OUT_OF_MEMORY + * GPE_OK + */ + +int getpart(char **outbuf, size_t *outlen, + const char *main, const char *sub, FILE *stream) +{ +# define MAX_TAG_LEN 79 + char couter[MAX_TAG_LEN+1]; /* current outermost section */ + char cmain[MAX_TAG_LEN+1]; /* current main section */ + char csub[MAX_TAG_LEN+1]; /* current sub section */ + char ptag[MAX_TAG_LEN+1]; /* potential tag */ + char patt[MAX_TAG_LEN+1]; /* potential attributes */ + char *buffer = NULL; + char *ptr; + char *end; + union { + ssize_t sig; + size_t uns; + } len; + size_t bufsize = 0; + size_t outalloc = 256; + int in_wanted_part = 0; + int base64 = 0; + int error; + + enum { + STATE_OUTSIDE = 0, + STATE_OUTER = 1, + STATE_INMAIN = 2, + STATE_INSUB = 3, + STATE_ILLEGAL = 4 + } state = STATE_OUTSIDE; + + *outlen = 0; + *outbuf = malloc(outalloc); + if(!*outbuf) + return GPE_OUT_OF_MEMORY; + *(*outbuf) = '\0'; + + couter[0] = cmain[0] = csub[0] = ptag[0] = patt[0] = '\0'; + + while((error = readline(&buffer, &bufsize, stream)) == GPE_OK) { + + ptr = buffer; + EAT_SPACE(ptr); + + if('<' != *ptr) { + if(in_wanted_part) { + show(("=> %s", buffer)); + error = appenddata(outbuf, outlen, &outalloc, buffer, base64); + if(error) + break; + } + continue; + } + + ptr++; + + if('/' == *ptr) { + /* + ** closing section tag + */ + + ptr++; + end = ptr; + EAT_WORD(end); + if((len.sig = end - ptr) > MAX_TAG_LEN) { + error = GPE_NO_BUFFER_SPACE; + break; + } + memcpy(ptag, ptr, len.uns); + ptag[len.uns] = '\0'; + + if((STATE_INSUB == state) && !strcmp(csub, ptag)) { + /* end of current sub section */ + state = STATE_INMAIN; + csub[0] = '\0'; + if(in_wanted_part) { + /* end of wanted part */ + in_wanted_part = 0; + + /* Do we need to base64 decode the data? */ + if(base64) { + error = decodedata(outbuf, outlen); + if(error) + return error; + } + break; + } + } + else if((STATE_INMAIN == state) && !strcmp(cmain, ptag)) { + /* end of current main section */ + state = STATE_OUTER; + cmain[0] = '\0'; + if(in_wanted_part) { + /* end of wanted part */ + in_wanted_part = 0; + + /* Do we need to base64 decode the data? */ + if(base64) { + error = decodedata(outbuf, outlen); + if(error) + return error; + } + break; + } + } + else if((STATE_OUTER == state) && !strcmp(couter, ptag)) { + /* end of outermost file section */ + state = STATE_OUTSIDE; + couter[0] = '\0'; + if(in_wanted_part) { + /* end of wanted part */ + in_wanted_part = 0; + break; + } + } + + } + else if(!in_wanted_part) { + /* + ** opening section tag + */ + + /* get potential tag */ + end = ptr; + EAT_WORD(end); + if((len.sig = end - ptr) > MAX_TAG_LEN) { + error = GPE_NO_BUFFER_SPACE; + break; + } + memcpy(ptag, ptr, len.uns); + ptag[len.uns] = '\0'; + + /* ignore comments, doctypes and xml declarations */ + if(('!' == ptag[0]) || ('?' == ptag[0])) { + show(("* ignoring (%s)", buffer)); + continue; + } + + /* get all potential attributes */ + ptr = end; + EAT_SPACE(ptr); + end = ptr; + while(*end && ('>' != *end)) + end++; + if((len.sig = end - ptr) > MAX_TAG_LEN) { + error = GPE_NO_BUFFER_SPACE; + break; + } + memcpy(patt, ptr, len.uns); + patt[len.uns] = '\0'; + + if(STATE_OUTSIDE == state) { + /* outermost element () */ + strcpy(couter, ptag); + state = STATE_OUTER; + continue; + } + else if(STATE_OUTER == state) { + /* start of a main section */ + strcpy(cmain, ptag); + state = STATE_INMAIN; + continue; + } + else if(STATE_INMAIN == state) { + /* start of a sub section */ + strcpy(csub, ptag); + state = STATE_INSUB; + if(!strcmp(cmain, main) && !strcmp(csub, sub)) { + /* start of wanted part */ + in_wanted_part = 1; + if(strstr(patt, "base64=")) + /* bit rough test, but "mostly" functional, */ + /* treat wanted part data as base64 encoded */ + base64 = 1; + } + continue; + } + + } + + if(in_wanted_part) { + show(("=> %s", buffer)); + error = appenddata(outbuf, outlen, &outalloc, buffer, base64); + if(error) + break; + } + + } /* while */ + + free(buffer); + + if(error != GPE_OK) { + if(error == GPE_END_OF_FILE) + error = GPE_OK; + else { + free(*outbuf); + *outbuf = NULL; + *outlen = 0; + } + } + + return error; +} + diff --git a/deps/curl-7.51.0/tests/server/getpart.h b/deps/curl-7.51.0/tests/server/getpart.h new file mode 100644 index 0000000000..2773685197 --- /dev/null +++ b/deps/curl-7.51.0/tests/server/getpart.h @@ -0,0 +1,34 @@ +#ifndef HEADER_CURL_SERVER_GETPART_H +#define HEADER_CURL_SERVER_GETPART_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "server_setup.h" + +#define GPE_NO_BUFFER_SPACE -2 +#define GPE_OUT_OF_MEMORY -1 +#define GPE_OK 0 +#define GPE_END_OF_FILE 1 + +int getpart(char **outbuf, size_t *outlen, + const char *main, const char *sub, FILE *stream); + +#endif /* HEADER_CURL_SERVER_GETPART_H */ diff --git a/deps/curl-7.51.0/tests/server/resolve.c b/deps/curl-7.51.0/tests/server/resolve.c new file mode 100644 index 0000000000..206245aba9 --- /dev/null +++ b/deps/curl-7.51.0/tests/server/resolve.c @@ -0,0 +1,154 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "server_setup.h" + +/* Purpose + * + * Resolve the given name, using system name resolve functions (NOT any + * function provided by libcurl). Used to see if the name exists and thus if + * we can allow a test case to use it for testing. + * + * Like if 'localhost' actual exists etc. + * + */ + +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef _XOPEN_SOURCE_EXTENDED +/* This define is "almost" required to build on HPUX 11 */ +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif + +#define ENABLE_CURLX_PRINTF +/* make the curlx header define all printf() functions to use the curlx_* + versions instead */ +#include "curlx.h" /* from the private lib dir */ +#include "util.h" + +/* include memdebug.h last */ +#include "memdebug.h" + +static bool use_ipv6 = FALSE; +static const char *ipv_inuse = "IPv4"; + +const char *serverlogfile=""; /* for a util.c function we don't use */ + +int main(int argc, char *argv[]) +{ + int arg=1; + const char *host = NULL; + int rc = 0; + + while(argc>arg) { + if(!strcmp("--version", argv[arg])) { + printf("resolve IPv4%s\n", +#ifdef ENABLE_IPV6 + "/IPv6" +#else + "" +#endif + ); + return 0; + } + else if(!strcmp("--ipv6", argv[arg])) { + ipv_inuse = "IPv6"; + use_ipv6 = TRUE; + arg++; + } + else if(!strcmp("--ipv4", argv[arg])) { + /* for completeness, we support this option as well */ + ipv_inuse = "IPv4"; + use_ipv6 = FALSE; + arg++; + } + else { + host = argv[arg++]; + } + } + if(!host) { + puts("Usage: resolve [option] \n" + " --version\n" + " --ipv4" +#ifdef ENABLE_IPV6 + "\n --ipv6" +#endif + ); + return 1; + } + +#ifdef WIN32 + win32_init(); + atexit(win32_cleanup); +#endif + + if(!use_ipv6) { + /* gethostbyname() resolve */ + struct hostent *he; + + he = gethostbyname(host); + + rc = !he; + } + else { +#ifdef ENABLE_IPV6 + /* Check that the system has IPv6 enabled before checking the resolver */ + curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0); + if(s == CURL_SOCKET_BAD) + /* an IPv6 address was requested and we can't get/use one */ + rc = -1; + else { + sclose(s); + } + + if(rc == 0) { + /* getaddrinfo() resolve */ + struct addrinfo *ai; + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + /* Use parenthesis around functions to stop them from being replaced by + the macro in memdebug.h */ + rc = (getaddrinfo)(host, "80", &hints, &ai); + if(rc == 0) + (freeaddrinfo)(ai); + } + +#else + puts("IPv6 support has been disabled in this program"); + return 1; +#endif + } + if(rc) + printf("Resolving %s '%s' didn't work\n", ipv_inuse, host); + + return !!rc; +} diff --git a/deps/curl-7.51.0/tests/server/rtspd.c b/deps/curl-7.51.0/tests/server/rtspd.c new file mode 100644 index 0000000000..db95917a93 --- /dev/null +++ b/deps/curl-7.51.0/tests/server/rtspd.c @@ -0,0 +1,1477 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "server_setup.h" + +/* + * curl's test suite Real Time Streaming Protocol (RTSP) server. + * + * This source file was started based on curl's HTTP test suite server. + */ + +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_NETINET_TCP_H +#include /* for TCP_NODELAY */ +#endif + +#define ENABLE_CURLX_PRINTF +/* make the curlx header define all printf() functions to use the curlx_* + versions instead */ +#include "curlx.h" /* from the private lib dir */ +#include "getpart.h" +#include "util.h" +#include "server_sockaddr.h" + +/* include memdebug.h last */ +#include "memdebug.h" + +#ifdef USE_WINSOCK +#undef EINTR +#define EINTR 4 /* errno.h value */ +#undef ERANGE +#define ERANGE 34 /* errno.h value */ +#endif + +#ifdef ENABLE_IPV6 +static bool use_ipv6 = FALSE; +#endif +static const char *ipv_inuse = "IPv4"; +static int serverlogslocked = 0; + +#define REQBUFSIZ 150000 +#define REQBUFSIZ_TXT "149999" + +static long prevtestno=-1; /* previous test number we served */ +static long prevpartno=-1; /* previous part number we served */ +static bool prevbounce=FALSE; /* instructs the server to increase the part + number for a test in case the identical + testno+partno request shows up again */ + +#define RCMD_NORMALREQ 0 /* default request, use the tests file normally */ +#define RCMD_IDLE 1 /* told to sit idle */ +#define RCMD_STREAM 2 /* told to stream */ + +typedef enum { + RPROT_NONE = 0, + RPROT_RTSP = 1, + RPROT_HTTP = 2 +} reqprot_t; + +#define SET_RTP_PKT_CHN(p,c) ((p)[1] = (unsigned char)((c) & 0xFF)) + +#define SET_RTP_PKT_LEN(p,l) (((p)[2] = (unsigned char)(((l) >> 8) & 0xFF)), \ + ((p)[3] = (unsigned char)((l) & 0xFF))) + +struct httprequest { + char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */ + size_t checkindex; /* where to start checking of the request */ + size_t offset; /* size of the incoming request */ + long testno; /* test number found in the request */ + long partno; /* part number found in the request */ + bool open; /* keep connection open info, as found in the request */ + bool auth_req; /* authentication required, don't wait for body unless + there's an Authorization header */ + bool auth; /* Authorization header present in the incoming request */ + size_t cl; /* Content-Length of the incoming request */ + bool digest; /* Authorization digest header found */ + bool ntlm; /* Authorization ntlm header found */ + int pipe; /* if non-zero, expect this many requests to do a "piped" + request/response */ + int skip; /* if non-zero, the server is instructed to not read this + many bytes from a PUT/POST request. Ie the client sends N + bytes said in Content-Length, but the server only reads N + - skip bytes. */ + int rcmd; /* doing a special command, see defines above */ + reqprot_t protocol; /* request protocol, HTTP or RTSP */ + int prot_version; /* HTTP or RTSP version (major*10 + minor) */ + bool pipelining; /* true if request is pipelined */ + char *rtp_buffer; + size_t rtp_buffersize; +}; + +static int ProcessRequest(struct httprequest *req); +static void storerequest(char *reqbuf, size_t totalsize); + +#define DEFAULT_PORT 8999 + +#ifndef DEFAULT_LOGFILE +#define DEFAULT_LOGFILE "log/rtspd.log" +#endif + +const char *serverlogfile = DEFAULT_LOGFILE; + +#define RTSPDVERSION "cURL test suite RTSP server/0.1" + +#define REQUEST_DUMP "log/server.input" +#define RESPONSE_DUMP "log/server.response" + +/* very-big-path support */ +#define MAXDOCNAMELEN 140000 +#define MAXDOCNAMELEN_TXT "139999" + +#define REQUEST_KEYWORD_SIZE 256 +#define REQUEST_KEYWORD_SIZE_TXT "255" + +#define CMD_AUTH_REQUIRED "auth_required" + +/* 'idle' means that it will accept the request fine but never respond + any data. Just keep the connection alive. */ +#define CMD_IDLE "idle" + +/* 'stream' means to send a never-ending stream of data */ +#define CMD_STREAM "stream" + +#define END_OF_HEADERS "\r\n\r\n" + +enum { + DOCNUMBER_NOTHING = -7, + DOCNUMBER_QUIT = -6, + DOCNUMBER_BADCONNECT = -5, + DOCNUMBER_INTERNAL= -4, + DOCNUMBER_CONNECT = -3, + DOCNUMBER_WERULEZ = -2, + DOCNUMBER_404 = -1 +}; + + +/* sent as reply to a QUIT */ +static const char *docquit = +"HTTP/1.1 200 Goodbye" END_OF_HEADERS; + +/* sent as reply to a CONNECT */ +static const char *docconnect = +"HTTP/1.1 200 Mighty fine indeed" END_OF_HEADERS; + +/* sent as reply to a "bad" CONNECT */ +static const char *docbadconnect = +"HTTP/1.1 501 Forbidden you fool" END_OF_HEADERS; + +/* send back this on HTTP 404 file not found */ +static const char *doc404_HTTP = "HTTP/1.1 404 Not Found\r\n" + "Server: " RTSPDVERSION "\r\n" + "Connection: close\r\n" + "Content-Type: text/html" + END_OF_HEADERS + "\n" + "\n" + "404 Not Found\n" + "\n" + "

Not Found

\n" + "The requested URL was not found on this server.\n" + "


" RTSPDVERSION "
\n" "\n"; + +/* send back this on RTSP 404 file not found */ +static const char *doc404_RTSP = "RTSP/1.0 404 Not Found\r\n" + "Server: " RTSPDVERSION + END_OF_HEADERS; + +/* Default size to send away fake RTP data */ +#define RTP_DATA_SIZE 12 +static const char *RTP_DATA = "$_1234\n\0asdf"; + +/* do-nothing macro replacement for systems which lack siginterrupt() */ + +#ifndef HAVE_SIGINTERRUPT +#define siginterrupt(x,y) do {} while(0) +#endif + +/* vars used to keep around previous signal handlers */ + +typedef RETSIGTYPE (*SIGHANDLER_T)(int); + +#ifdef SIGHUP +static SIGHANDLER_T old_sighup_handler = SIG_ERR; +#endif + +#ifdef SIGPIPE +static SIGHANDLER_T old_sigpipe_handler = SIG_ERR; +#endif + +#ifdef SIGALRM +static SIGHANDLER_T old_sigalrm_handler = SIG_ERR; +#endif + +#ifdef SIGINT +static SIGHANDLER_T old_sigint_handler = SIG_ERR; +#endif + +#ifdef SIGTERM +static SIGHANDLER_T old_sigterm_handler = SIG_ERR; +#endif + +#if defined(SIGBREAK) && defined(WIN32) +static SIGHANDLER_T old_sigbreak_handler = SIG_ERR; +#endif + +/* var which if set indicates that the program should finish execution */ + +SIG_ATOMIC_T got_exit_signal = 0; + +/* if next is set indicates the first signal handled in exit_signal_handler */ + +static volatile int exit_signal = 0; + +/* signal handler that will be triggered to indicate that the program + should finish its execution in a controlled manner as soon as possible. + The first time this is called it will set got_exit_signal to one and + store in exit_signal the signal that triggered its execution. */ + +static RETSIGTYPE exit_signal_handler(int signum) +{ + int old_errno = errno; + if(got_exit_signal == 0) { + got_exit_signal = 1; + exit_signal = signum; + } + (void)signal(signum, exit_signal_handler); + errno = old_errno; +} + +static void install_signal_handlers(void) +{ +#ifdef SIGHUP + /* ignore SIGHUP signal */ + if((old_sighup_handler = signal(SIGHUP, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGHUP handler: %s", strerror(errno)); +#endif +#ifdef SIGPIPE + /* ignore SIGPIPE signal */ + if((old_sigpipe_handler = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGPIPE handler: %s", strerror(errno)); +#endif +#ifdef SIGALRM + /* ignore SIGALRM signal */ + if((old_sigalrm_handler = signal(SIGALRM, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGALRM handler: %s", strerror(errno)); +#endif +#ifdef SIGINT + /* handle SIGINT signal with our exit_signal_handler */ + if((old_sigint_handler = signal(SIGINT, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGINT handler: %s", strerror(errno)); + else + siginterrupt(SIGINT, 1); +#endif +#ifdef SIGTERM + /* handle SIGTERM signal with our exit_signal_handler */ + if((old_sigterm_handler = signal(SIGTERM, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGTERM handler: %s", strerror(errno)); + else + siginterrupt(SIGTERM, 1); +#endif +#if defined(SIGBREAK) && defined(WIN32) + /* handle SIGBREAK signal with our exit_signal_handler */ + if((old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGBREAK handler: %s", strerror(errno)); + else + siginterrupt(SIGBREAK, 1); +#endif +} + +static void restore_signal_handlers(void) +{ +#ifdef SIGHUP + if(SIG_ERR != old_sighup_handler) + (void)signal(SIGHUP, old_sighup_handler); +#endif +#ifdef SIGPIPE + if(SIG_ERR != old_sigpipe_handler) + (void)signal(SIGPIPE, old_sigpipe_handler); +#endif +#ifdef SIGALRM + if(SIG_ERR != old_sigalrm_handler) + (void)signal(SIGALRM, old_sigalrm_handler); +#endif +#ifdef SIGINT + if(SIG_ERR != old_sigint_handler) + (void)signal(SIGINT, old_sigint_handler); +#endif +#ifdef SIGTERM + if(SIG_ERR != old_sigterm_handler) + (void)signal(SIGTERM, old_sigterm_handler); +#endif +#if defined(SIGBREAK) && defined(WIN32) + if(SIG_ERR != old_sigbreak_handler) + (void)signal(SIGBREAK, old_sigbreak_handler); +#endif +} + +static int ProcessRequest(struct httprequest *req) +{ + char *line=&req->reqbuf[req->checkindex]; + bool chunked = FALSE; + static char request[REQUEST_KEYWORD_SIZE]; + static char doc[MAXDOCNAMELEN]; + static char prot_str[5]; + char logbuf[256]; + int prot_major, prot_minor; + char *end; + int error; + end = strstr(line, END_OF_HEADERS); + + logmsg("ProcessRequest() called with testno %ld and line [%s]", + req->testno, line); + + /* try to figure out the request characteristics as soon as possible, but + only once! */ + if((req->testno == DOCNUMBER_NOTHING) && + sscanf(line, + "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s %4s/%d.%d", + request, + doc, + prot_str, + &prot_major, + &prot_minor) == 5) { + char *ptr; + + if(!strcmp(prot_str, "HTTP")) { + req->protocol = RPROT_HTTP; + } + else if(!strcmp(prot_str, "RTSP")) { + req->protocol = RPROT_RTSP; + } + else { + req->protocol = RPROT_NONE; + logmsg("got unknown protocol %s", prot_str); + return 1; + } + + req->prot_version = prot_major*10 + prot_minor; + + /* find the last slash */ + ptr = strrchr(doc, '/'); + + /* get the number after it */ + if(ptr) { + FILE *stream; + char *filename; + + if((strlen(doc) + strlen(request)) < 200) + snprintf(logbuf, sizeof(logbuf), "Got request: %s %s %s/%d.%d", + request, doc, prot_str, prot_major, prot_minor); + else + snprintf(logbuf, sizeof(logbuf), "Got a *HUGE* request %s/%d.%d", + prot_str, prot_major, prot_minor); + logmsg("%s", logbuf); + + if(!strncmp("/verifiedserver", ptr, 15)) { + logmsg("Are-we-friendly question received"); + req->testno = DOCNUMBER_WERULEZ; + return 1; /* done */ + } + + if(!strncmp("/quit", ptr, 5)) { + logmsg("Request-to-quit received"); + req->testno = DOCNUMBER_QUIT; + return 1; /* done */ + } + + ptr++; /* skip the slash */ + + /* skip all non-numericals following the slash */ + while(*ptr && !ISDIGIT(*ptr)) + ptr++; + + req->testno = strtol(ptr, &ptr, 10); + + if(req->testno > 10000) { + req->partno = req->testno % 10000; + req->testno /= 10000; + } + else + req->partno = 0; + + snprintf(logbuf, sizeof(logbuf), "Requested test number %ld part %ld", + req->testno, req->partno); + logmsg("%s", logbuf); + + filename = test2file(req->testno); + + stream=fopen(filename, "rb"); + if(!stream) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg("Error opening file: %s", filename); + logmsg("Couldn't open test file %ld", req->testno); + req->open = FALSE; /* closes connection */ + return 1; /* done */ + } + else { + char *cmd = NULL; + size_t cmdsize = 0; + int num=0; + + int rtp_channel = 0; + int rtp_size = 0; + int rtp_partno = -1; + int i = 0; + char *rtp_scratch = NULL; + + /* get the custom server control "commands" */ + error = getpart(&cmd, &cmdsize, "reply", "servercmd", stream); + fclose(stream); + if(error) { + logmsg("getpart() failed with error: %d", error); + req->open = FALSE; /* closes connection */ + return 1; /* done */ + } + ptr = cmd; + + if(cmdsize) { + logmsg("Found a reply-servercmd section!"); + do { + if(!strncmp(CMD_AUTH_REQUIRED, ptr, strlen(CMD_AUTH_REQUIRED))) { + logmsg("instructed to require authorization header"); + req->auth_req = TRUE; + } + else if(!strncmp(CMD_IDLE, ptr, strlen(CMD_IDLE))) { + logmsg("instructed to idle"); + req->rcmd = RCMD_IDLE; + req->open = TRUE; + } + else if(!strncmp(CMD_STREAM, ptr, strlen(CMD_STREAM))) { + logmsg("instructed to stream"); + req->rcmd = RCMD_STREAM; + } + else if(1 == sscanf(ptr, "pipe: %d", &num)) { + logmsg("instructed to allow a pipe size of %d", num); + if(num < 0) + logmsg("negative pipe size ignored"); + else if(num > 0) + req->pipe = num-1; /* decrease by one since we don't count the + first request in this number */ + } + else if(1 == sscanf(ptr, "skip: %d", &num)) { + logmsg("instructed to skip this number of bytes %d", num); + req->skip = num; + } + else if(3 == sscanf(ptr, "rtp: part %d channel %d size %d", + &rtp_partno, &rtp_channel, &rtp_size)) { + + if(rtp_partno == req->partno) { + logmsg("RTP: part %d channel %d size %d", + rtp_partno, rtp_channel, rtp_size); + + /* Make our scratch buffer enough to fit all the + * desired data and one for padding */ + rtp_scratch = malloc(rtp_size + 4 + RTP_DATA_SIZE); + + /* RTP is signalled with a $ */ + rtp_scratch[0] = '$'; + + /* The channel follows and is one byte */ + SET_RTP_PKT_CHN(rtp_scratch, rtp_channel); + + /* Length follows and is a two byte short in network order */ + SET_RTP_PKT_LEN(rtp_scratch, rtp_size); + + /* Fill it with junk data */ + for(i = 0; i < rtp_size; i+= RTP_DATA_SIZE) { + memcpy(rtp_scratch + 4 + i, RTP_DATA, RTP_DATA_SIZE); + } + + if(req->rtp_buffer == NULL) { + req->rtp_buffer = rtp_scratch; + req->rtp_buffersize = rtp_size + 4; + } + else { + req->rtp_buffer = realloc(req->rtp_buffer, + req->rtp_buffersize + + rtp_size + 4); + memcpy(req->rtp_buffer + req->rtp_buffersize, rtp_scratch, + rtp_size + 4); + req->rtp_buffersize += rtp_size + 4; + free(rtp_scratch); + } + logmsg("rtp_buffersize is %zu, rtp_size is %d.", + req->rtp_buffersize, rtp_size); + } + } + else { + logmsg("funny instruction found: %s", ptr); + } + + ptr = strchr(ptr, '\n'); + if(ptr) + ptr++; + else + ptr = NULL; + } while(ptr && *ptr); + logmsg("Done parsing server commands"); + } + free(cmd); + } + } + else { + if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d", + doc, &prot_major, &prot_minor) == 3) { + snprintf(logbuf, sizeof(logbuf), + "Received a CONNECT %s HTTP/%d.%d request", + doc, prot_major, prot_minor); + logmsg("%s", logbuf); + + if(req->prot_version == 10) + req->open = FALSE; /* HTTP 1.0 closes connection by default */ + + if(!strncmp(doc, "bad", 3)) + /* if the host name starts with bad, we fake an error here */ + req->testno = DOCNUMBER_BADCONNECT; + else if(!strncmp(doc, "test", 4)) { + /* if the host name starts with test, the port number used in the + CONNECT line will be used as test number! */ + char *portp = strchr(doc, ':'); + if(portp && (*(portp+1) != '\0') && ISDIGIT(*(portp+1))) + req->testno = strtol(portp+1, NULL, 10); + else + req->testno = DOCNUMBER_CONNECT; + } + else + req->testno = DOCNUMBER_CONNECT; + } + else { + logmsg("Did not find test number in PATH"); + req->testno = DOCNUMBER_404; + } + } + } + + if(!end) { + /* we don't have a complete request yet! */ + logmsg("ProcessRequest returned without a complete request"); + return 0; /* not complete yet */ + } + logmsg("ProcessRequest found a complete request"); + + if(req->pipe) + /* we do have a full set, advance the checkindex to after the end of the + headers, for the pipelining case mostly */ + req->checkindex += (end - line) + strlen(END_OF_HEADERS); + + /* **** Persistence **** + * + * If the request is a HTTP/1.0 one, we close the connection unconditionally + * when we're done. + * + * If the request is a HTTP/1.1 one, we MUST check for a "Connection:" + * header that might say "close". If it does, we close a connection when + * this request is processed. Otherwise, we keep the connection alive for X + * seconds. + */ + + do { + if(got_exit_signal) + return 1; /* done */ + + if((req->cl==0) && strncasecompare("Content-Length:", line, 15)) { + /* If we don't ignore content-length, we read it and we read the whole + request including the body before we return. If we've been told to + ignore the content-length, we will return as soon as all headers + have been received */ + char *endptr; + char *ptr = line + 15; + unsigned long clen = 0; + while(*ptr && ISSPACE(*ptr)) + ptr++; + endptr = ptr; + errno = 0; + clen = strtoul(ptr, &endptr, 10); + if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == errno)) { + /* this assumes that a zero Content-Length is valid */ + logmsg("Found invalid Content-Length: (%s) in the request", ptr); + req->open = FALSE; /* closes connection */ + return 1; /* done */ + } + req->cl = clen - req->skip; + + logmsg("Found Content-Length: %lu in the request", clen); + if(req->skip) + logmsg("... but will abort after %zu bytes", req->cl); + break; + } + else if(strncasecompare("Transfer-Encoding: chunked", line, + strlen("Transfer-Encoding: chunked"))) { + /* chunked data coming in */ + chunked = TRUE; + } + + if(chunked) { + if(strstr(req->reqbuf, "\r\n0\r\n\r\n")) + /* end of chunks reached */ + return 1; /* done */ + else + return 0; /* not done */ + } + + line = strchr(line, '\n'); + if(line) + line++; + + } while(line); + + if(!req->auth && strstr(req->reqbuf, "Authorization:")) { + req->auth = TRUE; /* Authorization: header present! */ + if(req->auth_req) + logmsg("Authorization header found, as required"); + } + + if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) { + /* If the client is passing this Digest-header, we set the part number + to 1000. Not only to spice up the complexity of this, but to make + Digest stuff to work in the test suite. */ + req->partno += 1000; + req->digest = TRUE; /* header found */ + logmsg("Received Digest request, sending back data %ld", req->partno); + } + else if(!req->ntlm && + strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) { + /* If the client is passing this type-3 NTLM header */ + req->partno += 1002; + req->ntlm = TRUE; /* NTLM found */ + logmsg("Received NTLM type-3, sending back data %ld", req->partno); + if(req->cl) { + logmsg(" Expecting %zu POSTed bytes", req->cl); + } + } + else if(!req->ntlm && + strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) { + /* If the client is passing this type-1 NTLM header */ + req->partno += 1001; + req->ntlm = TRUE; /* NTLM found */ + logmsg("Received NTLM type-1, sending back data %ld", req->partno); + } + else if((req->partno >= 1000) && + strstr(req->reqbuf, "Authorization: Basic")) { + /* If the client is passing this Basic-header and the part number is + already >=1000, we add 1 to the part number. This allows simple Basic + authentication negotiation to work in the test suite. */ + req->partno += 1; + logmsg("Received Basic request, sending back data %ld", req->partno); + } + if(strstr(req->reqbuf, "Connection: close")) + req->open = FALSE; /* close connection after this request */ + + if(!req->pipe && + req->open && + req->prot_version >= 11 && + end && + req->reqbuf + req->offset > end + strlen(END_OF_HEADERS) && + (!strncmp(req->reqbuf, "GET", strlen("GET")) || + !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) { + /* If we have a persistent connection, HTTP version >= 1.1 + and GET/HEAD request, enable pipelining. */ + req->checkindex = (end - req->reqbuf) + strlen(END_OF_HEADERS); + req->pipelining = TRUE; + } + + while(req->pipe) { + if(got_exit_signal) + return 1; /* done */ + /* scan for more header ends within this chunk */ + line = &req->reqbuf[req->checkindex]; + end = strstr(line, END_OF_HEADERS); + if(!end) + break; + req->checkindex += (end - line) + strlen(END_OF_HEADERS); + req->pipe--; + } + + /* If authentication is required and no auth was provided, end now. This + makes the server NOT wait for PUT/POST data and you can then make the + test case send a rejection before any such data has been sent. Test case + 154 uses this.*/ + if(req->auth_req && !req->auth) + return 1; /* done */ + + if(req->cl > 0) { + if(req->cl <= req->offset - (end - req->reqbuf) - strlen(END_OF_HEADERS)) + return 1; /* done */ + else + return 0; /* not complete yet */ + } + + return 1; /* done */ +} + +/* store the entire request in a file */ +static void storerequest(char *reqbuf, size_t totalsize) +{ + int res; + int error = 0; + size_t written; + size_t writeleft; + FILE *dump; + + if(reqbuf == NULL) + return; + if(totalsize == 0) + return; + + do { + dump = fopen(REQUEST_DUMP, "ab"); + } while((dump == NULL) && ((error = errno) == EINTR)); + if(dump == NULL) { + logmsg("Error opening file %s error: %d %s", + REQUEST_DUMP, error, strerror(error)); + logmsg("Failed to write request input to " REQUEST_DUMP); + return; + } + + writeleft = totalsize; + do { + written = fwrite(&reqbuf[totalsize-writeleft], + 1, writeleft, dump); + if(got_exit_signal) + goto storerequest_cleanup; + if(written > 0) + writeleft -= written; + } while((writeleft > 0) && ((error = errno) == EINTR)); + + if(writeleft == 0) + logmsg("Wrote request (%zu bytes) input to " REQUEST_DUMP, totalsize); + else if(writeleft > 0) { + logmsg("Error writing file %s error: %d %s", + REQUEST_DUMP, error, strerror(error)); + logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s", + totalsize-writeleft, totalsize, REQUEST_DUMP); + } + +storerequest_cleanup: + + do { + res = fclose(dump); + } while(res && ((error = errno) == EINTR)); + if(res) + logmsg("Error closing file %s error: %d %s", + REQUEST_DUMP, error, strerror(error)); +} + +/* return 0 on success, non-zero on failure */ +static int get_request(curl_socket_t sock, struct httprequest *req) +{ + int error; + int fail = 0; + int done_processing = 0; + char *reqbuf = req->reqbuf; + ssize_t got = 0; + + char *pipereq = NULL; + size_t pipereq_length = 0; + + if(req->pipelining) { + pipereq = reqbuf + req->checkindex; + pipereq_length = req->offset - req->checkindex; + } + + /*** Init the httprequest structure properly for the upcoming request ***/ + + req->checkindex = 0; + req->offset = 0; + req->testno = DOCNUMBER_NOTHING; + req->partno = 0; + req->open = TRUE; + req->auth_req = FALSE; + req->auth = FALSE; + req->cl = 0; + req->digest = FALSE; + req->ntlm = FALSE; + req->pipe = 0; + req->skip = 0; + req->rcmd = RCMD_NORMALREQ; + req->protocol = RPROT_NONE; + req->prot_version = 0; + req->pipelining = FALSE; + req->rtp_buffer = NULL; + req->rtp_buffersize = 0; + + /*** end of httprequest init ***/ + + while(!done_processing && (req->offset < REQBUFSIZ-1)) { + if(pipereq_length && pipereq) { + memmove(reqbuf, pipereq, pipereq_length); + got = curlx_uztosz(pipereq_length); + pipereq_length = 0; + } + else { + if(req->skip) + /* we are instructed to not read the entire thing, so we make sure to + only read what we're supposed to and NOT read the enire thing the + client wants to send! */ + got = sread(sock, reqbuf + req->offset, req->cl); + else + got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset); + } + if(got_exit_signal) + return 1; + if(got == 0) { + logmsg("Connection closed by client"); + fail = 1; + } + else if(got < 0) { + error = SOCKERRNO; + logmsg("recv() returned error: (%d) %s", error, strerror(error)); + fail = 1; + } + if(fail) { + /* dump the request received so far to the external file */ + reqbuf[req->offset] = '\0'; + storerequest(reqbuf, req->offset); + return 1; + } + + logmsg("Read %zd bytes", got); + + req->offset += (size_t)got; + reqbuf[req->offset] = '\0'; + + done_processing = ProcessRequest(req); + if(got_exit_signal) + return 1; + if(done_processing && req->pipe) { + logmsg("Waiting for another piped request"); + done_processing = 0; + req->pipe--; + } + } + + if((req->offset == REQBUFSIZ-1) && (got > 0)) { + logmsg("Request would overflow buffer, closing connection"); + /* dump request received so far to external file anyway */ + reqbuf[REQBUFSIZ-1] = '\0'; + fail = 1; + } + else if(req->offset > REQBUFSIZ-1) { + logmsg("Request buffer overflow, closing connection"); + /* dump request received so far to external file anyway */ + reqbuf[REQBUFSIZ-1] = '\0'; + fail = 1; + } + else + reqbuf[req->offset] = '\0'; + + /* dump the request to an external file */ + storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset); + if(got_exit_signal) + return 1; + + return fail; /* return 0 on success */ +} + +/* returns -1 on failure */ +static int send_doc(curl_socket_t sock, struct httprequest *req) +{ + ssize_t written; + size_t count; + const char *buffer; + char *ptr=NULL; + FILE *stream; + char *cmd=NULL; + size_t cmdsize=0; + FILE *dump; + bool persistant = TRUE; + bool sendfailure = FALSE; + size_t responsesize; + int error = 0; + int res; + + static char weare[256]; + + char partbuf[80]="data"; + + logmsg("Send response number %ld part %ld", req->testno, req->partno); + + switch(req->rcmd) { + default: + case RCMD_NORMALREQ: + break; /* continue with business as usual */ + case RCMD_STREAM: +#define STREAMTHIS "a string to stream 01234567890\n" + count = strlen(STREAMTHIS); + for(;;) { + written = swrite(sock, STREAMTHIS, count); + if(got_exit_signal) + return -1; + if(written != (ssize_t)count) { + logmsg("Stopped streaming"); + break; + } + } + return -1; + case RCMD_IDLE: + /* Do nothing. Sit idle. Pretend it rains. */ + return 0; + } + + req->open = FALSE; + + if(req->testno < 0) { + size_t msglen; + char msgbuf[64]; + + switch(req->testno) { + case DOCNUMBER_QUIT: + logmsg("Replying to QUIT"); + buffer = docquit; + break; + case DOCNUMBER_WERULEZ: + /* we got a "friends?" question, reply back that we sure are */ + logmsg("Identifying ourselves as friends"); + snprintf(msgbuf, sizeof(msgbuf), "RTSP_SERVER WE ROOLZ: %ld\r\n", + (long)getpid()); + msglen = strlen(msgbuf); + snprintf(weare, sizeof(weare), + "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s", + msglen, msgbuf); + buffer = weare; + break; + case DOCNUMBER_INTERNAL: + logmsg("Bailing out due to internal error"); + return -1; + case DOCNUMBER_CONNECT: + logmsg("Replying to CONNECT"); + buffer = docconnect; + break; + case DOCNUMBER_BADCONNECT: + logmsg("Replying to a bad CONNECT"); + buffer = docbadconnect; + break; + case DOCNUMBER_404: + default: + logmsg("Replying to with a 404"); + if(req->protocol == RPROT_HTTP) { + buffer = doc404_HTTP; + } + else { + buffer = doc404_RTSP; + } + break; + } + + count = strlen(buffer); + } + else { + char *filename = test2file(req->testno); + + if(0 != req->partno) + snprintf(partbuf, sizeof(partbuf), "data%ld", req->partno); + + stream=fopen(filename, "rb"); + if(!stream) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg("Error opening file: %s", filename); + logmsg("Couldn't open test file"); + return 0; + } + else { + error = getpart(&ptr, &count, "reply", partbuf, stream); + fclose(stream); + if(error) { + logmsg("getpart() failed with error: %d", error); + return 0; + } + buffer = ptr; + } + + if(got_exit_signal) { + free(ptr); + return -1; + } + + /* re-open the same file again */ + stream=fopen(filename, "rb"); + if(!stream) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg("Error opening file: %s", filename); + logmsg("Couldn't open test file"); + free(ptr); + return 0; + } + else { + /* get the custom server control "commands" */ + error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream); + fclose(stream); + if(error) { + logmsg("getpart() failed with error: %d", error); + free(ptr); + return 0; + } + } + } + + if(got_exit_signal) { + free(ptr); + free(cmd); + return -1; + } + + /* If the word 'swsclose' is present anywhere in the reply chunk, the + connection will be closed after the data has been sent to the requesting + client... */ + if(strstr(buffer, "swsclose") || !count) { + persistant = FALSE; + logmsg("connection close instruction \"swsclose\" found in response"); + } + if(strstr(buffer, "swsbounce")) { + prevbounce = TRUE; + logmsg("enable \"swsbounce\" in the next request"); + } + else + prevbounce = FALSE; + + dump = fopen(RESPONSE_DUMP, "ab"); + if(!dump) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg("Error opening file: %s", RESPONSE_DUMP); + logmsg("couldn't create logfile: " RESPONSE_DUMP); + free(ptr); + free(cmd); + return -1; + } + + responsesize = count; + do { + /* Ok, we send no more than 200 bytes at a time, just to make sure that + larger chunks are split up so that the client will need to do multiple + recv() calls to get it and thus we exercise that code better */ + size_t num = count; + if(num > 200) + num = 200; + written = swrite(sock, buffer, num); + if(written < 0) { + sendfailure = TRUE; + break; + } + else { + logmsg("Sent off %zd bytes", written); + } + /* write to file as well */ + fwrite(buffer, 1, (size_t)written, dump); + if(got_exit_signal) + break; + + count -= written; + buffer += written; + } while(count>0); + + /* Send out any RTP data */ + if(req->rtp_buffer) { + logmsg("About to write %zu RTP bytes", req->rtp_buffersize); + count = req->rtp_buffersize; + do { + size_t num = count; + if(num > 200) + num = 200; + written = swrite(sock, req->rtp_buffer + (req->rtp_buffersize - count), + num); + if(written < 0) { + sendfailure = TRUE; + break; + } + count -= written; + } while(count > 0); + + free(req->rtp_buffer); + req->rtp_buffersize = 0; + } + + do { + res = fclose(dump); + } while(res && ((error = errno) == EINTR)); + if(res) + logmsg("Error closing file %s error: %d %s", + RESPONSE_DUMP, error, strerror(error)); + + if(got_exit_signal) { + free(ptr); + free(cmd); + return -1; + } + + if(sendfailure) { + logmsg("Sending response failed. Only (%zu bytes) of " + "(%zu bytes) were sent", + responsesize-count, responsesize); + free(ptr); + free(cmd); + return -1; + } + + logmsg("Response sent (%zu bytes) and written to " RESPONSE_DUMP, + responsesize); + free(ptr); + + if(cmdsize > 0) { + char command[32]; + int quarters; + int num; + ptr=cmd; + do { + if(2 == sscanf(ptr, "%31s %d", command, &num)) { + if(!strcmp("wait", command)) { + logmsg("Told to sleep for %d seconds", num); + quarters = num * 4; + while(quarters > 0) { + quarters--; + res = wait_ms(250); + if(got_exit_signal) + break; + if(res) { + /* should not happen */ + error = errno; + logmsg("wait_ms() failed with error: (%d) %s", + error, strerror(error)); + break; + } + } + if(!quarters) + logmsg("Continuing after sleeping %d seconds", num); + } + else + logmsg("Unknown command in reply command section"); + } + ptr = strchr(ptr, '\n'); + if(ptr) + ptr++; + else + ptr = NULL; + } while(ptr && *ptr); + } + free(cmd); + req->open = persistant; + + prevtestno = req->testno; + prevpartno = req->partno; + + return 0; +} + + +int main(int argc, char *argv[]) +{ + srvr_sockaddr_union_t me; + curl_socket_t sock = CURL_SOCKET_BAD; + curl_socket_t msgsock = CURL_SOCKET_BAD; + int wrotepidfile = 0; + int flag; + unsigned short port = DEFAULT_PORT; + char *pidname= (char *)".rtsp.pid"; + struct httprequest req; + int rc; + int error; + int arg=1; + long pid; + + while(argc>arg) { + if(!strcmp("--version", argv[arg])) { + printf("rtspd IPv4%s" + "\n" + , +#ifdef ENABLE_IPV6 + "/IPv6" +#else + "" +#endif + ); + return 0; + } + else if(!strcmp("--pidfile", argv[arg])) { + arg++; + if(argc>arg) + pidname = argv[arg++]; + } + else if(!strcmp("--logfile", argv[arg])) { + arg++; + if(argc>arg) + serverlogfile = argv[arg++]; + } + else if(!strcmp("--ipv4", argv[arg])) { +#ifdef ENABLE_IPV6 + ipv_inuse = "IPv4"; + use_ipv6 = FALSE; +#endif + arg++; + } + else if(!strcmp("--ipv6", argv[arg])) { +#ifdef ENABLE_IPV6 + ipv_inuse = "IPv6"; + use_ipv6 = TRUE; +#endif + arg++; + } + else if(!strcmp("--port", argv[arg])) { + arg++; + if(argc>arg) { + char *endptr; + unsigned long ulnum = strtoul(argv[arg], &endptr, 10); + if((endptr != argv[arg] + strlen(argv[arg])) || + (ulnum < 1025UL) || (ulnum > 65535UL)) { + fprintf(stderr, "rtspd: invalid --port argument (%s)\n", + argv[arg]); + return 0; + } + port = curlx_ultous(ulnum); + arg++; + } + } + else if(!strcmp("--srcdir", argv[arg])) { + arg++; + if(argc>arg) { + path = argv[arg]; + arg++; + } + } + else { + puts("Usage: rtspd [option]\n" + " --version\n" + " --logfile [file]\n" + " --pidfile [file]\n" + " --ipv4\n" + " --ipv6\n" + " --port [port]\n" + " --srcdir [path]"); + return 0; + } + } + +#ifdef WIN32 + win32_init(); + atexit(win32_cleanup); +#endif + + install_signal_handlers(); + + pid = (long)getpid(); + +#ifdef ENABLE_IPV6 + if(!use_ipv6) +#endif + sock = socket(AF_INET, SOCK_STREAM, 0); +#ifdef ENABLE_IPV6 + else + sock = socket(AF_INET6, SOCK_STREAM, 0); +#endif + + if(CURL_SOCKET_BAD == sock) { + error = SOCKERRNO; + logmsg("Error creating socket: (%d) %s", + error, strerror(error)); + goto server_cleanup; + } + + flag = 1; + if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (void *)&flag, sizeof(flag))) { + error = SOCKERRNO; + logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s", + error, strerror(error)); + goto server_cleanup; + } + +#ifdef ENABLE_IPV6 + if(!use_ipv6) { +#endif + memset(&me.sa4, 0, sizeof(me.sa4)); + me.sa4.sin_family = AF_INET; + me.sa4.sin_addr.s_addr = INADDR_ANY; + me.sa4.sin_port = htons(port); + rc = bind(sock, &me.sa, sizeof(me.sa4)); +#ifdef ENABLE_IPV6 + } + else { + memset(&me.sa6, 0, sizeof(me.sa6)); + me.sa6.sin6_family = AF_INET6; + me.sa6.sin6_addr = in6addr_any; + me.sa6.sin6_port = htons(port); + rc = bind(sock, &me.sa, sizeof(me.sa6)); + } +#endif /* ENABLE_IPV6 */ + if(0 != rc) { + error = SOCKERRNO; + logmsg("Error binding socket on port %hu: (%d) %s", + port, error, strerror(error)); + goto server_cleanup; + } + + logmsg("Running %s version on port %d", ipv_inuse, (int)port); + + /* start accepting connections */ + rc = listen(sock, 5); + if(0 != rc) { + error = SOCKERRNO; + logmsg("listen() failed with error: (%d) %s", + error, strerror(error)); + goto server_cleanup; + } + + /* + ** As soon as this server writes its pid file the test harness will + ** attempt to connect to this server and initiate its verification. + */ + + wrotepidfile = write_pidfile(pidname); + if(!wrotepidfile) + goto server_cleanup; + + for(;;) { + msgsock = accept(sock, NULL, NULL); + + if(got_exit_signal) + break; + if(CURL_SOCKET_BAD == msgsock) { + error = SOCKERRNO; + logmsg("MAJOR ERROR: accept() failed with error: (%d) %s", + error, strerror(error)); + break; + } + + /* + ** As soon as this server acepts a connection from the test harness it + ** must set the server logs advisor read lock to indicate that server + ** logs should not be read until this lock is removed by this server. + */ + + set_advisor_read_lock(SERVERLOGS_LOCK); + serverlogslocked = 1; + + logmsg("====> Client connect"); + +#ifdef TCP_NODELAY + /* + * Disable the Nagle algorithm to make it easier to send out a large + * response in many small segments to torture the clients more. + */ + flag = 1; + if(setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY, + (void *)&flag, sizeof(flag)) == -1) { + logmsg("====> TCP_NODELAY failed"); + } +#endif + + /* initialization of httprequest struct is done in get_request(), but due + to pipelining treatment the pipelining struct field must be initialized + previously to FALSE every time a new connection arrives. */ + + req.pipelining = FALSE; + + do { + if(got_exit_signal) + break; + + if(get_request(msgsock, &req)) + /* non-zero means error, break out of loop */ + break; + + if(prevbounce) { + /* bounce treatment requested */ + if((req.testno == prevtestno) && + (req.partno == prevpartno)) { + req.partno++; + logmsg("BOUNCE part number to %ld", req.partno); + } + else { + prevbounce = FALSE; + prevtestno = -1; + prevpartno = -1; + } + } + + send_doc(msgsock, &req); + if(got_exit_signal) + break; + + if((req.testno < 0) && (req.testno != DOCNUMBER_CONNECT)) { + logmsg("special request received, no persistency"); + break; + } + if(!req.open) { + logmsg("instructed to close connection after server-reply"); + break; + } + + if(req.open) + logmsg("=> persistant connection request ended, awaits new request"); + /* if we got a CONNECT, loop and get another request as well! */ + } while(req.open || (req.testno == DOCNUMBER_CONNECT)); + + if(got_exit_signal) + break; + + logmsg("====> Client disconnect"); + sclose(msgsock); + msgsock = CURL_SOCKET_BAD; + + if(serverlogslocked) { + serverlogslocked = 0; + clear_advisor_read_lock(SERVERLOGS_LOCK); + } + + if(req.testno == DOCNUMBER_QUIT) + break; + } + +server_cleanup: + + if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD)) + sclose(msgsock); + + if(sock != CURL_SOCKET_BAD) + sclose(sock); + + if(got_exit_signal) + logmsg("signalled to die"); + + if(wrotepidfile) + unlink(pidname); + + if(serverlogslocked) { + serverlogslocked = 0; + clear_advisor_read_lock(SERVERLOGS_LOCK); + } + + restore_signal_handlers(); + + if(got_exit_signal) { + logmsg("========> %s rtspd (port: %d pid: %ld) exits with signal (%d)", + ipv_inuse, (int)port, pid, exit_signal); + /* + * To properly set the return status of the process we + * must raise the same signal SIGINT or SIGTERM that we + * caught and let the old handler take care of it. + */ + raise(exit_signal); + } + + logmsg("========> rtspd quits"); + return 0; +} + diff --git a/deps/curl-7.51.0/tests/server/server_setup.h b/deps/curl-7.51.0/tests/server/server_setup.h new file mode 100644 index 0000000000..76c462369b --- /dev/null +++ b/deps/curl-7.51.0/tests/server/server_setup.h @@ -0,0 +1,29 @@ +#ifndef HEADER_CURL_SERVER_SETUP_H +#define HEADER_CURL_SERVER_SETUP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#define CURL_NO_OLDIES + +#include "curl_setup.h" /* portability help from the lib directory */ + +#endif /* HEADER_CURL_SERVER_SETUP_H */ diff --git a/deps/curl-7.51.0/tests/server/server_sockaddr.h b/deps/curl-7.51.0/tests/server/server_sockaddr.h new file mode 100644 index 0000000000..bbcab83460 --- /dev/null +++ b/deps/curl-7.51.0/tests/server/server_sockaddr.h @@ -0,0 +1,41 @@ +#ifndef HEADER_CURL_SERVER_SOCKADDR_H +#define HEADER_CURL_SERVER_SOCKADDR_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "server_setup.h" + +#ifdef HAVE_SYS_UN_H +#include /* for sockaddr_un */ +#endif + +typedef union { + struct sockaddr sa; + struct sockaddr_in sa4; +#ifdef ENABLE_IPV6 + struct sockaddr_in6 sa6; +#endif +#ifdef USE_UNIX_SOCKETS + struct sockaddr_un sau; +#endif +} srvr_sockaddr_union_t; + +#endif /* HEADER_CURL_SERVER_SOCKADDR_H */ diff --git a/deps/curl-7.51.0/tests/server/sockfilt.c b/deps/curl-7.51.0/tests/server/sockfilt.c new file mode 100644 index 0000000000..38aa51e677 --- /dev/null +++ b/deps/curl-7.51.0/tests/server/sockfilt.c @@ -0,0 +1,1564 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "server_setup.h" + +/* Purpose + * + * 1. Accept a TCP connection on a custom port (IPv4 or IPv6), or connect + * to a given (localhost) port. + * + * 2. Get commands on STDIN. Pass data on to the TCP stream. + * Get data from TCP stream and pass on to STDOUT. + * + * This program is made to perform all the socket/stream/connection stuff for + * the test suite's (perl) FTP server. Previously the perl code did all of + * this by its own, but I decided to let this program do the socket layer + * because of several things: + * + * o We want the perl code to work with rather old perl installations, thus + * we cannot use recent perl modules or features. + * + * o We want IPv6 support for systems that provide it, and doing optional IPv6 + * support in perl seems if not impossible so at least awkward. + * + * o We want FTP-SSL support, which means that a connection that starts with + * plain sockets needs to be able to "go SSL" in the midst. This would also + * require some nasty perl stuff I'd rather avoid. + * + * (Source originally based on sws.c) + */ + +/* + * Signal handling notes for sockfilt + * ---------------------------------- + * + * This program is a single-threaded process. + * + * This program is intended to be highly portable and as such it must be kept + * as simple as possible, due to this the only signal handling mechanisms used + * will be those of ANSI C, and used only in the most basic form which is good + * enough for the purpose of this program. + * + * For the above reason and the specific needs of this program signals SIGHUP, + * SIGPIPE and SIGALRM will be simply ignored on systems where this can be + * done. If possible, signals SIGINT and SIGTERM will be handled by this + * program as an indication to cleanup and finish execution as soon as + * possible. This will be achieved with a single signal handler + * 'exit_signal_handler' for both signals. + * + * The 'exit_signal_handler' upon the first SIGINT or SIGTERM received signal + * will just set to one the global var 'got_exit_signal' storing in global var + * 'exit_signal' the signal that triggered this change. + * + * Nothing fancy that could introduce problems is used, the program at certain + * points in its normal flow checks if var 'got_exit_signal' is set and in + * case this is true it just makes its way out of loops and functions in + * structured and well behaved manner to achieve proper program cleanup and + * termination. + * + * Even with the above mechanism implemented it is worthwile to note that + * other signals might still be received, or that there might be systems on + * which it is not possible to trap and ignore some of the above signals. + * This implies that for increased portability and reliability the program + * must be coded as if no signal was being ignored or handled at all. Enjoy + * it! + */ + +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif + +#define ENABLE_CURLX_PRINTF +/* make the curlx header define all printf() functions to use the curlx_* + versions instead */ +#include "curlx.h" /* from the private lib dir */ +#include "getpart.h" +#include "inet_pton.h" +#include "util.h" +#include "server_sockaddr.h" +#include "warnless.h" + +/* include memdebug.h last */ +#include "memdebug.h" + +#ifdef USE_WINSOCK +#undef EINTR +#define EINTR 4 /* errno.h value */ +#undef EAGAIN +#define EAGAIN 11 /* errno.h value */ +#undef ENOMEM +#define ENOMEM 12 /* errno.h value */ +#undef EINVAL +#define EINVAL 22 /* errno.h value */ +#endif + +#define DEFAULT_PORT 8999 + +#ifndef DEFAULT_LOGFILE +#define DEFAULT_LOGFILE "log/sockfilt.log" +#endif + +const char *serverlogfile = DEFAULT_LOGFILE; + +static bool verbose = FALSE; +static bool bind_only = FALSE; +#ifdef ENABLE_IPV6 +static bool use_ipv6 = FALSE; +#endif +static const char *ipv_inuse = "IPv4"; +static unsigned short port = DEFAULT_PORT; +static unsigned short connectport = 0; /* if non-zero, we activate this mode */ + +enum sockmode { + PASSIVE_LISTEN, /* as a server waiting for connections */ + PASSIVE_CONNECT, /* as a server, connected to a client */ + ACTIVE, /* as a client, connected to a server */ + ACTIVE_DISCONNECT /* as a client, disconnected from server */ +}; + +/* do-nothing macro replacement for systems which lack siginterrupt() */ + +#ifndef HAVE_SIGINTERRUPT +#define siginterrupt(x,y) do {} while(0) +#endif + +/* vars used to keep around previous signal handlers */ + +typedef RETSIGTYPE (*SIGHANDLER_T)(int); + +#ifdef SIGHUP +static SIGHANDLER_T old_sighup_handler = SIG_ERR; +#endif + +#ifdef SIGPIPE +static SIGHANDLER_T old_sigpipe_handler = SIG_ERR; +#endif + +#ifdef SIGALRM +static SIGHANDLER_T old_sigalrm_handler = SIG_ERR; +#endif + +#ifdef SIGINT +static SIGHANDLER_T old_sigint_handler = SIG_ERR; +#endif + +#ifdef SIGTERM +static SIGHANDLER_T old_sigterm_handler = SIG_ERR; +#endif + +#if defined(SIGBREAK) && defined(WIN32) +static SIGHANDLER_T old_sigbreak_handler = SIG_ERR; +#endif + +/* var which if set indicates that the program should finish execution */ + +SIG_ATOMIC_T got_exit_signal = 0; + +/* if next is set indicates the first signal handled in exit_signal_handler */ + +static volatile int exit_signal = 0; + +/* signal handler that will be triggered to indicate that the program + should finish its execution in a controlled manner as soon as possible. + The first time this is called it will set got_exit_signal to one and + store in exit_signal the signal that triggered its execution. */ + +static RETSIGTYPE exit_signal_handler(int signum) +{ + int old_errno = errno; + if(got_exit_signal == 0) { + got_exit_signal = 1; + exit_signal = signum; + } + (void)signal(signum, exit_signal_handler); + errno = old_errno; +} + +static void install_signal_handlers(void) +{ +#ifdef SIGHUP + /* ignore SIGHUP signal */ + if((old_sighup_handler = signal(SIGHUP, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGHUP handler: %s", strerror(errno)); +#endif +#ifdef SIGPIPE + /* ignore SIGPIPE signal */ + if((old_sigpipe_handler = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGPIPE handler: %s", strerror(errno)); +#endif +#ifdef SIGALRM + /* ignore SIGALRM signal */ + if((old_sigalrm_handler = signal(SIGALRM, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGALRM handler: %s", strerror(errno)); +#endif +#ifdef SIGINT + /* handle SIGINT signal with our exit_signal_handler */ + if((old_sigint_handler = signal(SIGINT, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGINT handler: %s", strerror(errno)); + else + siginterrupt(SIGINT, 1); +#endif +#ifdef SIGTERM + /* handle SIGTERM signal with our exit_signal_handler */ + if((old_sigterm_handler = signal(SIGTERM, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGTERM handler: %s", strerror(errno)); + else + siginterrupt(SIGTERM, 1); +#endif +#if defined(SIGBREAK) && defined(WIN32) + /* handle SIGBREAK signal with our exit_signal_handler */ + if((old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGBREAK handler: %s", strerror(errno)); + else + siginterrupt(SIGBREAK, 1); +#endif +} + +static void restore_signal_handlers(void) +{ +#ifdef SIGHUP + if(SIG_ERR != old_sighup_handler) + (void)signal(SIGHUP, old_sighup_handler); +#endif +#ifdef SIGPIPE + if(SIG_ERR != old_sigpipe_handler) + (void)signal(SIGPIPE, old_sigpipe_handler); +#endif +#ifdef SIGALRM + if(SIG_ERR != old_sigalrm_handler) + (void)signal(SIGALRM, old_sigalrm_handler); +#endif +#ifdef SIGINT + if(SIG_ERR != old_sigint_handler) + (void)signal(SIGINT, old_sigint_handler); +#endif +#ifdef SIGTERM + if(SIG_ERR != old_sigterm_handler) + (void)signal(SIGTERM, old_sigterm_handler); +#endif +#if defined(SIGBREAK) && defined(WIN32) + if(SIG_ERR != old_sigbreak_handler) + (void)signal(SIGBREAK, old_sigbreak_handler); +#endif +} + +#ifdef WIN32 +/* + * read-wrapper to support reading from stdin on Windows. + */ +static ssize_t read_wincon(int fd, void *buf, size_t count) +{ + HANDLE handle = NULL; + DWORD mode, rcount = 0; + BOOL success; + + if(fd == fileno(stdin)) { + handle = GetStdHandle(STD_INPUT_HANDLE); + } + else { + return read(fd, buf, count); + } + + if(GetConsoleMode(handle, &mode)) { + success = ReadConsole(handle, buf, curlx_uztoul(count), &rcount, NULL); + } + else { + success = ReadFile(handle, buf, curlx_uztoul(count), &rcount, NULL); + } + if(success) { + return rcount; + } + + errno = GetLastError(); + return -1; +} +#undef read +#define read(a,b,c) read_wincon(a,b,c) + +/* + * write-wrapper to support writing to stdout and stderr on Windows. + */ +static ssize_t write_wincon(int fd, const void *buf, size_t count) +{ + HANDLE handle = NULL; + DWORD mode, wcount = 0; + BOOL success; + + if(fd == fileno(stdout)) { + handle = GetStdHandle(STD_OUTPUT_HANDLE); + } + else if(fd == fileno(stderr)) { + handle = GetStdHandle(STD_ERROR_HANDLE); + } + else { + return write(fd, buf, count); + } + + if(GetConsoleMode(handle, &mode)) { + success = WriteConsole(handle, buf, curlx_uztoul(count), &wcount, NULL); + } + else { + success = WriteFile(handle, buf, curlx_uztoul(count), &wcount, NULL); + } + if(success) { + return wcount; + } + + errno = GetLastError(); + return -1; +} +#undef write +#define write(a,b,c) write_wincon(a,b,c) +#endif + +/* + * fullread is a wrapper around the read() function. This will repeat the call + * to read() until it actually has read the complete number of bytes indicated + * in nbytes or it fails with a condition that cannot be handled with a simple + * retry of the read call. + */ + +static ssize_t fullread(int filedes, void *buffer, size_t nbytes) +{ + int error; + ssize_t rc; + ssize_t nread = 0; + + do { + rc = read(filedes, (unsigned char *)buffer + nread, nbytes - nread); + + if(got_exit_signal) { + logmsg("signalled to die"); + return -1; + } + + if(rc < 0) { + error = errno; + if((error == EINTR) || (error == EAGAIN)) + continue; + logmsg("reading from file descriptor: %d,", filedes); + logmsg("unrecoverable read() failure: (%d) %s", + error, strerror(error)); + return -1; + } + + if(rc == 0) { + logmsg("got 0 reading from stdin"); + return 0; + } + + nread += rc; + + } while((size_t)nread < nbytes); + + if(verbose) + logmsg("read %zd bytes", nread); + + return nread; +} + +/* + * fullwrite is a wrapper around the write() function. This will repeat the + * call to write() until it actually has written the complete number of bytes + * indicated in nbytes or it fails with a condition that cannot be handled + * with a simple retry of the write call. + */ + +static ssize_t fullwrite(int filedes, const void *buffer, size_t nbytes) +{ + int error; + ssize_t wc; + ssize_t nwrite = 0; + + do { + wc = write(filedes, (unsigned char *)buffer + nwrite, nbytes - nwrite); + + if(got_exit_signal) { + logmsg("signalled to die"); + return -1; + } + + if(wc < 0) { + error = errno; + if((error == EINTR) || (error == EAGAIN)) + continue; + logmsg("writing to file descriptor: %d,", filedes); + logmsg("unrecoverable write() failure: (%d) %s", + error, strerror(error)); + return -1; + } + + if(wc == 0) { + logmsg("put 0 writing to stdout"); + return 0; + } + + nwrite += wc; + + } while((size_t)nwrite < nbytes); + + if(verbose) + logmsg("wrote %zd bytes", nwrite); + + return nwrite; +} + +/* + * read_stdin tries to read from stdin nbytes into the given buffer. This is a + * blocking function that will only return TRUE when nbytes have actually been + * read or FALSE when an unrecoverable error has been detected. Failure of this + * function is an indication that the sockfilt process should terminate. + */ + +static bool read_stdin(void *buffer, size_t nbytes) +{ + ssize_t nread = fullread(fileno(stdin), buffer, nbytes); + if(nread != (ssize_t)nbytes) { + logmsg("exiting..."); + return FALSE; + } + return TRUE; +} + +/* + * write_stdout tries to write to stdio nbytes from the given buffer. This is a + * blocking function that will only return TRUE when nbytes have actually been + * written or FALSE when an unrecoverable error has been detected. Failure of + * this function is an indication that the sockfilt process should terminate. + */ + +static bool write_stdout(const void *buffer, size_t nbytes) +{ + ssize_t nwrite = fullwrite(fileno(stdout), buffer, nbytes); + if(nwrite != (ssize_t)nbytes) { + logmsg("exiting..."); + return FALSE; + } + return TRUE; +} + +static void lograw(unsigned char *buffer, ssize_t len) +{ + char data[120]; + ssize_t i; + unsigned char *ptr = buffer; + char *optr = data; + ssize_t width=0; + int left = sizeof(data); + + for(i=0; i60) { + logmsg("'%s'", data); + width = 0; + optr = data; + left = sizeof(data); + } + } + if(width) + logmsg("'%s'", data); +} + +#ifdef USE_WINSOCK +/* + * WinSock select() does not support standard file descriptors, + * it can only check SOCKETs. The following function is an attempt + * to re-create a select() function with support for other handle types. + * + * select() function with support for WINSOCK2 sockets and all + * other handle types supported by WaitForMultipleObjectsEx() as + * well as disk files, anonymous and names pipes, and character input. + * + * https://msdn.microsoft.com/en-us/library/windows/desktop/ms687028.aspx + * https://msdn.microsoft.com/en-us/library/windows/desktop/ms741572.aspx + */ +struct select_ws_wait_data { + HANDLE handle; /* actual handle to wait for during select */ + HANDLE event; /* internal event to abort waiting thread */ +}; +static DWORD WINAPI select_ws_wait_thread(LPVOID lpParameter) +{ + struct select_ws_wait_data *data; + HANDLE handle, handles[2]; + INPUT_RECORD inputrecord; + LARGE_INTEGER size, pos; + DWORD type, length; + + /* retrieve handles from internal structure */ + data = (struct select_ws_wait_data *) lpParameter; + if(data) { + handle = data->handle; + handles[0] = data->event; + handles[1] = handle; + free(data); + } + else + return -1; + + /* retrieve the type of file to wait on */ + type = GetFileType(handle); + switch(type) { + case FILE_TYPE_DISK: + /* The handle represents a file on disk, this means: + * - WaitForMultipleObjectsEx will always be signalled for it. + * - comparison of current position in file and total size of + * the file can be used to check if we reached the end yet. + * + * Approach: Loop till either the internal event is signalled + * or if the end of the file has already been reached. + */ + while(WaitForMultipleObjectsEx(1, handles, FALSE, 0, FALSE) + == WAIT_TIMEOUT) { + /* get total size of file */ + length = 0; + size.QuadPart = 0; + size.LowPart = GetFileSize(handle, &length); + if((size.LowPart != INVALID_FILE_SIZE) || + (GetLastError() == NO_ERROR)) { + size.HighPart = length; + /* get the current position within the file */ + pos.QuadPart = 0; + pos.LowPart = SetFilePointer(handle, 0, &pos.HighPart, + FILE_CURRENT); + if((pos.LowPart != INVALID_SET_FILE_POINTER) || + (GetLastError() == NO_ERROR)) { + /* compare position with size, abort if not equal */ + if(size.QuadPart == pos.QuadPart) { + /* sleep and continue waiting */ + SleepEx(0, FALSE); + continue; + } + } + } + /* there is some data available, stop waiting */ + break; + } + break; + + case FILE_TYPE_CHAR: + /* The handle represents a character input, this means: + * - WaitForMultipleObjectsEx will be signalled on any kind of input, + * including mouse and window size events we do not care about. + * + * Approach: Loop till either the internal event is signalled + * or we get signalled for an actual key-event. + */ + while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE) + == WAIT_OBJECT_0 + 1) { + /* check if this is an actual console handle */ + length = 0; + if(GetConsoleMode(handle, &length)) { + /* retrieve an event from the console buffer */ + length = 0; + if(PeekConsoleInput(handle, &inputrecord, 1, &length)) { + /* check if the event is not an actual key-event */ + if(length == 1 && inputrecord.EventType != KEY_EVENT) { + /* purge the non-key-event and continue waiting */ + ReadConsoleInput(handle, &inputrecord, 1, &length); + continue; + } + } + } + /* there is some data available, stop waiting */ + break; + } + break; + + case FILE_TYPE_PIPE: + /* The handle represents an anonymous or named pipe, this means: + * - WaitForMultipleObjectsEx will always be signalled for it. + * - peek into the pipe and retrieve the amount of data available. + * + * Approach: Loop till either the internal event is signalled + * or there is data in the pipe available for reading. + */ + while(WaitForMultipleObjectsEx(1, handles, FALSE, 0, FALSE) + == WAIT_TIMEOUT) { + /* peek into the pipe and retrieve the amount of data available */ + length = 0; + if(PeekNamedPipe(handle, NULL, 0, NULL, &length, NULL)) { + /* if there is no data available, sleep and continue waiting */ + if(length == 0) { + SleepEx(0, FALSE); + continue; + } + } + else { + /* if the pipe has been closed, sleep and continue waiting */ + if(GetLastError() == ERROR_BROKEN_PIPE) { + SleepEx(0, FALSE); + continue; + } + } + /* there is some data available, stop waiting */ + break; + } + break; + + default: + /* The handle has an unknown type, try to wait on it */ + WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE); + break; + } + + return 0; +} +static HANDLE select_ws_wait(HANDLE handle, HANDLE event) +{ + struct select_ws_wait_data *data; + HANDLE thread = NULL; + + /* allocate internal waiting data structure */ + data = malloc(sizeof(struct select_ws_wait_data)); + if(data) { + data->handle = handle; + data->event = event; + + /* launch waiting thread */ + thread = CreateThread(NULL, 0, + &select_ws_wait_thread, + data, 0, NULL); + + /* free data if thread failed to launch */ + if(!thread) { + free(data); + } + } + + return thread; +} +struct select_ws_data { + curl_socket_t fd; /* the original input handle (indexed by fds) */ + curl_socket_t wsasock; /* the internal socket handle (indexed by wsa) */ + WSAEVENT wsaevent; /* the internal WINSOCK2 event (indexed by wsa) */ + HANDLE thread; /* the internal threads handle (indexed by thd) */ +}; +static int select_ws(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout) +{ + DWORD milliseconds, wait, idx; + WSANETWORKEVENTS wsanetevents; + struct select_ws_data *data; + HANDLE handle, *handles; + curl_socket_t sock; + long networkevents; + WSAEVENT wsaevent; + int error, fds; + HANDLE waitevent = NULL; + DWORD nfd = 0, thd = 0, wsa = 0; + int ret = 0; + + /* check if the input value is valid */ + if(nfds < 0) { + errno = EINVAL; + return -1; + } + + /* check if we got descriptors, sleep in case we got none */ + if(!nfds) { + Sleep((timeout->tv_sec*1000)+(DWORD)(((double)timeout->tv_usec)/1000.0)); + return 0; + } + + /* create internal event to signal waiting threads */ + waitevent = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!waitevent) { + errno = ENOMEM; + return -1; + } + + /* allocate internal array for the internal data */ + data = malloc(nfds * sizeof(struct select_ws_data)); + if(data == NULL) { + errno = ENOMEM; + return -1; + } + + /* allocate internal array for the internal event handles */ + handles = malloc(nfds * sizeof(HANDLE)); + if(handles == NULL) { + free(data); + errno = ENOMEM; + return -1; + } + + /* clear internal arrays */ + memset(data, 0, nfds * sizeof(struct select_ws_data)); + memset(handles, 0, nfds * sizeof(HANDLE)); + + /* loop over the handles in the input descriptor sets */ + for(fds = 0; fds < nfds; fds++) { + networkevents = 0; + handles[nfd] = 0; + + if(FD_ISSET(fds, readfds)) + networkevents |= FD_READ|FD_ACCEPT|FD_CLOSE; + + if(FD_ISSET(fds, writefds)) + networkevents |= FD_WRITE|FD_CONNECT; + + if(FD_ISSET(fds, exceptfds)) + networkevents |= FD_OOB|FD_CLOSE; + + /* only wait for events for which we actually care */ + if(networkevents) { + data[nfd].fd = curlx_sitosk(fds); + if(fds == fileno(stdin)) { + handle = GetStdHandle(STD_INPUT_HANDLE); + handle = select_ws_wait(handle, waitevent); + handles[nfd] = handle; + data[thd].thread = handle; + thd++; + } + else if(fds == fileno(stdout)) { + handles[nfd] = GetStdHandle(STD_OUTPUT_HANDLE); + } + else if(fds == fileno(stderr)) { + handles[nfd] = GetStdHandle(STD_ERROR_HANDLE); + } + else { + wsaevent = WSACreateEvent(); + if(wsaevent != WSA_INVALID_EVENT) { + error = WSAEventSelect(fds, wsaevent, networkevents); + if(error != SOCKET_ERROR) { + handle = (HANDLE) wsaevent; + handles[nfd] = handle; + data[wsa].wsasock = curlx_sitosk(fds); + data[wsa].wsaevent = wsaevent; + wsa++; + } + else { + WSACloseEvent(wsaevent); + handle = (HANDLE) curlx_sitosk(fds); + handle = select_ws_wait(handle, waitevent); + handles[nfd] = handle; + data[thd].thread = handle; + thd++; + } + } + } + nfd++; + } + } + + /* convert struct timeval to milliseconds */ + if(timeout) { + milliseconds = ((timeout->tv_sec * 1000) + (timeout->tv_usec / 1000)); + } + else { + milliseconds = INFINITE; + } + + /* wait for one of the internal handles to trigger */ + wait = WaitForMultipleObjectsEx(nfd, handles, FALSE, milliseconds, FALSE); + + /* signal the event handle for the waiting threads */ + SetEvent(waitevent); + + /* loop over the internal handles returned in the descriptors */ + for(idx = 0; idx < nfd; idx++) { + handle = handles[idx]; + sock = data[idx].fd; + fds = curlx_sktosi(sock); + + /* check if the current internal handle was triggered */ + if(wait != WAIT_FAILED && (wait - WAIT_OBJECT_0) <= idx && + WaitForSingleObjectEx(handle, 0, FALSE) == WAIT_OBJECT_0) { + /* first handle stdin, stdout and stderr */ + if(fds == fileno(stdin)) { + /* stdin is never ready for write or exceptional */ + FD_CLR(sock, writefds); + FD_CLR(sock, exceptfds); + } + else if(fds == fileno(stdout) || fds == fileno(stderr)) { + /* stdout and stderr are never ready for read or exceptional */ + FD_CLR(sock, readfds); + FD_CLR(sock, exceptfds); + } + else { + /* try to handle the event with the WINSOCK2 functions */ + wsanetevents.lNetworkEvents = 0; + error = WSAEnumNetworkEvents(fds, handle, &wsanetevents); + if(error != SOCKET_ERROR) { + /* remove from descriptor set if not ready for read/accept/close */ + if(!(wsanetevents.lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE))) + FD_CLR(sock, readfds); + + /* remove from descriptor set if not ready for write/connect */ + if(!(wsanetevents.lNetworkEvents & (FD_WRITE|FD_CONNECT))) + FD_CLR(sock, writefds); + + /* HACK: + * use exceptfds together with readfds to signal + * that the connection was closed by the client. + * + * Reason: FD_CLOSE is only signaled once, sometimes + * at the same time as FD_READ with data being available. + * This means that recv/sread is not reliable to detect + * that the connection is closed. + */ + /* remove from descriptor set if not exceptional */ + if(!(wsanetevents.lNetworkEvents & (FD_OOB|FD_CLOSE))) + FD_CLR(sock, exceptfds); + } + } + + /* check if the event has not been filtered using specific tests */ + if(FD_ISSET(sock, readfds) || FD_ISSET(sock, writefds) || + FD_ISSET(sock, exceptfds)) { + ret++; + } + } + else { + /* remove from all descriptor sets since this handle did not trigger */ + FD_CLR(sock, readfds); + FD_CLR(sock, writefds); + FD_CLR(sock, exceptfds); + } + } + + for(fds = 0; fds < nfds; fds++) { + if(FD_ISSET(fds, readfds)) + logmsg("select_ws: %d is readable", fds); + + if(FD_ISSET(fds, writefds)) + logmsg("select_ws: %d is writable", fds); + + if(FD_ISSET(fds, exceptfds)) + logmsg("select_ws: %d is excepted", fds); + } + + for(idx = 0; idx < wsa; idx++) { + WSAEventSelect(data[idx].wsasock, NULL, 0); + WSACloseEvent(data[idx].wsaevent); + } + + for(idx = 0; idx < thd; idx++) { + WaitForSingleObject(data[idx].thread, INFINITE); + CloseHandle(data[idx].thread); + } + + CloseHandle(waitevent); + + free(handles); + free(data); + + return ret; +} +#define select(a,b,c,d,e) select_ws(a,b,c,d,e) +#endif /* USE_WINSOCK */ + +/* + sockfdp is a pointer to an established stream or CURL_SOCKET_BAD + + if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must + accept() +*/ +static bool juggle(curl_socket_t *sockfdp, + curl_socket_t listenfd, + enum sockmode *mode) +{ + struct timeval timeout; + fd_set fds_read; + fd_set fds_write; + fd_set fds_err; + curl_socket_t sockfd = CURL_SOCKET_BAD; + int maxfd = -99; + ssize_t rc; + ssize_t nread_socket; + ssize_t bytes_written; + ssize_t buffer_len; + int error = 0; + + /* 'buffer' is this excessively large only to be able to support things like + test 1003 which tests exceedingly large server response lines */ + unsigned char buffer[17010]; + char data[16]; + + if(got_exit_signal) { + logmsg("signalled to die, exiting..."); + return FALSE; + } + +#ifdef HAVE_GETPPID + /* As a last resort, quit if sockfilt process becomes orphan. Just in case + parent ftpserver process has died without killing its sockfilt children */ + if(getppid() <= 1) { + logmsg("process becomes orphan, exiting"); + return FALSE; + } +#endif + + timeout.tv_sec = 120; + timeout.tv_usec = 0; + + FD_ZERO(&fds_read); + FD_ZERO(&fds_write); + FD_ZERO(&fds_err); + + FD_SET((curl_socket_t)fileno(stdin), &fds_read); + + switch(*mode) { + + case PASSIVE_LISTEN: + + /* server mode */ + sockfd = listenfd; + /* there's always a socket to wait for */ + FD_SET(sockfd, &fds_read); + maxfd = (int)sockfd; + break; + + case PASSIVE_CONNECT: + + sockfd = *sockfdp; + if(CURL_SOCKET_BAD == sockfd) { + /* eeek, we are supposedly connected and then this cannot be -1 ! */ + logmsg("socket is -1! on %s:%d", __FILE__, __LINE__); + maxfd = 0; /* stdin */ + } + else { + /* there's always a socket to wait for */ + FD_SET(sockfd, &fds_read); +#ifdef USE_WINSOCK + FD_SET(sockfd, &fds_err); +#endif + maxfd = (int)sockfd; + } + break; + + case ACTIVE: + + sockfd = *sockfdp; + /* sockfd turns CURL_SOCKET_BAD when our connection has been closed */ + if(CURL_SOCKET_BAD != sockfd) { + FD_SET(sockfd, &fds_read); +#ifdef USE_WINSOCK + FD_SET(sockfd, &fds_err); +#endif + maxfd = (int)sockfd; + } + else { + logmsg("No socket to read on"); + maxfd = 0; + } + break; + + case ACTIVE_DISCONNECT: + + logmsg("disconnected, no socket to read on"); + maxfd = 0; + sockfd = CURL_SOCKET_BAD; + break; + + } /* switch(*mode) */ + + + do { + + /* select() blocking behavior call on blocking descriptors please */ + + rc = select(maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout); + + if(got_exit_signal) { + logmsg("signalled to die, exiting..."); + return FALSE; + } + + } while((rc == -1) && ((error = errno) == EINTR)); + + if(rc < 0) { + logmsg("select() failed with error: (%d) %s", + error, strerror(error)); + return FALSE; + } + + if(rc == 0) + /* timeout */ + return TRUE; + + + if(FD_ISSET(fileno(stdin), &fds_read)) { + /* read from stdin, commands/data to be dealt with and possibly passed on + to the socket + + protocol: + + 4 letter command + LF [mandatory] + + 4-digit hexadecimal data length + LF [if the command takes data] + data [the data being as long as set above] + + Commands: + + DATA - plain pass-thru data + */ + + if(!read_stdin(buffer, 5)) + return FALSE; + + logmsg("Received %c%c%c%c (on stdin)", + buffer[0], buffer[1], buffer[2], buffer[3]); + + if(!memcmp("PING", buffer, 4)) { + /* send reply on stdout, just proving we are alive */ + if(!write_stdout("PONG\n", 5)) + return FALSE; + } + + else if(!memcmp("PORT", buffer, 4)) { + /* Question asking us what PORT number we are listening to. + Replies to PORT with "IPv[num]/[port]" */ + snprintf((char *)buffer, sizeof(buffer), "%s/%hu\n", ipv_inuse, port); + buffer_len = (ssize_t)strlen((char *)buffer); + snprintf(data, sizeof(data), "PORT\n%04zx\n", buffer_len); + if(!write_stdout(data, 10)) + return FALSE; + if(!write_stdout(buffer, buffer_len)) + return FALSE; + } + else if(!memcmp("QUIT", buffer, 4)) { + /* just die */ + logmsg("quits"); + return FALSE; + } + else if(!memcmp("DATA", buffer, 4)) { + /* data IN => data OUT */ + + if(!read_stdin(buffer, 5)) + return FALSE; + + buffer[5] = '\0'; + + buffer_len = (ssize_t)strtol((char *)buffer, NULL, 16); + if(buffer_len > (ssize_t)sizeof(buffer)) { + logmsg("ERROR: Buffer size (%zu bytes) too small for data size " + "(%zd bytes)", sizeof(buffer), buffer_len); + return FALSE; + } + logmsg("> %zd bytes data, server => client", buffer_len); + + if(!read_stdin(buffer, buffer_len)) + return FALSE; + + lograw(buffer, buffer_len); + + if(*mode == PASSIVE_LISTEN) { + logmsg("*** We are disconnected!"); + if(!write_stdout("DISC\n", 5)) + return FALSE; + } + else { + /* send away on the socket */ + bytes_written = swrite(sockfd, buffer, buffer_len); + if(bytes_written != buffer_len) { + logmsg("Not all data was sent. Bytes to send: %zd sent: %zd", + buffer_len, bytes_written); + } + } + } + else if(!memcmp("DISC", buffer, 4)) { + /* disconnect! */ + if(!write_stdout("DISC\n", 5)) + return FALSE; + if(sockfd != CURL_SOCKET_BAD) { + logmsg("====> Client forcibly disconnected"); + sclose(sockfd); + *sockfdp = CURL_SOCKET_BAD; + if(*mode == PASSIVE_CONNECT) + *mode = PASSIVE_LISTEN; + else + *mode = ACTIVE_DISCONNECT; + } + else + logmsg("attempt to close already dead connection"); + return TRUE; + } + } + + + if((sockfd != CURL_SOCKET_BAD) && (FD_ISSET(sockfd, &fds_read)) ) { + + curl_socket_t newfd = CURL_SOCKET_BAD; /* newly accepted socket */ + + if(*mode == PASSIVE_LISTEN) { + /* there's no stream set up yet, this is an indication that there's a + client connecting. */ + newfd = accept(sockfd, NULL, NULL); + if(CURL_SOCKET_BAD == newfd) { + error = SOCKERRNO; + logmsg("accept(%d, NULL, NULL) failed with error: (%d) %s", + sockfd, error, strerror(error)); + } + else { + logmsg("====> Client connect"); + if(!write_stdout("CNCT\n", 5)) + return FALSE; + *sockfdp = newfd; /* store the new socket */ + *mode = PASSIVE_CONNECT; /* we have connected */ + } + return TRUE; + } + + /* read from socket, pass on data to stdout */ + nread_socket = sread(sockfd, buffer, sizeof(buffer)); + + if(nread_socket > 0) { + snprintf(data, sizeof(data), "DATA\n%04zx\n", nread_socket); + if(!write_stdout(data, 10)) + return FALSE; + if(!write_stdout(buffer, nread_socket)) + return FALSE; + + logmsg("< %zd bytes data, client => server", nread_socket); + lograw(buffer, nread_socket); + } + + if(nread_socket <= 0 +#ifdef USE_WINSOCK + || FD_ISSET(sockfd, &fds_err) +#endif + ) { + logmsg("====> Client disconnect"); + if(!write_stdout("DISC\n", 5)) + return FALSE; + sclose(sockfd); + *sockfdp = CURL_SOCKET_BAD; + if(*mode == PASSIVE_CONNECT) + *mode = PASSIVE_LISTEN; + else + *mode = ACTIVE_DISCONNECT; + return TRUE; + } + } + + return TRUE; +} + +static curl_socket_t sockdaemon(curl_socket_t sock, + unsigned short *listenport) +{ + /* passive daemon style */ + srvr_sockaddr_union_t listener; + int flag; + int rc; + int totdelay = 0; + int maxretr = 10; + int delay= 20; + int attempt = 0; + int error = 0; + + do { + attempt++; + flag = 1; + rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (void *)&flag, sizeof(flag)); + if(rc) { + error = SOCKERRNO; + logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s", + error, strerror(error)); + if(maxretr) { + rc = wait_ms(delay); + if(rc) { + /* should not happen */ + error = errno; + logmsg("wait_ms() failed with error: (%d) %s", + error, strerror(error)); + sclose(sock); + return CURL_SOCKET_BAD; + } + if(got_exit_signal) { + logmsg("signalled to die, exiting..."); + sclose(sock); + return CURL_SOCKET_BAD; + } + totdelay += delay; + delay *= 2; /* double the sleep for next attempt */ + } + } + } while(rc && maxretr--); + + if(rc) { + logmsg("setsockopt(SO_REUSEADDR) failed %d times in %d ms. Error: (%d) %s", + attempt, totdelay, error, strerror(error)); + logmsg("Continuing anyway..."); + } + + /* When the specified listener port is zero, it is actually a + request to let the system choose a non-zero available port. */ + +#ifdef ENABLE_IPV6 + if(!use_ipv6) { +#endif + memset(&listener.sa4, 0, sizeof(listener.sa4)); + listener.sa4.sin_family = AF_INET; + listener.sa4.sin_addr.s_addr = INADDR_ANY; + listener.sa4.sin_port = htons(*listenport); + rc = bind(sock, &listener.sa, sizeof(listener.sa4)); +#ifdef ENABLE_IPV6 + } + else { + memset(&listener.sa6, 0, sizeof(listener.sa6)); + listener.sa6.sin6_family = AF_INET6; + listener.sa6.sin6_addr = in6addr_any; + listener.sa6.sin6_port = htons(*listenport); + rc = bind(sock, &listener.sa, sizeof(listener.sa6)); + } +#endif /* ENABLE_IPV6 */ + if(rc) { + error = SOCKERRNO; + logmsg("Error binding socket on port %hu: (%d) %s", + *listenport, error, strerror(error)); + sclose(sock); + return CURL_SOCKET_BAD; + } + + if(!*listenport) { + /* The system was supposed to choose a port number, figure out which + port we actually got and update the listener port value with it. */ + curl_socklen_t la_size; + srvr_sockaddr_union_t localaddr; +#ifdef ENABLE_IPV6 + if(!use_ipv6) +#endif + la_size = sizeof(localaddr.sa4); +#ifdef ENABLE_IPV6 + else + la_size = sizeof(localaddr.sa6); +#endif + memset(&localaddr.sa, 0, (size_t)la_size); + if(getsockname(sock, &localaddr.sa, &la_size) < 0) { + error = SOCKERRNO; + logmsg("getsockname() failed with error: (%d) %s", + error, strerror(error)); + sclose(sock); + return CURL_SOCKET_BAD; + } + switch (localaddr.sa.sa_family) { + case AF_INET: + *listenport = ntohs(localaddr.sa4.sin_port); + break; +#ifdef ENABLE_IPV6 + case AF_INET6: + *listenport = ntohs(localaddr.sa6.sin6_port); + break; +#endif + default: + break; + } + if(!*listenport) { + /* Real failure, listener port shall not be zero beyond this point. */ + logmsg("Apparently getsockname() succeeded, with listener port zero."); + logmsg("A valid reason for this failure is a binary built without"); + logmsg("proper network library linkage. This might not be the only"); + logmsg("reason, but double check it before anything else."); + sclose(sock); + return CURL_SOCKET_BAD; + } + } + + /* bindonly option forces no listening */ + if(bind_only) { + logmsg("instructed to bind port without listening"); + return sock; + } + + /* start accepting connections */ + rc = listen(sock, 5); + if(0 != rc) { + error = SOCKERRNO; + logmsg("listen(%d, 5) failed with error: (%d) %s", + sock, error, strerror(error)); + sclose(sock); + return CURL_SOCKET_BAD; + } + + return sock; +} + + +int main(int argc, char *argv[]) +{ + srvr_sockaddr_union_t me; + curl_socket_t sock = CURL_SOCKET_BAD; + curl_socket_t msgsock = CURL_SOCKET_BAD; + int wrotepidfile = 0; + char *pidname= (char *)".sockfilt.pid"; + bool juggle_again; + int rc; + int error; + int arg=1; + enum sockmode mode = PASSIVE_LISTEN; /* default */ + const char *addr = NULL; + + while(argc>arg) { + if(!strcmp("--version", argv[arg])) { + printf("sockfilt IPv4%s\n", +#ifdef ENABLE_IPV6 + "/IPv6" +#else + "" +#endif + ); + return 0; + } + else if(!strcmp("--verbose", argv[arg])) { + verbose = TRUE; + arg++; + } + else if(!strcmp("--pidfile", argv[arg])) { + arg++; + if(argc>arg) + pidname = argv[arg++]; + } + else if(!strcmp("--logfile", argv[arg])) { + arg++; + if(argc>arg) + serverlogfile = argv[arg++]; + } + else if(!strcmp("--ipv6", argv[arg])) { +#ifdef ENABLE_IPV6 + ipv_inuse = "IPv6"; + use_ipv6 = TRUE; +#endif + arg++; + } + else if(!strcmp("--ipv4", argv[arg])) { + /* for completeness, we support this option as well */ +#ifdef ENABLE_IPV6 + ipv_inuse = "IPv4"; + use_ipv6 = FALSE; +#endif + arg++; + } + else if(!strcmp("--bindonly", argv[arg])) { + bind_only = TRUE; + arg++; + } + else if(!strcmp("--port", argv[arg])) { + arg++; + if(argc>arg) { + char *endptr; + unsigned long ulnum = strtoul(argv[arg], &endptr, 10); + if((endptr != argv[arg] + strlen(argv[arg])) || + ((ulnum != 0UL) && ((ulnum < 1025UL) || (ulnum > 65535UL)))) { + fprintf(stderr, "sockfilt: invalid --port argument (%s)\n", + argv[arg]); + return 0; + } + port = curlx_ultous(ulnum); + arg++; + } + } + else if(!strcmp("--connect", argv[arg])) { + /* Asked to actively connect to the specified local port instead of + doing a passive server-style listening. */ + arg++; + if(argc>arg) { + char *endptr; + unsigned long ulnum = strtoul(argv[arg], &endptr, 10); + if((endptr != argv[arg] + strlen(argv[arg])) || + (ulnum < 1025UL) || (ulnum > 65535UL)) { + fprintf(stderr, "sockfilt: invalid --connect argument (%s)\n", + argv[arg]); + return 0; + } + connectport = curlx_ultous(ulnum); + arg++; + } + } + else if(!strcmp("--addr", argv[arg])) { + /* Set an IP address to use with --connect; otherwise use localhost */ + arg++; + if(argc>arg) { + addr = argv[arg]; + arg++; + } + } + else { + puts("Usage: sockfilt [option]\n" + " --version\n" + " --verbose\n" + " --logfile [file]\n" + " --pidfile [file]\n" + " --ipv4\n" + " --ipv6\n" + " --bindonly\n" + " --port [port]\n" + " --connect [port]\n" + " --addr [address]"); + return 0; + } + } + +#ifdef WIN32 + win32_init(); + atexit(win32_cleanup); + + setmode(fileno(stdin), O_BINARY); + setmode(fileno(stdout), O_BINARY); + setmode(fileno(stderr), O_BINARY); +#endif + + install_signal_handlers(); + +#ifdef ENABLE_IPV6 + if(!use_ipv6) +#endif + sock = socket(AF_INET, SOCK_STREAM, 0); +#ifdef ENABLE_IPV6 + else + sock = socket(AF_INET6, SOCK_STREAM, 0); +#endif + + if(CURL_SOCKET_BAD == sock) { + error = SOCKERRNO; + logmsg("Error creating socket: (%d) %s", + error, strerror(error)); + write_stdout("FAIL\n", 5); + goto sockfilt_cleanup; + } + + if(connectport) { + /* Active mode, we should connect to the given port number */ + mode = ACTIVE; +#ifdef ENABLE_IPV6 + if(!use_ipv6) { +#endif + memset(&me.sa4, 0, sizeof(me.sa4)); + me.sa4.sin_family = AF_INET; + me.sa4.sin_port = htons(connectport); + me.sa4.sin_addr.s_addr = INADDR_ANY; + if(!addr) + addr = "127.0.0.1"; + Curl_inet_pton(AF_INET, addr, &me.sa4.sin_addr); + + rc = connect(sock, &me.sa, sizeof(me.sa4)); +#ifdef ENABLE_IPV6 + } + else { + memset(&me.sa6, 0, sizeof(me.sa6)); + me.sa6.sin6_family = AF_INET6; + me.sa6.sin6_port = htons(connectport); + if(!addr) + addr = "::1"; + Curl_inet_pton(AF_INET6, addr, &me.sa6.sin6_addr); + + rc = connect(sock, &me.sa, sizeof(me.sa6)); + } +#endif /* ENABLE_IPV6 */ + if(rc) { + error = SOCKERRNO; + logmsg("Error connecting to port %hu: (%d) %s", + connectport, error, strerror(error)); + write_stdout("FAIL\n", 5); + goto sockfilt_cleanup; + } + logmsg("====> Client connect"); + msgsock = sock; /* use this as stream */ + } + else { + /* passive daemon style */ + sock = sockdaemon(sock, &port); + if(CURL_SOCKET_BAD == sock) { + write_stdout("FAIL\n", 5); + goto sockfilt_cleanup; + } + msgsock = CURL_SOCKET_BAD; /* no stream socket yet */ + } + + logmsg("Running %s version", ipv_inuse); + + if(connectport) + logmsg("Connected to port %hu", connectport); + else if(bind_only) + logmsg("Bound without listening on port %hu", port); + else + logmsg("Listening on port %hu", port); + + wrotepidfile = write_pidfile(pidname); + if(!wrotepidfile) { + write_stdout("FAIL\n", 5); + goto sockfilt_cleanup; + } + + do { + juggle_again = juggle(&msgsock, sock, &mode); + } while(juggle_again); + +sockfilt_cleanup: + + if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD)) + sclose(msgsock); + + if(sock != CURL_SOCKET_BAD) + sclose(sock); + + if(wrotepidfile) + unlink(pidname); + + restore_signal_handlers(); + + if(got_exit_signal) { + logmsg("============> sockfilt exits with signal (%d)", exit_signal); + /* + * To properly set the return status of the process we + * must raise the same signal SIGINT or SIGTERM that we + * caught and let the old handler take care of it. + */ + raise(exit_signal); + } + + logmsg("============> sockfilt quits"); + return 0; +} + diff --git a/deps/curl-7.51.0/tests/server/sws.c b/deps/curl-7.51.0/tests/server/sws.c new file mode 100644 index 0000000000..c94e23453d --- /dev/null +++ b/deps/curl-7.51.0/tests/server/sws.c @@ -0,0 +1,2383 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "server_setup.h" + +/* sws.c: simple (silly?) web server + + This code was originally graciously donated to the project by Juergen + Wilke. Thanks a bunch! + + */ + +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_NETINET_TCP_H +#include /* for TCP_NODELAY */ +#endif + +#define ENABLE_CURLX_PRINTF +/* make the curlx header define all printf() functions to use the curlx_* + versions instead */ +#include "curlx.h" /* from the private lib dir */ +#include "getpart.h" +#include "inet_pton.h" +#include "util.h" +#include "server_sockaddr.h" + +/* include memdebug.h last */ +#include "memdebug.h" + +#ifdef USE_WINSOCK +#undef EINTR +#define EINTR 4 /* errno.h value */ +#undef EAGAIN +#define EAGAIN 11 /* errno.h value */ +#undef ERANGE +#define ERANGE 34 /* errno.h value */ +#endif + +static enum { + socket_domain_inet = AF_INET +#ifdef ENABLE_IPV6 + , socket_domain_inet6 = AF_INET6 +#endif +#ifdef USE_UNIX_SOCKETS + , socket_domain_unix = AF_UNIX +#endif +} socket_domain = AF_INET; +static bool use_gopher = FALSE; +static int serverlogslocked = 0; +static bool is_proxy = FALSE; + +#define REQBUFSIZ 150000 +#define REQBUFSIZ_TXT "149999" + +static long prevtestno=-1; /* previous test number we served */ +static long prevpartno=-1; /* previous part number we served */ +static bool prevbounce=FALSE; /* instructs the server to increase the part + number for a test in case the identical + testno+partno request shows up again */ + +#define RCMD_NORMALREQ 0 /* default request, use the tests file normally */ +#define RCMD_IDLE 1 /* told to sit idle */ +#define RCMD_STREAM 2 /* told to stream */ + +struct httprequest { + char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */ + bool connect_request; /* if a CONNECT */ + unsigned short connect_port; /* the port number CONNECT used */ + size_t checkindex; /* where to start checking of the request */ + size_t offset; /* size of the incoming request */ + long testno; /* test number found in the request */ + long partno; /* part number found in the request */ + bool open; /* keep connection open info, as found in the request */ + bool auth_req; /* authentication required, don't wait for body unless + there's an Authorization header */ + bool auth; /* Authorization header present in the incoming request */ + size_t cl; /* Content-Length of the incoming request */ + bool digest; /* Authorization digest header found */ + bool ntlm; /* Authorization ntlm header found */ + int writedelay; /* if non-zero, delay this number of seconds between + writes in the response */ + int pipe; /* if non-zero, expect this many requests to do a "piped" + request/response */ + int skip; /* if non-zero, the server is instructed to not read this + many bytes from a PUT/POST request. Ie the client sends N + bytes said in Content-Length, but the server only reads N + - skip bytes. */ + int rcmd; /* doing a special command, see defines above */ + int prot_version; /* HTTP version * 10 */ + bool pipelining; /* true if request is pipelined */ + int callcount; /* times ProcessRequest() gets called */ + bool connmon; /* monitor the state of the connection, log disconnects */ + bool upgrade; /* test case allows upgrade to http2 */ + bool upgrade_request; /* upgrade request found and allowed */ + int done_processing; +}; + +#define MAX_SOCKETS 1024 + +static curl_socket_t all_sockets[MAX_SOCKETS]; +static size_t num_sockets = 0; + +static int ProcessRequest(struct httprequest *req); +static void storerequest(char *reqbuf, size_t totalsize); + +#define DEFAULT_PORT 8999 + +#ifndef DEFAULT_LOGFILE +#define DEFAULT_LOGFILE "log/sws.log" +#endif + +const char *serverlogfile = DEFAULT_LOGFILE; + +#define SWSVERSION "cURL test suite HTTP server/0.1" + +#define REQUEST_DUMP "log/server.input" +#define RESPONSE_DUMP "log/server.response" + +/* when told to run as proxy, we store the logs in different files so that + they can co-exist with the same program running as a "server" */ +#define REQUEST_PROXY_DUMP "log/proxy.input" +#define RESPONSE_PROXY_DUMP "log/proxy.response" + +/* very-big-path support */ +#define MAXDOCNAMELEN 140000 +#define MAXDOCNAMELEN_TXT "139999" + +#define REQUEST_KEYWORD_SIZE 256 +#define REQUEST_KEYWORD_SIZE_TXT "255" + +#define CMD_AUTH_REQUIRED "auth_required" + +/* 'idle' means that it will accept the request fine but never respond + any data. Just keep the connection alive. */ +#define CMD_IDLE "idle" + +/* 'stream' means to send a never-ending stream of data */ +#define CMD_STREAM "stream" + +/* 'connection-monitor' will output when a server/proxy connection gets + disconnected as for some cases it is important that it gets done at the + proper point - like with NTLM */ +#define CMD_CONNECTIONMONITOR "connection-monitor" + +/* upgrade to http2 */ +#define CMD_UPGRADE "upgrade" + +#define END_OF_HEADERS "\r\n\r\n" + +enum { + DOCNUMBER_NOTHING = -4, + DOCNUMBER_QUIT = -3, + DOCNUMBER_WERULEZ = -2, + DOCNUMBER_404 = -1 +}; + +static const char *end_of_headers = END_OF_HEADERS; + +/* sent as reply to a QUIT */ +static const char *docquit = +"HTTP/1.1 200 Goodbye" END_OF_HEADERS; + +/* send back this on 404 file not found */ +static const char *doc404 = "HTTP/1.1 404 Not Found\r\n" + "Server: " SWSVERSION "\r\n" + "Connection: close\r\n" + "Content-Type: text/html" + END_OF_HEADERS + "\n" + "\n" + "404 Not Found\n" + "\n" + "

Not Found

\n" + "The requested URL was not found on this server.\n" + "


" SWSVERSION "
\n" "\n"; + +/* do-nothing macro replacement for systems which lack siginterrupt() */ + +#ifndef HAVE_SIGINTERRUPT +#define siginterrupt(x,y) do {} while(0) +#endif + +/* vars used to keep around previous signal handlers */ + +typedef RETSIGTYPE (*SIGHANDLER_T)(int); + +#ifdef SIGHUP +static SIGHANDLER_T old_sighup_handler = SIG_ERR; +#endif + +#ifdef SIGPIPE +static SIGHANDLER_T old_sigpipe_handler = SIG_ERR; +#endif + +#ifdef SIGALRM +static SIGHANDLER_T old_sigalrm_handler = SIG_ERR; +#endif + +#ifdef SIGINT +static SIGHANDLER_T old_sigint_handler = SIG_ERR; +#endif + +#ifdef SIGTERM +static SIGHANDLER_T old_sigterm_handler = SIG_ERR; +#endif + +#if defined(SIGBREAK) && defined(WIN32) +static SIGHANDLER_T old_sigbreak_handler = SIG_ERR; +#endif + +/* var which if set indicates that the program should finish execution */ + +SIG_ATOMIC_T got_exit_signal = 0; + +/* if next is set indicates the first signal handled in exit_signal_handler */ + +static volatile int exit_signal = 0; + +/* signal handler that will be triggered to indicate that the program + should finish its execution in a controlled manner as soon as possible. + The first time this is called it will set got_exit_signal to one and + store in exit_signal the signal that triggered its execution. */ + +static RETSIGTYPE exit_signal_handler(int signum) +{ + int old_errno = errno; + if(got_exit_signal == 0) { + got_exit_signal = 1; + exit_signal = signum; + } + (void)signal(signum, exit_signal_handler); + errno = old_errno; +} + +static void install_signal_handlers(void) +{ +#ifdef SIGHUP + /* ignore SIGHUP signal */ + if((old_sighup_handler = signal(SIGHUP, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGHUP handler: %s", strerror(errno)); +#endif +#ifdef SIGPIPE + /* ignore SIGPIPE signal */ + if((old_sigpipe_handler = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGPIPE handler: %s", strerror(errno)); +#endif +#ifdef SIGALRM + /* ignore SIGALRM signal */ + if((old_sigalrm_handler = signal(SIGALRM, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGALRM handler: %s", strerror(errno)); +#endif +#ifdef SIGINT + /* handle SIGINT signal with our exit_signal_handler */ + if((old_sigint_handler = signal(SIGINT, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGINT handler: %s", strerror(errno)); + else + siginterrupt(SIGINT, 1); +#endif +#ifdef SIGTERM + /* handle SIGTERM signal with our exit_signal_handler */ + if((old_sigterm_handler = signal(SIGTERM, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGTERM handler: %s", strerror(errno)); + else + siginterrupt(SIGTERM, 1); +#endif +#if defined(SIGBREAK) && defined(WIN32) + /* handle SIGBREAK signal with our exit_signal_handler */ + if((old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGBREAK handler: %s", strerror(errno)); + else + siginterrupt(SIGBREAK, 1); +#endif +} + +static void restore_signal_handlers(void) +{ +#ifdef SIGHUP + if(SIG_ERR != old_sighup_handler) + (void)signal(SIGHUP, old_sighup_handler); +#endif +#ifdef SIGPIPE + if(SIG_ERR != old_sigpipe_handler) + (void)signal(SIGPIPE, old_sigpipe_handler); +#endif +#ifdef SIGALRM + if(SIG_ERR != old_sigalrm_handler) + (void)signal(SIGALRM, old_sigalrm_handler); +#endif +#ifdef SIGINT + if(SIG_ERR != old_sigint_handler) + (void)signal(SIGINT, old_sigint_handler); +#endif +#ifdef SIGTERM + if(SIG_ERR != old_sigterm_handler) + (void)signal(SIGTERM, old_sigterm_handler); +#endif +#if defined(SIGBREAK) && defined(WIN32) + if(SIG_ERR != old_sigbreak_handler) + (void)signal(SIGBREAK, old_sigbreak_handler); +#endif +} + +/* returns true if the current socket is an IP one */ +static bool socket_domain_is_ip(void) +{ + switch(socket_domain) { + case AF_INET: +#ifdef ENABLE_IPV6 + case AF_INET6: +#endif + return true; + default: + /* case AF_UNIX: */ + return false; + } +} + +/* based on the testno, parse the correct server commands */ +static int parse_servercmd(struct httprequest *req) +{ + FILE *stream; + char *filename; + int error; + + filename = test2file(req->testno); + + stream=fopen(filename, "rb"); + if(!stream) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg(" [1] Error opening file: %s", filename); + logmsg(" Couldn't open test file %ld", req->testno); + req->open = FALSE; /* closes connection */ + return 1; /* done */ + } + else { + char *orgcmd = NULL; + char *cmd = NULL; + size_t cmdsize = 0; + int num=0; + + /* get the custom server control "commands" */ + error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream); + fclose(stream); + if(error) { + logmsg("getpart() failed with error: %d", error); + req->open = FALSE; /* closes connection */ + return 1; /* done */ + } + + req->connmon = FALSE; + + cmd = orgcmd; + while(cmd && cmdsize) { + char *check; + + if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) { + logmsg("instructed to require authorization header"); + req->auth_req = TRUE; + } + else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) { + logmsg("instructed to idle"); + req->rcmd = RCMD_IDLE; + req->open = TRUE; + } + else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) { + logmsg("instructed to stream"); + req->rcmd = RCMD_STREAM; + } + else if(!strncmp(CMD_CONNECTIONMONITOR, cmd, + strlen(CMD_CONNECTIONMONITOR))) { + logmsg("enabled connection monitoring"); + req->connmon = TRUE; + } + else if(!strncmp(CMD_UPGRADE, cmd, strlen(CMD_UPGRADE))) { + logmsg("enabled upgrade to http2"); + req->upgrade = TRUE; + } + else if(1 == sscanf(cmd, "pipe: %d", &num)) { + logmsg("instructed to allow a pipe size of %d", num); + if(num < 0) + logmsg("negative pipe size ignored"); + else if(num > 0) + req->pipe = num-1; /* decrease by one since we don't count the + first request in this number */ + } + else if(1 == sscanf(cmd, "skip: %d", &num)) { + logmsg("instructed to skip this number of bytes %d", num); + req->skip = num; + } + else if(1 == sscanf(cmd, "writedelay: %d", &num)) { + logmsg("instructed to delay %d secs between packets", num); + req->writedelay = num; + } + else { + logmsg("Unknown instruction found: %s", cmd); + } + /* try to deal with CRLF or just LF */ + check = strchr(cmd, '\r'); + if(!check) + check = strchr(cmd, '\n'); + + if(check) { + /* get to the letter following the newline */ + while((*check == '\r') || (*check == '\n')) + check++; + + if(!*check) + /* if we reached a zero, get out */ + break; + cmd = check; + } + else + break; + } + free(orgcmd); + } + + return 0; /* OK! */ +} + +static int ProcessRequest(struct httprequest *req) +{ + char *line=&req->reqbuf[req->checkindex]; + bool chunked = FALSE; + static char request[REQUEST_KEYWORD_SIZE]; + static char doc[MAXDOCNAMELEN]; + char logbuf[456]; + int prot_major, prot_minor; + char *end = strstr(line, end_of_headers); + + req->callcount++; + + logmsg("Process %d bytes request%s", req->offset, + req->callcount > 1?" [CONTINUED]":""); + + /* try to figure out the request characteristics as soon as possible, but + only once! */ + + if(use_gopher && + (req->testno == DOCNUMBER_NOTHING) && + !strncmp("/verifiedserver", line, 15)) { + logmsg("Are-we-friendly question received"); + req->testno = DOCNUMBER_WERULEZ; + return 1; /* done */ + } + + else if((req->testno == DOCNUMBER_NOTHING) && + sscanf(line, + "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d", + request, + doc, + &prot_major, + &prot_minor) == 4) { + char *ptr; + + req->prot_version = prot_major*10 + prot_minor; + + /* find the last slash */ + ptr = strrchr(doc, '/'); + + /* get the number after it */ + if(ptr) { + if((strlen(doc) + strlen(request)) < 400) + snprintf(logbuf, sizeof(logbuf), "Got request: %s %s HTTP/%d.%d", + request, doc, prot_major, prot_minor); + else + snprintf(logbuf, sizeof(logbuf), "Got a *HUGE* request HTTP/%d.%d", + prot_major, prot_minor); + logmsg("%s", logbuf); + + if(!strncmp("/verifiedserver", ptr, 15)) { + logmsg("Are-we-friendly question received"); + req->testno = DOCNUMBER_WERULEZ; + return 1; /* done */ + } + + if(!strncmp("/quit", ptr, 5)) { + logmsg("Request-to-quit received"); + req->testno = DOCNUMBER_QUIT; + return 1; /* done */ + } + + ptr++; /* skip the slash */ + + /* skip all non-numericals following the slash */ + while(*ptr && !ISDIGIT(*ptr)) + ptr++; + + req->testno = strtol(ptr, &ptr, 10); + + if(req->testno > 10000) { + req->partno = req->testno % 10000; + req->testno /= 10000; + } + else + req->partno = 0; + + if(req->testno) { + + snprintf(logbuf, sizeof(logbuf), "Requested test number %ld part %ld", + req->testno, req->partno); + logmsg("%s", logbuf); + + /* find and parse for this test */ + parse_servercmd(req); + } + else + req->testno = DOCNUMBER_NOTHING; + + } + + if(req->testno == DOCNUMBER_NOTHING) { + /* didn't find any in the first scan, try alternative test case + number placements */ + + if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d", + doc, &prot_major, &prot_minor) == 3) { + char *portp = NULL; + unsigned long part=0; + + snprintf(logbuf, sizeof(logbuf), + "Received a CONNECT %s HTTP/%d.%d request", + doc, prot_major, prot_minor); + logmsg("%s", logbuf); + + req->connect_request = TRUE; + + if(req->prot_version == 10) + req->open = FALSE; /* HTTP 1.0 closes connection by default */ + + if(doc[0] == '[') { + char *p = &doc[1]; + /* scan through the hexgroups and store the value of the last group + in the 'part' variable and use as test case number!! */ + while(*p && (ISXDIGIT(*p) || (*p == ':') || (*p == '.'))) { + char *endp; + part = strtoul(p, &endp, 16); + if(ISXDIGIT(*p)) + p = endp; + else + p++; + } + if(*p != ']') + logmsg("Invalid CONNECT IPv6 address format"); + else if(*(p+1) != ':') + logmsg("Invalid CONNECT IPv6 port format"); + else + portp = p+1; + + req->testno = part; + } + else + portp = strchr(doc, ':'); + + if(portp && (*(portp+1) != '\0') && ISDIGIT(*(portp+1))) { + unsigned long ulnum = strtoul(portp+1, NULL, 10); + if(!ulnum || (ulnum > 65535UL)) + logmsg("Invalid CONNECT port received"); + else + req->connect_port = curlx_ultous(ulnum); + + } + logmsg("Port number: %d, test case number: %ld", + req->connect_port, req->testno); + } + } + + if(req->testno == DOCNUMBER_NOTHING) { + /* Still no test case number. Try to get the the number off the last dot + instead, IE we consider the TLD to be the test number. Test 123 can + then be written as "example.com.123". */ + + /* find the last dot */ + ptr = strrchr(doc, '.'); + + /* get the number after it */ + if(ptr) { + ptr++; /* skip the dot */ + + req->testno = strtol(ptr, &ptr, 10); + + if(req->testno > 10000) { + req->partno = req->testno % 10000; + req->testno /= 10000; + + logmsg("found test %d in requested host name", req->testno); + + } + else + req->partno = 0; + + snprintf(logbuf, sizeof(logbuf), + "Requested test number %ld part %ld (from host name)", + req->testno, req->partno); + logmsg("%s", logbuf); + + } + + if(!req->testno) { + logmsg("Did not find test number in PATH"); + req->testno = DOCNUMBER_404; + } + else + parse_servercmd(req); + } + } + else if((req->offset >= 3) && (req->testno == DOCNUMBER_NOTHING)) { + logmsg("** Unusual request. Starts with %02x %02x %02x", + line[0], line[1], line[2]); + } + + if(!end) { + /* we don't have a complete request yet! */ + logmsg("request not complete yet"); + return 0; /* not complete yet */ + } + logmsg("- request found to be complete"); + + if(use_gopher) { + /* when using gopher we cannot check the request until the entire + thing has been received */ + char *ptr; + + /* find the last slash in the line */ + ptr = strrchr(line, '/'); + + if(ptr) { + ptr++; /* skip the slash */ + + /* skip all non-numericals following the slash */ + while(*ptr && !ISDIGIT(*ptr)) + ptr++; + + req->testno = strtol(ptr, &ptr, 10); + + if(req->testno > 10000) { + req->partno = req->testno % 10000; + req->testno /= 10000; + } + else + req->partno = 0; + + snprintf(logbuf, sizeof(logbuf), + "Requested GOPHER test number %ld part %ld", + req->testno, req->partno); + logmsg("%s", logbuf); + } + } + + if(req->pipe) + /* we do have a full set, advance the checkindex to after the end of the + headers, for the pipelining case mostly */ + req->checkindex += (end - line) + strlen(end_of_headers); + + /* **** Persistence **** + * + * If the request is a HTTP/1.0 one, we close the connection unconditionally + * when we're done. + * + * If the request is a HTTP/1.1 one, we MUST check for a "Connection:" + * header that might say "close". If it does, we close a connection when + * this request is processed. Otherwise, we keep the connection alive for X + * seconds. + */ + + do { + if(got_exit_signal) + return 1; /* done */ + + if((req->cl==0) && strncasecompare("Content-Length:", line, 15)) { + /* If we don't ignore content-length, we read it and we read the whole + request including the body before we return. If we've been told to + ignore the content-length, we will return as soon as all headers + have been received */ + char *endptr; + char *ptr = line + 15; + unsigned long clen = 0; + while(*ptr && ISSPACE(*ptr)) + ptr++; + endptr = ptr; + errno = 0; + clen = strtoul(ptr, &endptr, 10); + if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == errno)) { + /* this assumes that a zero Content-Length is valid */ + logmsg("Found invalid Content-Length: (%s) in the request", ptr); + req->open = FALSE; /* closes connection */ + return 1; /* done */ + } + req->cl = clen - req->skip; + + logmsg("Found Content-Length: %lu in the request", clen); + if(req->skip) + logmsg("... but will abort after %zu bytes", req->cl); + break; + } + else if(strncasecompare("Transfer-Encoding: chunked", line, + strlen("Transfer-Encoding: chunked"))) { + /* chunked data coming in */ + chunked = TRUE; + } + + if(chunked) { + if(strstr(req->reqbuf, "\r\n0\r\n\r\n")) + /* end of chunks reached */ + return 1; /* done */ + else + return 0; /* not done */ + } + + line = strchr(line, '\n'); + if(line) + line++; + + } while(line); + + if(!req->auth && strstr(req->reqbuf, "Authorization:")) { + req->auth = TRUE; /* Authorization: header present! */ + if(req->auth_req) + logmsg("Authorization header found, as required"); + } + + if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) { + /* If the client is passing this Digest-header, we set the part number + to 1000. Not only to spice up the complexity of this, but to make + Digest stuff to work in the test suite. */ + req->partno += 1000; + req->digest = TRUE; /* header found */ + logmsg("Received Digest request, sending back data %ld", req->partno); + } + else if(!req->ntlm && + strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) { + /* If the client is passing this type-3 NTLM header */ + req->partno += 1002; + req->ntlm = TRUE; /* NTLM found */ + logmsg("Received NTLM type-3, sending back data %ld", req->partno); + if(req->cl) { + logmsg(" Expecting %zu POSTed bytes", req->cl); + } + } + else if(!req->ntlm && + strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) { + /* If the client is passing this type-1 NTLM header */ + req->partno += 1001; + req->ntlm = TRUE; /* NTLM found */ + logmsg("Received NTLM type-1, sending back data %ld", req->partno); + } + else if((req->partno >= 1000) && + strstr(req->reqbuf, "Authorization: Basic")) { + /* If the client is passing this Basic-header and the part number is + already >=1000, we add 1 to the part number. This allows simple Basic + authentication negotiation to work in the test suite. */ + req->partno += 1; + logmsg("Received Basic request, sending back data %ld", req->partno); + } + if(strstr(req->reqbuf, "Connection: close")) + req->open = FALSE; /* close connection after this request */ + + if(!req->pipe && + req->open && + req->prot_version >= 11 && + end && + req->reqbuf + req->offset > end + strlen(end_of_headers) && + !req->cl && + (!strncmp(req->reqbuf, "GET", strlen("GET")) || + !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) { + /* If we have a persistent connection, HTTP version >= 1.1 + and GET/HEAD request, enable pipelining. */ + req->checkindex = (end - req->reqbuf) + strlen(end_of_headers); + req->pipelining = TRUE; + } + + while(req->pipe) { + if(got_exit_signal) + return 1; /* done */ + /* scan for more header ends within this chunk */ + line = &req->reqbuf[req->checkindex]; + end = strstr(line, end_of_headers); + if(!end) + break; + req->checkindex += (end - line) + strlen(end_of_headers); + req->pipe--; + } + + /* If authentication is required and no auth was provided, end now. This + makes the server NOT wait for PUT/POST data and you can then make the + test case send a rejection before any such data has been sent. Test case + 154 uses this.*/ + if(req->auth_req && !req->auth) { + logmsg("Return early due to auth requested by none provided"); + return 1; /* done */ + } + + if(req->upgrade && strstr(req->reqbuf, "Upgrade:")) { + /* we allow upgrade and there was one! */ + logmsg("Found Upgrade: in request and allows it"); + req->upgrade_request = TRUE; + } + + if(req->cl > 0) { + if(req->cl <= req->offset - (end - req->reqbuf) - strlen(end_of_headers)) + return 1; /* done */ + else + return 0; /* not complete yet */ + } + + return 1; /* done */ +} + +/* store the entire request in a file */ +static void storerequest(char *reqbuf, size_t totalsize) +{ + int res; + int error = 0; + size_t written; + size_t writeleft; + FILE *dump; + const char *dumpfile=is_proxy?REQUEST_PROXY_DUMP:REQUEST_DUMP; + + if(reqbuf == NULL) + return; + if(totalsize == 0) + return; + + do { + dump = fopen(dumpfile, "ab"); + } while((dump == NULL) && ((error = errno) == EINTR)); + if(dump == NULL) { + logmsg("[2] Error opening file %s error: %d %s", + dumpfile, error, strerror(error)); + logmsg("Failed to write request input "); + return; + } + + writeleft = totalsize; + do { + written = fwrite(&reqbuf[totalsize-writeleft], + 1, writeleft, dump); + if(got_exit_signal) + goto storerequest_cleanup; + if(written > 0) + writeleft -= written; + } while((writeleft > 0) && ((error = errno) == EINTR)); + + if(writeleft == 0) + logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile); + else if(writeleft > 0) { + logmsg("Error writing file %s error: %d %s", + dumpfile, error, strerror(error)); + logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s", + totalsize-writeleft, totalsize, dumpfile); + } + +storerequest_cleanup: + + do { + res = fclose(dump); + } while(res && ((error = errno) == EINTR)); + if(res) + logmsg("Error closing file %s error: %d %s", + dumpfile, error, strerror(error)); +} + +static void init_httprequest(struct httprequest *req) +{ + /* Pipelining is already set, so do not initialize it here. Only initialize + checkindex and offset if pipelining is not set, since in a pipeline they + need to be inherited from the previous request. */ + if(!req->pipelining) { + req->checkindex = 0; + req->offset = 0; + } + req->testno = DOCNUMBER_NOTHING; + req->partno = 0; + req->connect_request = FALSE; + req->open = TRUE; + req->auth_req = FALSE; + req->auth = FALSE; + req->cl = 0; + req->digest = FALSE; + req->ntlm = FALSE; + req->pipe = 0; + req->skip = 0; + req->writedelay = 0; + req->rcmd = RCMD_NORMALREQ; + req->prot_version = 0; + req->callcount = 0; + req->connect_port = 0; + req->done_processing = 0; + req->upgrade = 0; + req->upgrade_request = 0; +} + +/* returns 1 if the connection should be serviced again immediately, 0 if there + is no data waiting, or < 0 if it should be closed */ +static int get_request(curl_socket_t sock, struct httprequest *req) +{ + int error; + int fail = 0; + char *reqbuf = req->reqbuf; + ssize_t got = 0; + int overflow = 0; + + char *pipereq = NULL; + size_t pipereq_length = 0; + + if(req->pipelining) { + pipereq = reqbuf + req->checkindex; + pipereq_length = req->offset - req->checkindex; + + /* Now that we've got the pipelining info we can reset the + pipelining-related vars which were skipped in init_httprequest */ + req->pipelining = FALSE; + req->checkindex = 0; + req->offset = 0; + } + + if(req->offset >= REQBUFSIZ-1) { + /* buffer is already full; do nothing */ + overflow = 1; + } + else { + if(pipereq_length && pipereq) { + memmove(reqbuf, pipereq, pipereq_length); + got = curlx_uztosz(pipereq_length); + pipereq_length = 0; + } + else { + if(req->skip) + /* we are instructed to not read the entire thing, so we make sure to + only read what we're supposed to and NOT read the enire thing the + client wants to send! */ + got = sread(sock, reqbuf + req->offset, req->cl); + else + got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset); + } + if(got_exit_signal) + return -1; + if(got == 0) { + logmsg("Connection closed by client"); + fail = 1; + } + else if(got < 0) { + error = SOCKERRNO; + if(EAGAIN == error || EWOULDBLOCK == error) { + /* nothing to read at the moment */ + return 0; + } + logmsg("recv() returned error: (%d) %s", error, strerror(error)); + fail = 1; + } + if(fail) { + /* dump the request received so far to the external file */ + reqbuf[req->offset] = '\0'; + storerequest(reqbuf, req->offset); + return -1; + } + + logmsg("Read %zd bytes", got); + + req->offset += (size_t)got; + reqbuf[req->offset] = '\0'; + + req->done_processing = ProcessRequest(req); + if(got_exit_signal) + return -1; + if(req->done_processing && req->pipe) { + logmsg("Waiting for another piped request"); + req->done_processing = 0; + req->pipe--; + } + } + + if(overflow || (req->offset == REQBUFSIZ-1 && got > 0)) { + logmsg("Request would overflow buffer, closing connection"); + /* dump request received so far to external file anyway */ + reqbuf[REQBUFSIZ-1] = '\0'; + fail = 1; + } + else if(req->offset > REQBUFSIZ-1) { + logmsg("Request buffer overflow, closing connection"); + /* dump request received so far to external file anyway */ + reqbuf[REQBUFSIZ-1] = '\0'; + fail = 1; + } + else + reqbuf[req->offset] = '\0'; + + /* at the end of a request dump it to an external file */ + if(fail || req->done_processing) + storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset); + if(got_exit_signal) + return -1; + + return fail ? -1 : 1; +} + +/* returns -1 on failure */ +static int send_doc(curl_socket_t sock, struct httprequest *req) +{ + ssize_t written; + size_t count; + const char *buffer; + char *ptr=NULL; + FILE *stream; + char *cmd=NULL; + size_t cmdsize=0; + FILE *dump; + bool persistant = TRUE; + bool sendfailure = FALSE; + size_t responsesize; + int error = 0; + int res; + const char *responsedump = is_proxy?RESPONSE_PROXY_DUMP:RESPONSE_DUMP; + static char weare[256]; + + switch(req->rcmd) { + default: + case RCMD_NORMALREQ: + break; /* continue with business as usual */ + case RCMD_STREAM: +#define STREAMTHIS "a string to stream 01234567890\n" + count = strlen(STREAMTHIS); + for(;;) { + written = swrite(sock, STREAMTHIS, count); + if(got_exit_signal) + return -1; + if(written != (ssize_t)count) { + logmsg("Stopped streaming"); + break; + } + } + return -1; + case RCMD_IDLE: + /* Do nothing. Sit idle. Pretend it rains. */ + return 0; + } + + req->open = FALSE; + + if(req->testno < 0) { + size_t msglen; + char msgbuf[64]; + + switch(req->testno) { + case DOCNUMBER_QUIT: + logmsg("Replying to QUIT"); + buffer = docquit; + break; + case DOCNUMBER_WERULEZ: + /* we got a "friends?" question, reply back that we sure are */ + logmsg("Identifying ourselves as friends"); + snprintf(msgbuf, sizeof(msgbuf), "WE ROOLZ: %ld\r\n", (long)getpid()); + msglen = strlen(msgbuf); + if(use_gopher) + snprintf(weare, sizeof(weare), "%s", msgbuf); + else + snprintf(weare, sizeof(weare), + "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s", + msglen, msgbuf); + buffer = weare; + break; + case DOCNUMBER_404: + default: + logmsg("Replying to with a 404"); + buffer = doc404; + break; + } + + count = strlen(buffer); + } + else { + char partbuf[80]; + char *filename = test2file(req->testno); + + /* select the tag for "normal" requests and the one + for CONNECT requests (within the section) */ + const char *section= req->connect_request?"connect":"data"; + + if(req->partno) + snprintf(partbuf, sizeof(partbuf), "%s%ld", section, req->partno); + else + snprintf(partbuf, sizeof(partbuf), "%s", section); + + logmsg("Send response test%ld section <%s>", req->testno, partbuf); + + stream=fopen(filename, "rb"); + if(!stream) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg(" [3] Error opening file: %s", filename); + return 0; + } + else { + error = getpart(&ptr, &count, "reply", partbuf, stream); + fclose(stream); + if(error) { + logmsg("getpart() failed with error: %d", error); + return 0; + } + buffer = ptr; + } + + if(got_exit_signal) { + free(ptr); + return -1; + } + + /* re-open the same file again */ + stream=fopen(filename, "rb"); + if(!stream) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg(" [4] Error opening file: %s", filename); + free(ptr); + return 0; + } + else { + /* get the custom server control "commands" */ + error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream); + fclose(stream); + if(error) { + logmsg("getpart() failed with error: %d", error); + free(ptr); + return 0; + } + } + } + + if(got_exit_signal) { + free(ptr); + free(cmd); + return -1; + } + + /* If the word 'swsclose' is present anywhere in the reply chunk, the + connection will be closed after the data has been sent to the requesting + client... */ + if(strstr(buffer, "swsclose") || !count) { + persistant = FALSE; + logmsg("connection close instruction \"swsclose\" found in response"); + } + if(strstr(buffer, "swsbounce")) { + prevbounce = TRUE; + logmsg("enable \"swsbounce\" in the next request"); + } + else + prevbounce = FALSE; + + dump = fopen(responsedump, "ab"); + if(!dump) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg(" [5] Error opening file: %s", responsedump); + free(ptr); + free(cmd); + return -1; + } + + responsesize = count; + do { + /* Ok, we send no more than 200 bytes at a time, just to make sure that + larger chunks are split up so that the client will need to do multiple + recv() calls to get it and thus we exercise that code better */ + size_t num = count; + if(num > 200) + num = 200; + written = swrite(sock, buffer, num); + if(written < 0) { + sendfailure = TRUE; + break; + } + else { + logmsg("Sent off %zd bytes", written); + } + /* write to file as well */ + fwrite(buffer, 1, (size_t)written, dump); + + count -= written; + buffer += written; + + if(req->writedelay) { + int quarters = req->writedelay * 4; + logmsg("Pausing %d seconds", req->writedelay); + while((quarters > 0) && !got_exit_signal) { + quarters--; + wait_ms(250); + } + } + } while((count > 0) && !got_exit_signal); + + do { + res = fclose(dump); + } while(res && ((error = errno) == EINTR)); + if(res) + logmsg("Error closing file %s error: %d %s", + responsedump, error, strerror(error)); + + if(got_exit_signal) { + free(ptr); + free(cmd); + return -1; + } + + if(sendfailure) { + logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) " + "were sent", + responsesize-count, responsesize); + free(ptr); + free(cmd); + return -1; + } + + logmsg("Response sent (%zu bytes) and written to %s", + responsesize, responsedump); + free(ptr); + + if(cmdsize > 0) { + char command[32]; + int quarters; + int num; + ptr=cmd; + do { + if(2 == sscanf(ptr, "%31s %d", command, &num)) { + if(!strcmp("wait", command)) { + logmsg("Told to sleep for %d seconds", num); + quarters = num * 4; + while((quarters > 0) && !got_exit_signal) { + quarters--; + res = wait_ms(250); + if(res) { + /* should not happen */ + error = errno; + logmsg("wait_ms() failed with error: (%d) %s", + error, strerror(error)); + break; + } + } + if(!quarters) + logmsg("Continuing after sleeping %d seconds", num); + } + else + logmsg("Unknown command in reply command section"); + } + ptr = strchr(ptr, '\n'); + if(ptr) + ptr++; + else + ptr = NULL; + } while(ptr && *ptr); + } + free(cmd); + req->open = use_gopher?FALSE:persistant; + + prevtestno = req->testno; + prevpartno = req->partno; + + return 0; +} + +static curl_socket_t connect_to(const char *ipaddr, unsigned short port) +{ + srvr_sockaddr_union_t serveraddr; + curl_socket_t serverfd; + int error; + int rc = 0; + const char *op_br = ""; + const char *cl_br = ""; + +#ifdef ENABLE_IPV6 + if(socket_domain == AF_INET6) { + op_br = "["; + cl_br = "]"; + } +#endif + + if(!ipaddr) + return CURL_SOCKET_BAD; + + logmsg("about to connect to %s%s%s:%hu", + op_br, ipaddr, cl_br, port); + + + serverfd = socket(socket_domain, SOCK_STREAM, 0); + if(CURL_SOCKET_BAD == serverfd) { + error = SOCKERRNO; + logmsg("Error creating socket for server conection: (%d) %s", + error, strerror(error)); + return CURL_SOCKET_BAD; + } + +#ifdef TCP_NODELAY + if(socket_domain_is_ip()) { + /* Disable the Nagle algorithm */ + curl_socklen_t flag = 1; + if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY, + (void *)&flag, sizeof(flag))) + logmsg("====> TCP_NODELAY for server conection failed"); + } +#endif + + switch(socket_domain) { + case AF_INET: + memset(&serveraddr.sa4, 0, sizeof(serveraddr.sa4)); + serveraddr.sa4.sin_family = AF_INET; + serveraddr.sa4.sin_port = htons(port); + if(Curl_inet_pton(AF_INET, ipaddr, &serveraddr.sa4.sin_addr) < 1) { + logmsg("Error inet_pton failed AF_INET conversion of '%s'", ipaddr); + sclose(serverfd); + return CURL_SOCKET_BAD; + } + + rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa4)); + break; +#ifdef ENABLE_IPV6 + case AF_INET6: + memset(&serveraddr.sa6, 0, sizeof(serveraddr.sa6)); + serveraddr.sa6.sin6_family = AF_INET6; + serveraddr.sa6.sin6_port = htons(port); + if(Curl_inet_pton(AF_INET6, ipaddr, &serveraddr.sa6.sin6_addr) < 1) { + logmsg("Error inet_pton failed AF_INET6 conversion of '%s'", ipaddr); + sclose(serverfd); + return CURL_SOCKET_BAD; + } + + rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6)); + break; +#endif /* ENABLE_IPV6 */ +#ifdef USE_UNIX_SOCKETS + case AF_UNIX: + logmsg("Proxying through Unix socket is not (yet?) supported."); + return CURL_SOCKET_BAD; +#endif /* USE_UNIX_SOCKETS */ + } + + if(got_exit_signal) { + sclose(serverfd); + return CURL_SOCKET_BAD; + } + + if(rc) { + error = SOCKERRNO; + logmsg("Error connecting to server port %hu: (%d) %s", + port, error, strerror(error)); + sclose(serverfd); + return CURL_SOCKET_BAD; + } + + logmsg("connected fine to %s%s%s:%hu, now tunnel", + op_br, ipaddr, cl_br, port); + + return serverfd; +} + +/* + * A CONNECT has been received, a CONNECT response has been sent. + * + * This function needs to connect to the server, and then pass data between + * the client and the server back and forth until the connection is closed by + * either end. + * + * When doing FTP through a CONNECT proxy, we expect that the data connection + * will be setup while the first connect is still being kept up. Therefor we + * must accept a new connection and deal with it appropriately. + */ + +#define data_or_ctrl(x) ((x)?"DATA":"CTRL") + +#define CTRL 0 +#define DATA 1 + +static void http_connect(curl_socket_t *infdp, + curl_socket_t rootfd, + const char *ipaddr, + unsigned short ipport) +{ + curl_socket_t serverfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD}; + curl_socket_t clientfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD}; + ssize_t toc[2] = {0, 0}; /* number of bytes to client */ + ssize_t tos[2] = {0, 0}; /* number of bytes to server */ + char readclient[2][256]; + char readserver[2][256]; + bool poll_client_rd[2] = { TRUE, TRUE }; + bool poll_server_rd[2] = { TRUE, TRUE }; + bool poll_client_wr[2] = { TRUE, TRUE }; + bool poll_server_wr[2] = { TRUE, TRUE }; + bool primary = FALSE; + bool secondary = FALSE; + int max_tunnel_idx; /* CTRL or DATA */ + int loop; + int i; + int timeout_count=0; + + /* primary tunnel client endpoint already connected */ + clientfd[CTRL] = *infdp; + + /* Sleep here to make sure the client reads CONNECT response's + 'end of headers' separate from the server data that follows. + This is done to prevent triggering libcurl known bug #39. */ + for(loop = 2; (loop > 0) && !got_exit_signal; loop--) + wait_ms(250); + if(got_exit_signal) + goto http_connect_cleanup; + + serverfd[CTRL] = connect_to(ipaddr, ipport); + if(serverfd[CTRL] == CURL_SOCKET_BAD) + goto http_connect_cleanup; + + /* Primary tunnel socket endpoints are now connected. Tunnel data back and + forth over the primary tunnel until client or server breaks the primary + tunnel, simultaneously allowing establishment, operation and teardown of + a secondary tunnel that may be used for passive FTP data connection. */ + + max_tunnel_idx = CTRL; + primary = TRUE; + + while(!got_exit_signal) { + + fd_set input; + fd_set output; + struct timeval timeout = {1, 0}; /* 1000 ms */ + ssize_t rc; + curl_socket_t maxfd = (curl_socket_t)-1; + + FD_ZERO(&input); + FD_ZERO(&output); + + if((clientfd[DATA] == CURL_SOCKET_BAD) && + (serverfd[DATA] == CURL_SOCKET_BAD) && + poll_client_rd[CTRL] && poll_client_wr[CTRL] && + poll_server_rd[CTRL] && poll_server_wr[CTRL]) { + /* listener socket is monitored to allow client to establish + secondary tunnel only when this tunnel is not established + and primary one is fully operational */ + FD_SET(rootfd, &input); + maxfd = rootfd; + } + + /* set tunnel sockets to wait for */ + for(i = 0; i <= max_tunnel_idx; i++) { + /* client side socket monitoring */ + if(clientfd[i] != CURL_SOCKET_BAD) { + if(poll_client_rd[i]) { + /* unless told not to do so, monitor readability */ + FD_SET(clientfd[i], &input); + if(clientfd[i] > maxfd) + maxfd = clientfd[i]; + } + if(poll_client_wr[i] && toc[i]) { + /* unless told not to do so, monitor writeability + if there is data ready to be sent to client */ + FD_SET(clientfd[i], &output); + if(clientfd[i] > maxfd) + maxfd = clientfd[i]; + } + } + /* server side socket monitoring */ + if(serverfd[i] != CURL_SOCKET_BAD) { + if(poll_server_rd[i]) { + /* unless told not to do so, monitor readability */ + FD_SET(serverfd[i], &input); + if(serverfd[i] > maxfd) + maxfd = serverfd[i]; + } + if(poll_server_wr[i] && tos[i]) { + /* unless told not to do so, monitor writeability + if there is data ready to be sent to server */ + FD_SET(serverfd[i], &output); + if(serverfd[i] > maxfd) + maxfd = serverfd[i]; + } + } + } + if(got_exit_signal) + break; + + rc = select((int)maxfd + 1, &input, &output, NULL, &timeout); + + if(rc > 0) { + /* socket action */ + bool tcp_fin_wr; + timeout_count=0; + + if(got_exit_signal) + break; + + tcp_fin_wr = FALSE; + + /* ---------------------------------------------------------- */ + + /* passive mode FTP may establish a secondary tunnel */ + if((clientfd[DATA] == CURL_SOCKET_BAD) && + (serverfd[DATA] == CURL_SOCKET_BAD) && FD_ISSET(rootfd, &input)) { + /* a new connection on listener socket (most likely from client) */ + curl_socket_t datafd = accept(rootfd, NULL, NULL); + if(datafd != CURL_SOCKET_BAD) { + struct httprequest req2; + int err = 0; + memset(&req2, 0, sizeof(req2)); + logmsg("====> Client connect DATA"); +#ifdef TCP_NODELAY + if(socket_domain_is_ip()) { + /* Disable the Nagle algorithm */ + curl_socklen_t flag = 1; + if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY, + (void *)&flag, sizeof(flag))) + logmsg("====> TCP_NODELAY for client DATA conection failed"); + } +#endif + req2.pipelining = FALSE; + init_httprequest(&req2); + while(!req2.done_processing) { + err = get_request(datafd, &req2); + if(err < 0) { + /* this socket must be closed, done or not */ + break; + } + } + + /* skip this and close the socket if err < 0 */ + if(err >= 0) { + err = send_doc(datafd, &req2); + if(!err && req2.connect_request) { + /* sleep to prevent triggering libcurl known bug #39. */ + for(loop = 2; (loop > 0) && !got_exit_signal; loop--) + wait_ms(250); + if(!got_exit_signal) { + /* connect to the server */ + serverfd[DATA] = connect_to(ipaddr, req2.connect_port); + if(serverfd[DATA] != CURL_SOCKET_BAD) { + /* secondary tunnel established, now we have two + connections */ + poll_client_rd[DATA] = TRUE; + poll_client_wr[DATA] = TRUE; + poll_server_rd[DATA] = TRUE; + poll_server_wr[DATA] = TRUE; + max_tunnel_idx = DATA; + secondary = TRUE; + toc[DATA] = 0; + tos[DATA] = 0; + clientfd[DATA] = datafd; + datafd = CURL_SOCKET_BAD; + } + } + } + } + if(datafd != CURL_SOCKET_BAD) { + /* secondary tunnel not established */ + shutdown(datafd, SHUT_RDWR); + sclose(datafd); + } + } + if(got_exit_signal) + break; + } + + /* ---------------------------------------------------------- */ + + /* react to tunnel endpoint readable/writeable notifications */ + for(i = 0; i <= max_tunnel_idx; i++) { + size_t len; + if(clientfd[i] != CURL_SOCKET_BAD) { + len = sizeof(readclient[i]) - tos[i]; + if(len && FD_ISSET(clientfd[i], &input)) { + /* read from client */ + rc = sread(clientfd[i], &readclient[i][tos[i]], len); + if(rc <= 0) { + logmsg("[%s] got %zd, STOP READING client", data_or_ctrl(i), rc); + shutdown(clientfd[i], SHUT_RD); + poll_client_rd[i] = FALSE; + } + else { + logmsg("[%s] READ %zd bytes from client", data_or_ctrl(i), rc); + logmsg("[%s] READ \"%s\"", data_or_ctrl(i), + data_to_hex(&readclient[i][tos[i]], rc)); + tos[i] += rc; + } + } + } + if(serverfd[i] != CURL_SOCKET_BAD) { + len = sizeof(readserver[i])-toc[i]; + if(len && FD_ISSET(serverfd[i], &input)) { + /* read from server */ + rc = sread(serverfd[i], &readserver[i][toc[i]], len); + if(rc <= 0) { + logmsg("[%s] got %zd, STOP READING server", data_or_ctrl(i), rc); + shutdown(serverfd[i], SHUT_RD); + poll_server_rd[i] = FALSE; + } + else { + logmsg("[%s] READ %zd bytes from server", data_or_ctrl(i), rc); + logmsg("[%s] READ \"%s\"", data_or_ctrl(i), + data_to_hex(&readserver[i][toc[i]], rc)); + toc[i] += rc; + } + } + } + if(clientfd[i] != CURL_SOCKET_BAD) { + if(toc[i] && FD_ISSET(clientfd[i], &output)) { + /* write to client */ + rc = swrite(clientfd[i], readserver[i], toc[i]); + if(rc <= 0) { + logmsg("[%s] got %zd, STOP WRITING client", data_or_ctrl(i), rc); + shutdown(clientfd[i], SHUT_WR); + poll_client_wr[i] = FALSE; + tcp_fin_wr = TRUE; + } + else { + logmsg("[%s] SENT %zd bytes to client", data_or_ctrl(i), rc); + logmsg("[%s] SENT \"%s\"", data_or_ctrl(i), + data_to_hex(readserver[i], rc)); + if(toc[i] - rc) + memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc); + toc[i] -= rc; + } + } + } + if(serverfd[i] != CURL_SOCKET_BAD) { + if(tos[i] && FD_ISSET(serverfd[i], &output)) { + /* write to server */ + rc = swrite(serverfd[i], readclient[i], tos[i]); + if(rc <= 0) { + logmsg("[%s] got %zd, STOP WRITING server", data_or_ctrl(i), rc); + shutdown(serverfd[i], SHUT_WR); + poll_server_wr[i] = FALSE; + tcp_fin_wr = TRUE; + } + else { + logmsg("[%s] SENT %zd bytes to server", data_or_ctrl(i), rc); + logmsg("[%s] SENT \"%s\"", data_or_ctrl(i), + data_to_hex(readclient[i], rc)); + if(tos[i] - rc) + memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc); + tos[i] -= rc; + } + } + } + } + if(got_exit_signal) + break; + + /* ---------------------------------------------------------- */ + + /* endpoint read/write disabling, endpoint closing and tunnel teardown */ + for(i = 0; i <= max_tunnel_idx; i++) { + for(loop = 2; loop > 0; loop--) { + /* loop twice to satisfy condition interdependencies without + having to await select timeout or another socket event */ + if(clientfd[i] != CURL_SOCKET_BAD) { + if(poll_client_rd[i] && !poll_server_wr[i]) { + logmsg("[%s] DISABLED READING client", data_or_ctrl(i)); + shutdown(clientfd[i], SHUT_RD); + poll_client_rd[i] = FALSE; + } + if(poll_client_wr[i] && !poll_server_rd[i] && !toc[i]) { + logmsg("[%s] DISABLED WRITING client", data_or_ctrl(i)); + shutdown(clientfd[i], SHUT_WR); + poll_client_wr[i] = FALSE; + tcp_fin_wr = TRUE; + } + } + if(serverfd[i] != CURL_SOCKET_BAD) { + if(poll_server_rd[i] && !poll_client_wr[i]) { + logmsg("[%s] DISABLED READING server", data_or_ctrl(i)); + shutdown(serverfd[i], SHUT_RD); + poll_server_rd[i] = FALSE; + } + if(poll_server_wr[i] && !poll_client_rd[i] && !tos[i]) { + logmsg("[%s] DISABLED WRITING server", data_or_ctrl(i)); + shutdown(serverfd[i], SHUT_WR); + poll_server_wr[i] = FALSE; + tcp_fin_wr = TRUE; + } + } + } + } + + if(tcp_fin_wr) + /* allow kernel to place FIN bit packet on the wire */ + wait_ms(250); + + /* socket clearing */ + for(i = 0; i <= max_tunnel_idx; i++) { + for(loop = 2; loop > 0; loop--) { + if(clientfd[i] != CURL_SOCKET_BAD) { + if(!poll_client_wr[i] && !poll_client_rd[i]) { + logmsg("[%s] CLOSING client socket", data_or_ctrl(i)); + sclose(clientfd[i]); + clientfd[i] = CURL_SOCKET_BAD; + if(serverfd[i] == CURL_SOCKET_BAD) { + logmsg("[%s] ENDING", data_or_ctrl(i)); + if(i == DATA) + secondary = FALSE; + else + primary = FALSE; + } + } + } + if(serverfd[i] != CURL_SOCKET_BAD) { + if(!poll_server_wr[i] && !poll_server_rd[i]) { + logmsg("[%s] CLOSING server socket", data_or_ctrl(i)); + sclose(serverfd[i]); + serverfd[i] = CURL_SOCKET_BAD; + if(clientfd[i] == CURL_SOCKET_BAD) { + logmsg("[%s] ENDING", data_or_ctrl(i)); + if(i == DATA) + secondary = FALSE; + else + primary = FALSE; + } + } + } + } + } + + /* ---------------------------------------------------------- */ + + max_tunnel_idx = secondary ? DATA : CTRL; + + if(!primary) + /* exit loop upon primary tunnel teardown */ + break; + + } /* (rc > 0) */ + else { + timeout_count++; + if(timeout_count > 5) { + logmsg("CONNECT proxy timeout after %d idle seconds!", timeout_count); + break; + } + } + } + +http_connect_cleanup: + + for(i = DATA; i >= CTRL; i--) { + if(serverfd[i] != CURL_SOCKET_BAD) { + logmsg("[%s] CLOSING server socket (cleanup)", data_or_ctrl(i)); + shutdown(serverfd[i], SHUT_RDWR); + sclose(serverfd[i]); + } + if(clientfd[i] != CURL_SOCKET_BAD) { + logmsg("[%s] CLOSING client socket (cleanup)", data_or_ctrl(i)); + shutdown(clientfd[i], SHUT_RDWR); + sclose(clientfd[i]); + } + if((serverfd[i] != CURL_SOCKET_BAD) || + (clientfd[i] != CURL_SOCKET_BAD)) { + logmsg("[%s] ABORTING", data_or_ctrl(i)); + } + } + + *infdp = CURL_SOCKET_BAD; +} + +static void http2(struct httprequest *req) +{ + (void)req; + logmsg("switched to http2"); + /* left to implement */ +} + + +/* returns a socket handle, or 0 if there are no more waiting sockets, + or < 0 if there was an error */ +static curl_socket_t accept_connection(curl_socket_t sock) +{ + curl_socket_t msgsock = CURL_SOCKET_BAD; + int error; + int flag = 1; + + if(MAX_SOCKETS == num_sockets) { + logmsg("Too many open sockets!"); + return CURL_SOCKET_BAD; + } + + msgsock = accept(sock, NULL, NULL); + + if(got_exit_signal) { + if(CURL_SOCKET_BAD != msgsock) + sclose(msgsock); + return CURL_SOCKET_BAD; + } + + if(CURL_SOCKET_BAD == msgsock) { + error = SOCKERRNO; + if(EAGAIN == error || EWOULDBLOCK == error) { + /* nothing to accept */ + return 0; + } + logmsg("MAJOR ERROR: accept() failed with error: (%d) %s", + error, strerror(error)); + return CURL_SOCKET_BAD; + } + + if(0 != curlx_nonblock(msgsock, TRUE)) { + error = SOCKERRNO; + logmsg("curlx_nonblock failed with error: (%d) %s", + error, strerror(error)); + sclose(msgsock); + return CURL_SOCKET_BAD; + } + + if(0 != setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE, + (void *)&flag, sizeof(flag))) { + error = SOCKERRNO; + logmsg("setsockopt(SO_KEEPALIVE) failed with error: (%d) %s", + error, strerror(error)); + sclose(msgsock); + return CURL_SOCKET_BAD; + } + + /* + ** As soon as this server accepts a connection from the test harness it + ** must set the server logs advisor read lock to indicate that server + ** logs should not be read until this lock is removed by this server. + */ + + if(!serverlogslocked) + set_advisor_read_lock(SERVERLOGS_LOCK); + serverlogslocked += 1; + + logmsg("====> Client connect"); + + all_sockets[num_sockets] = msgsock; + num_sockets += 1; + +#ifdef TCP_NODELAY + if(socket_domain_is_ip()) { + /* + * Disable the Nagle algorithm to make it easier to send out a large + * response in many small segments to torture the clients more. + */ + if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY, + (void *)&flag, sizeof(flag))) + logmsg("====> TCP_NODELAY failed"); + } +#endif + + return msgsock; +} + +/* returns 1 if the connection should be serviced again immediately, 0 if there + is no data waiting, or < 0 if it should be closed */ +static int service_connection(curl_socket_t msgsock, struct httprequest *req, + curl_socket_t listensock, + const char *connecthost) +{ + if(got_exit_signal) + return -1; + + while(!req->done_processing) { + int rc = get_request(msgsock, req); + if(rc <= 0) { + /* Nothing further to read now (possibly because the socket was closed */ + return rc; + } + } + + if(prevbounce) { + /* bounce treatment requested */ + if((req->testno == prevtestno) && + (req->partno == prevpartno)) { + req->partno++; + logmsg("BOUNCE part number to %ld", req->partno); + } + else { + prevbounce = FALSE; + prevtestno = -1; + prevpartno = -1; + } + } + + send_doc(msgsock, req); + if(got_exit_signal) + return -1; + + if(req->testno < 0) { + logmsg("special request received, no persistency"); + return -1; + } + if(!req->open) { + logmsg("instructed to close connection after server-reply"); + return -1; + } + + if(req->connect_request) { + /* a CONNECT request, setup and talk the tunnel */ + if(!is_proxy) { + logmsg("received CONNECT but isn't running as proxy!"); + return 1; + } + else { + http_connect(&msgsock, listensock, connecthost, req->connect_port); + return -1; + } + } + + if(req->upgrade_request) { + /* an upgrade request, switch to http2 here */ + http2(req); + return -1; + } + + /* if we got a CONNECT, loop and get another request as well! */ + + if(req->open) { + logmsg("=> persistant connection request ended, awaits new request\n"); + return 1; + } + + return -1; +} + +int main(int argc, char *argv[]) +{ + srvr_sockaddr_union_t me; + curl_socket_t sock = CURL_SOCKET_BAD; + int wrotepidfile = 0; + int flag; + unsigned short port = DEFAULT_PORT; +#ifdef USE_UNIX_SOCKETS + const char *unix_socket = NULL; + bool unlink_socket = false; +#endif + char *pidname= (char *)".http.pid"; + struct httprequest req; + int rc = 0; + int error; + int arg=1; + long pid; + const char *connecthost = "127.0.0.1"; + const char *socket_type = "IPv4"; + char port_str[11]; + const char *location_str = port_str; + + /* a default CONNECT port is basically pointless but still ... */ + size_t socket_idx; + + memset(&req, 0, sizeof(req)); + + while(argc>arg) { + if(!strcmp("--version", argv[arg])) { + puts("sws IPv4" +#ifdef ENABLE_IPV6 + "/IPv6" +#endif +#ifdef USE_UNIX_SOCKETS + "/unix" +#endif + ); + return 0; + } + else if(!strcmp("--pidfile", argv[arg])) { + arg++; + if(argc>arg) + pidname = argv[arg++]; + } + else if(!strcmp("--logfile", argv[arg])) { + arg++; + if(argc>arg) + serverlogfile = argv[arg++]; + } + else if(!strcmp("--gopher", argv[arg])) { + arg++; + use_gopher = TRUE; + end_of_headers = "\r\n"; /* gopher style is much simpler */ + } + else if(!strcmp("--ipv4", argv[arg])) { + socket_type = "IPv4"; + socket_domain = AF_INET; + location_str = port_str; + arg++; + } + else if(!strcmp("--ipv6", argv[arg])) { +#ifdef ENABLE_IPV6 + socket_type = "IPv6"; + socket_domain = AF_INET6; + location_str = port_str; +#endif + arg++; + } + else if(!strcmp("--unix-socket", argv[arg])) { + arg++; + if(argc>arg) { +#ifdef USE_UNIX_SOCKETS + unix_socket = argv[arg]; + if(strlen(unix_socket) >= sizeof(me.sau.sun_path)) { + fprintf(stderr, "sws: socket path must be shorter than %zu chars\n", + sizeof(me.sau.sun_path)); + return 0; + } + socket_type = "unix"; + socket_domain = AF_UNIX; + location_str = unix_socket; +#endif + arg++; + } + } + else if(!strcmp("--port", argv[arg])) { + arg++; + if(argc>arg) { + char *endptr; + unsigned long ulnum = strtoul(argv[arg], &endptr, 10); + if((endptr != argv[arg] + strlen(argv[arg])) || + (ulnum < 1025UL) || (ulnum > 65535UL)) { + fprintf(stderr, "sws: invalid --port argument (%s)\n", + argv[arg]); + return 0; + } + port = curlx_ultous(ulnum); + arg++; + } + } + else if(!strcmp("--srcdir", argv[arg])) { + arg++; + if(argc>arg) { + path = argv[arg]; + arg++; + } + } + else if(!strcmp("--connect", argv[arg])) { + /* The connect host IP number that the proxy will connect to no matter + what the client asks for, but also use this as a hint that we run as + a proxy and do a few different internal choices */ + arg++; + if(argc>arg) { + connecthost = argv[arg]; + arg++; + is_proxy = TRUE; + logmsg("Run as proxy, CONNECT to host %s", connecthost); + } + } + else { + puts("Usage: sws [option]\n" + " --version\n" + " --logfile [file]\n" + " --pidfile [file]\n" + " --ipv4\n" + " --ipv6\n" + " --unix-socket [file]\n" + " --port [port]\n" + " --srcdir [path]\n" + " --connect [ip4-addr]\n" + " --gopher"); + return 0; + } + } + + snprintf(port_str, sizeof(port_str), "port %hu", port); + +#ifdef WIN32 + win32_init(); + atexit(win32_cleanup); +#endif + + install_signal_handlers(); + + pid = (long)getpid(); + + sock = socket(socket_domain, SOCK_STREAM, 0); + + all_sockets[0] = sock; + num_sockets = 1; + + if(CURL_SOCKET_BAD == sock) { + error = SOCKERRNO; + logmsg("Error creating socket: (%d) %s", + error, strerror(error)); + goto sws_cleanup; + } + + flag = 1; + if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (void *)&flag, sizeof(flag))) { + error = SOCKERRNO; + logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s", + error, strerror(error)); + goto sws_cleanup; + } + if(0 != curlx_nonblock(sock, TRUE)) { + error = SOCKERRNO; + logmsg("curlx_nonblock failed with error: (%d) %s", + error, strerror(error)); + goto sws_cleanup; + } + + switch(socket_domain) { + case AF_INET: + memset(&me.sa4, 0, sizeof(me.sa4)); + me.sa4.sin_family = AF_INET; + me.sa4.sin_addr.s_addr = INADDR_ANY; + me.sa4.sin_port = htons(port); + rc = bind(sock, &me.sa, sizeof(me.sa4)); + break; +#ifdef ENABLE_IPV6 + case AF_INET6: + memset(&me.sa6, 0, sizeof(me.sa6)); + me.sa6.sin6_family = AF_INET6; + me.sa6.sin6_addr = in6addr_any; + me.sa6.sin6_port = htons(port); + rc = bind(sock, &me.sa, sizeof(me.sa6)); + break; +#endif /* ENABLE_IPV6 */ +#ifdef USE_UNIX_SOCKETS + case AF_UNIX: + memset(&me.sau, 0, sizeof(me.sau)); + me.sau.sun_family = AF_UNIX; + strncpy(me.sau.sun_path, unix_socket, sizeof(me.sau.sun_path)); + rc = bind(sock, &me.sa, sizeof(me.sau)); + if(0 != rc && errno == EADDRINUSE) { + struct stat statbuf; + /* socket already exists. Perhaps it is stale? */ + int unixfd = socket(AF_UNIX, SOCK_STREAM, 0); + if(CURL_SOCKET_BAD == unixfd) { + error = SOCKERRNO; + logmsg("Error binding socket, failed to create socket at %s: (%d) %s", + unix_socket, error, strerror(error)); + goto sws_cleanup; + } + /* check whether the server is alive */ + rc = connect(unixfd, &me.sa, sizeof(me.sau)); + error = errno; + close(unixfd); + if(ECONNREFUSED != error) { + logmsg("Error binding socket, failed to connect to %s: (%d) %s", + unix_socket, error, strerror(error)); + goto sws_cleanup; + } + /* socket server is not alive, now check if it was actually a socket. + * Systems which have Unix sockets will also have lstat */ + rc = lstat(unix_socket, &statbuf); + if(0 != rc) { + logmsg("Error binding socket, failed to stat %s: (%d) %s", + unix_socket, errno, strerror(errno)); + goto sws_cleanup; + } + if((statbuf.st_mode & S_IFSOCK) != S_IFSOCK) { + logmsg("Error binding socket, failed to stat %s: (%d) %s", + unix_socket, error, strerror(error)); + goto sws_cleanup; + } + /* dead socket, cleanup and retry bind */ + rc = unlink(unix_socket); + if(0 != rc) { + logmsg("Error binding socket, failed to unlink %s: (%d) %s", + unix_socket, errno, strerror(errno)); + goto sws_cleanup; + } + /* stale socket is gone, retry bind */ + rc = bind(sock, &me.sa, sizeof(me.sau)); + } + break; +#endif /* USE_UNIX_SOCKETS */ + } + if(0 != rc) { + error = SOCKERRNO; + logmsg("Error binding socket on %s: (%d) %s", + location_str, error, strerror(error)); + goto sws_cleanup; + } + + logmsg("Running %s %s version on %s", + use_gopher?"GOPHER":"HTTP", socket_type, location_str); + + /* start accepting connections */ + rc = listen(sock, 5); + if(0 != rc) { + error = SOCKERRNO; + logmsg("listen() failed with error: (%d) %s", + error, strerror(error)); + goto sws_cleanup; + } + +#ifdef USE_UNIX_SOCKETS + /* listen succeeds, so let's assume a valid listening Unix socket */ + unlink_socket = true; +#endif + + /* + ** As soon as this server writes its pid file the test harness will + ** attempt to connect to this server and initiate its verification. + */ + + wrotepidfile = write_pidfile(pidname); + if(!wrotepidfile) + goto sws_cleanup; + + /* initialization of httprequest struct is done before get_request(), but + the pipelining struct field must be initialized previously to FALSE + every time a new connection arrives. */ + + req.pipelining = FALSE; + init_httprequest(&req); + + for(;;) { + fd_set input; + fd_set output; + struct timeval timeout = {0, 250000L}; /* 250 ms */ + curl_socket_t maxfd = (curl_socket_t)-1; + + /* Clear out closed sockets */ + for(socket_idx = num_sockets - 1; socket_idx >= 1; --socket_idx) { + if(CURL_SOCKET_BAD == all_sockets[socket_idx]) { + char* dst = (char *) (all_sockets + socket_idx); + char* src = (char *) (all_sockets + socket_idx + 1); + char* end = (char *) (all_sockets + num_sockets); + memmove(dst, src, end - src); + num_sockets -= 1; + } + } + + if(got_exit_signal) + goto sws_cleanup; + + /* Set up for select*/ + FD_ZERO(&input); + FD_ZERO(&output); + + for(socket_idx = 0; socket_idx < num_sockets; ++socket_idx) { + /* Listen on all sockets */ + FD_SET(all_sockets[socket_idx], &input); + if(all_sockets[socket_idx] > maxfd) + maxfd = all_sockets[socket_idx]; + } + + if(got_exit_signal) + goto sws_cleanup; + + rc = select((int)maxfd + 1, &input, &output, NULL, &timeout); + if(rc < 0) { + error = SOCKERRNO; + logmsg("select() failed with error: (%d) %s", + error, strerror(error)); + goto sws_cleanup; + } + + if(got_exit_signal) + goto sws_cleanup; + + if(rc == 0) { + /* Timed out - try again*/ + continue; + } + + /* Check if the listening socket is ready to accept */ + if(FD_ISSET(all_sockets[0], &input)) { + /* Service all queued connections */ + curl_socket_t msgsock; + do { + msgsock = accept_connection(sock); + logmsg("accept_connection %d returned %d", sock, msgsock); + if(CURL_SOCKET_BAD == msgsock) + goto sws_cleanup; + } while(msgsock > 0); + } + + /* Service all connections that are ready */ + for(socket_idx = 1; socket_idx < num_sockets; ++socket_idx) { + if(FD_ISSET(all_sockets[socket_idx], &input)) { + if(got_exit_signal) + goto sws_cleanup; + + /* Service this connection until it has nothing available */ + do { + rc = service_connection(all_sockets[socket_idx], &req, sock, + connecthost); + if(got_exit_signal) + goto sws_cleanup; + + if(rc < 0) { + logmsg("====> Client disconnect %d", req.connmon); + + if(req.connmon) { + const char *keepopen="[DISCONNECT]\n"; + storerequest((char *)keepopen, strlen(keepopen)); + } + + if(!req.open) + /* When instructed to close connection after server-reply we + wait a very small amount of time before doing so. If this + is not done client might get an ECONNRESET before reading + a single byte of server-reply. */ + wait_ms(50); + + if(all_sockets[socket_idx] != CURL_SOCKET_BAD) { + sclose(all_sockets[socket_idx]); + all_sockets[socket_idx] = CURL_SOCKET_BAD; + } + + serverlogslocked -= 1; + if(!serverlogslocked) + clear_advisor_read_lock(SERVERLOGS_LOCK); + + if(req.testno == DOCNUMBER_QUIT) + goto sws_cleanup; + } + + /* Reset the request, unless we're still in the middle of reading */ + if(rc != 0) + init_httprequest(&req); + } while(rc > 0); + } + } + + if(got_exit_signal) + goto sws_cleanup; + } + +sws_cleanup: + + for(socket_idx = 1; socket_idx < num_sockets; ++socket_idx) + if((all_sockets[socket_idx] != sock) && + (all_sockets[socket_idx] != CURL_SOCKET_BAD)) + sclose(all_sockets[socket_idx]); + + if(sock != CURL_SOCKET_BAD) + sclose(sock); + +#ifdef USE_UNIX_SOCKETS + if(unlink_socket && socket_domain == AF_UNIX) { + rc = unlink(unix_socket); + logmsg("unlink(%s) = %d (%s)", unix_socket, rc, strerror(rc)); + } +#endif + + if(got_exit_signal) + logmsg("signalled to die"); + + if(wrotepidfile) + unlink(pidname); + + if(serverlogslocked) { + serverlogslocked = 0; + clear_advisor_read_lock(SERVERLOGS_LOCK); + } + + restore_signal_handlers(); + + if(got_exit_signal) { + logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)", + socket_type, location_str, pid, exit_signal); + /* + * To properly set the return status of the process we + * must raise the same signal SIGINT or SIGTERM that we + * caught and let the old handler take care of it. + */ + raise(exit_signal); + } + + logmsg("========> sws quits"); + return 0; +} + diff --git a/deps/curl-7.51.0/tests/server/testpart.c b/deps/curl-7.51.0/tests/server/testpart.c new file mode 100644 index 0000000000..f3a70c7cfb --- /dev/null +++ b/deps/curl-7.51.0/tests/server/testpart.c @@ -0,0 +1,51 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "server_setup.h" + +#include "getpart.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include + +/* include memdebug.h last */ +#include "memdebug.h" + +int main(int argc, char **argv) +{ + int rc; + char *part; + size_t partlen, i; + + if(argc< 3) { + printf("./testpart main sub\n"); + } + else { + rc = getpart(&part, &partlen, argv[1], argv[2], stdin); + if(rc) + return rc; + for(i = 0; i < partlen; i++) + printf("%c", part[i]); + free(part); + } + return 0; +} + diff --git a/deps/curl-7.51.0/tests/server/tftp.h b/deps/curl-7.51.0/tests/server/tftp.h new file mode 100644 index 0000000000..3cdd6e6d0e --- /dev/null +++ b/deps/curl-7.51.0/tests/server/tftp.h @@ -0,0 +1,61 @@ +#ifndef HEADER_CURL_SERVER_TFTP_H +#define HEADER_CURL_SERVER_TFTP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "server_setup.h" + +/* This file is a rewrite/clone of the arpa/tftp.h file for systems without + it. */ + +#define SEGSIZE 512 /* data segment size */ + +#if defined(__GNUC__) && ((__GNUC__ >= 3) || \ + ((__GNUC__ == 2) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 7))) +# define PACKED_STRUCT __attribute__((__packed__)) +#else +# define PACKED_STRUCT /*NOTHING*/ +#endif + +/* Using a packed struct as binary in a program is begging for problems, but + the tftpd server was written like this so we have this struct here to make + things build. */ + +struct tftphdr { + short th_opcode; /* packet type */ + unsigned short th_block; /* all sorts of things */ + char th_data[1]; /* data or error string */ +} PACKED_STRUCT; + +#define th_stuff th_block +#define th_code th_block +#define th_msg th_data + +#define EUNDEF 0 +#define ENOTFOUND 1 +#define EACCESS 2 +#define ENOSPACE 3 +#define EBADOP 4 +#define EBADID 5 +#define EEXISTS 6 +#define ENOUSER 7 + +#endif /* HEADER_CURL_SERVER_TFTP_H */ diff --git a/deps/curl-7.51.0/tests/server/tftpd.c b/deps/curl-7.51.0/tests/server/tftpd.c new file mode 100644 index 0000000000..afc0884e0d --- /dev/null +++ b/deps/curl-7.51.0/tests/server/tftpd.c @@ -0,0 +1,1431 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * + * Trivial file transfer protocol server. + * + * This code includes many modifications by Jim Guyton + * + * This source file was started based on netkit-tftpd 0.17 + * Heavily modified for curl's test suite + */ + +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "server_setup.h" + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_ARPA_TFTP_H +#include +#else +#include "tftp.h" +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_SYS_FILIO_H +/* FIONREAD on Solaris 7 */ +#include +#endif + +#include + +#ifdef HAVE_PWD_H +#include +#endif + +#define ENABLE_CURLX_PRINTF +/* make the curlx header define all printf() functions to use the curlx_* + versions instead */ +#include "curlx.h" /* from the private lib dir */ +#include "getpart.h" +#include "util.h" +#include "server_sockaddr.h" + +/* include memdebug.h last */ +#include "memdebug.h" + +/***************************************************************************** +* STRUCT DECLARATIONS AND DEFINES * +*****************************************************************************/ + +#ifndef PKTSIZE +#define PKTSIZE (SEGSIZE + 4) /* SEGSIZE defined in arpa/tftp.h */ +#endif + +struct testcase { + char *buffer; /* holds the file data to send to the client */ + size_t bufsize; /* size of the data in buffer */ + char *rptr; /* read pointer into the buffer */ + size_t rcount; /* amount of data left to read of the file */ + long testno; /* test case number */ + int ofile; /* file descriptor for output file when uploading to us */ + + int writedelay; /* number of seconds between each packet */ +}; + +struct formats { + const char *f_mode; + int f_convert; +}; + +struct errmsg { + int e_code; + const char *e_msg; +}; + +typedef union { + struct tftphdr hdr; + char storage[PKTSIZE]; +} tftphdr_storage_t; + +/* + * bf.counter values in range [-1 .. SEGSIZE] represents size of data in the + * bf.buf buffer. Additionally it can also hold flags BF_ALLOC or BF_FREE. + */ + +struct bf { + int counter; /* size of data in buffer, or flag */ + tftphdr_storage_t buf; /* room for data packet */ +}; + +#define BF_ALLOC -3 /* alloc'd but not yet filled */ +#define BF_FREE -2 /* free */ + +#define opcode_RRQ 1 +#define opcode_WRQ 2 +#define opcode_DATA 3 +#define opcode_ACK 4 +#define opcode_ERROR 5 + +#define TIMEOUT 5 + +#undef MIN +#define MIN(x,y) ((x)<(y)?(x):(y)) + +#ifndef DEFAULT_LOGFILE +#define DEFAULT_LOGFILE "log/tftpd.log" +#endif + +#define REQUEST_DUMP "log/server.input" + +#define DEFAULT_PORT 8999 /* UDP */ + +/***************************************************************************** +* GLOBAL VARIABLES * +*****************************************************************************/ + +static struct errmsg errmsgs[] = { + { EUNDEF, "Undefined error code" }, + { ENOTFOUND, "File not found" }, + { EACCESS, "Access violation" }, + { ENOSPACE, "Disk full or allocation exceeded" }, + { EBADOP, "Illegal TFTP operation" }, + { EBADID, "Unknown transfer ID" }, + { EEXISTS, "File already exists" }, + { ENOUSER, "No such user" }, + { -1, 0 } +}; + +static struct formats formata[] = { + { "netascii", 1 }, + { "octet", 0 }, + { NULL, 0 } +}; + +static struct bf bfs[2]; + +static int nextone; /* index of next buffer to use */ +static int current; /* index of buffer in use */ + + /* control flags for crlf conversions */ +static int newline = 0; /* fillbuf: in middle of newline expansion */ +static int prevchar = -1; /* putbuf: previous char (cr check) */ + +static tftphdr_storage_t buf; +static tftphdr_storage_t ackbuf; + +static srvr_sockaddr_union_t from; +static curl_socklen_t fromlen; + +static curl_socket_t peer = CURL_SOCKET_BAD; + +static int timeout; +static int maxtimeout = 5 * TIMEOUT; + +#ifdef ENABLE_IPV6 +static bool use_ipv6 = FALSE; +#endif +static const char *ipv_inuse = "IPv4"; + +const char *serverlogfile = DEFAULT_LOGFILE; +static char *pidname= (char *)".tftpd.pid"; +static int serverlogslocked = 0; +static int wrotepidfile = 0; + +#ifdef HAVE_SIGSETJMP +static sigjmp_buf timeoutbuf; +#endif + +#if defined(HAVE_ALARM) && defined(SIGALRM) +static int rexmtval = TIMEOUT; +#endif + +/* do-nothing macro replacement for systems which lack siginterrupt() */ + +#ifndef HAVE_SIGINTERRUPT +#define siginterrupt(x,y) do {} while(0) +#endif + +/* vars used to keep around previous signal handlers */ + +typedef RETSIGTYPE (*SIGHANDLER_T)(int); + +#ifdef SIGHUP +static SIGHANDLER_T old_sighup_handler = SIG_ERR; +#endif + +#ifdef SIGPIPE +static SIGHANDLER_T old_sigpipe_handler = SIG_ERR; +#endif + +#ifdef SIGINT +static SIGHANDLER_T old_sigint_handler = SIG_ERR; +#endif + +#ifdef SIGTERM +static SIGHANDLER_T old_sigterm_handler = SIG_ERR; +#endif + +#if defined(SIGBREAK) && defined(WIN32) +static SIGHANDLER_T old_sigbreak_handler = SIG_ERR; +#endif + +/* var which if set indicates that the program should finish execution */ + +SIG_ATOMIC_T got_exit_signal = 0; + +/* if next is set indicates the first signal handled in exit_signal_handler */ + +static volatile int exit_signal = 0; + +/***************************************************************************** +* FUNCTION PROTOTYPES * +*****************************************************************************/ + +static struct tftphdr *rw_init(int); + +static struct tftphdr *w_init(void); + +static struct tftphdr *r_init(void); + +static void read_ahead(struct testcase *test, int convert); + +static ssize_t write_behind(struct testcase *test, int convert); + +static int synchnet(curl_socket_t); + +static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size); + +static int validate_access(struct testcase *test, const char *fname, int mode); + +static void sendtftp(struct testcase *test, struct formats *pf); + +static void recvtftp(struct testcase *test, struct formats *pf); + +static void nak(int error); + +#if defined(HAVE_ALARM) && defined(SIGALRM) + +static void mysignal(int sig, void (*handler)(int)); + +static void timer(int signum); + +static void justtimeout(int signum); + +#endif /* HAVE_ALARM && SIGALRM */ + +static RETSIGTYPE exit_signal_handler(int signum); + +static void install_signal_handlers(void); + +static void restore_signal_handlers(void); + +/***************************************************************************** +* FUNCTION IMPLEMENTATIONS * +*****************************************************************************/ + +#if defined(HAVE_ALARM) && defined(SIGALRM) + +/* + * Like signal(), but with well-defined semantics. + */ +static void mysignal(int sig, void (*handler)(int)) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; + sigaction(sig, &sa, NULL); +} + +static void timer(int signum) +{ + (void)signum; + + logmsg("alarm!"); + + timeout += rexmtval; + if(timeout >= maxtimeout) { + if(wrotepidfile) { + wrotepidfile = 0; + unlink(pidname); + } + if(serverlogslocked) { + serverlogslocked = 0; + clear_advisor_read_lock(SERVERLOGS_LOCK); + } + exit(1); + } +#ifdef HAVE_SIGSETJMP + siglongjmp(timeoutbuf, 1); +#endif +} + +static void justtimeout(int signum) +{ + (void)signum; +} + +#endif /* HAVE_ALARM && SIGALRM */ + +/* signal handler that will be triggered to indicate that the program + should finish its execution in a controlled manner as soon as possible. + The first time this is called it will set got_exit_signal to one and + store in exit_signal the signal that triggered its execution. */ + +static RETSIGTYPE exit_signal_handler(int signum) +{ + int old_errno = errno; + if(got_exit_signal == 0) { + got_exit_signal = 1; + exit_signal = signum; + } + (void)signal(signum, exit_signal_handler); + errno = old_errno; +} + +static void install_signal_handlers(void) +{ +#ifdef SIGHUP + /* ignore SIGHUP signal */ + if((old_sighup_handler = signal(SIGHUP, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGHUP handler: %s", strerror(errno)); +#endif +#ifdef SIGPIPE + /* ignore SIGPIPE signal */ + if((old_sigpipe_handler = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) + logmsg("cannot install SIGPIPE handler: %s", strerror(errno)); +#endif +#ifdef SIGINT + /* handle SIGINT signal with our exit_signal_handler */ + if((old_sigint_handler = signal(SIGINT, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGINT handler: %s", strerror(errno)); + else + siginterrupt(SIGINT, 1); +#endif +#ifdef SIGTERM + /* handle SIGTERM signal with our exit_signal_handler */ + if((old_sigterm_handler = signal(SIGTERM, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGTERM handler: %s", strerror(errno)); + else + siginterrupt(SIGTERM, 1); +#endif +#if defined(SIGBREAK) && defined(WIN32) + /* handle SIGBREAK signal with our exit_signal_handler */ + if((old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler)) == SIG_ERR) + logmsg("cannot install SIGBREAK handler: %s", strerror(errno)); + else + siginterrupt(SIGBREAK, 1); +#endif +} + +static void restore_signal_handlers(void) +{ +#ifdef SIGHUP + if(SIG_ERR != old_sighup_handler) + (void)signal(SIGHUP, old_sighup_handler); +#endif +#ifdef SIGPIPE + if(SIG_ERR != old_sigpipe_handler) + (void)signal(SIGPIPE, old_sigpipe_handler); +#endif +#ifdef SIGINT + if(SIG_ERR != old_sigint_handler) + (void)signal(SIGINT, old_sigint_handler); +#endif +#ifdef SIGTERM + if(SIG_ERR != old_sigterm_handler) + (void)signal(SIGTERM, old_sigterm_handler); +#endif +#if defined(SIGBREAK) && defined(WIN32) + if(SIG_ERR != old_sigbreak_handler) + (void)signal(SIGBREAK, old_sigbreak_handler); +#endif +} + +/* + * init for either read-ahead or write-behind. + * zero for write-behind, one for read-head. + */ +static struct tftphdr *rw_init(int x) +{ + newline = 0; /* init crlf flag */ + prevchar = -1; + bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ + current = 0; + bfs[1].counter = BF_FREE; + nextone = x; /* ahead or behind? */ + return &bfs[0].buf.hdr; +} + +static struct tftphdr *w_init(void) +{ + return rw_init(0); /* write-behind */ +} + +static struct tftphdr *r_init(void) +{ + return rw_init(1); /* read-ahead */ +} + +/* Have emptied current buffer by sending to net and getting ack. + Free it and return next buffer filled with data. + */ +static int readit(struct testcase *test, struct tftphdr **dpp, + int convert /* if true, convert to ascii */) +{ + struct bf *b; + + bfs[current].counter = BF_FREE; /* free old one */ + current = !current; /* "incr" current */ + + b = &bfs[current]; /* look at new buffer */ + if(b->counter == BF_FREE) /* if it's empty */ + read_ahead(test, convert); /* fill it */ + + *dpp = &b->buf.hdr; /* set caller's ptr */ + return b->counter; +} + +/* + * fill the input buffer, doing ascii conversions if requested + * conversions are lf -> cr, lf and cr -> cr, nul + */ +static void read_ahead(struct testcase *test, + int convert /* if true, convert to ascii */) +{ + int i; + char *p; + int c; + struct bf *b; + struct tftphdr *dp; + + b = &bfs[nextone]; /* look at "next" buffer */ + if(b->counter != BF_FREE) /* nop if not free */ + return; + nextone = !nextone; /* "incr" next buffer ptr */ + + dp = &b->buf.hdr; + + if(convert == 0) { + /* The former file reading code did this: + b->counter = read(fileno(file), dp->th_data, SEGSIZE); */ + size_t copy_n = MIN(SEGSIZE, test->rcount); + memcpy(dp->th_data, test->rptr, copy_n); + + /* decrease amount, advance pointer */ + test->rcount -= copy_n; + test->rptr += copy_n; + b->counter = (int)copy_n; + return; + } + + p = dp->th_data; + for(i = 0 ; i < SEGSIZE; i++) { + if(newline) { + if(prevchar == '\n') + c = '\n'; /* lf to cr,lf */ + else + c = '\0'; /* cr to cr,nul */ + newline = 0; + } + else { + if(test->rcount) { + c=test->rptr[0]; + test->rptr++; + test->rcount--; + } + else + break; + if(c == '\n' || c == '\r') { + prevchar = c; + c = '\r'; + newline = 1; + } + } + *p++ = (char)c; + } + b->counter = (int)(p - dp->th_data); +} + +/* Update count associated with the buffer, get new buffer from the queue. + Calls write_behind only if next buffer not available. + */ +static int writeit(struct testcase *test, struct tftphdr * volatile *dpp, + int ct, int convert) +{ + bfs[current].counter = ct; /* set size of data to write */ + current = !current; /* switch to other buffer */ + if(bfs[current].counter != BF_FREE) /* if not free */ + write_behind(test, convert); /* flush it */ + bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ + *dpp = &bfs[current].buf.hdr; + return ct; /* this is a lie of course */ +} + +/* + * Output a buffer to a file, converting from netascii if requested. + * CR, NUL -> CR and CR, LF => LF. + * Note spec is undefined if we get CR as last byte of file or a + * CR followed by anything else. In this case we leave it alone. + */ +static ssize_t write_behind(struct testcase *test, int convert) +{ + char *writebuf; + int count; + int ct; + char *p; + int c; /* current character */ + struct bf *b; + struct tftphdr *dp; + + b = &bfs[nextone]; + if(b->counter < -1) /* anything to flush? */ + return 0; /* just nop if nothing to do */ + + if(!test->ofile) { + char outfile[256]; + snprintf(outfile, sizeof(outfile), "log/upload.%ld", test->testno); +#ifdef WIN32 + test->ofile=open(outfile, O_CREAT|O_RDWR|O_BINARY, 0777); +#else + test->ofile=open(outfile, O_CREAT|O_RDWR, 0777); +#endif + if(test->ofile == -1) { + logmsg("Couldn't create and/or open file %s for upload!", outfile); + return -1; /* failure! */ + } + } + + count = b->counter; /* remember byte count */ + b->counter = BF_FREE; /* reset flag */ + dp = &b->buf.hdr; + nextone = !nextone; /* incr for next time */ + writebuf = dp->th_data; + + if(count <= 0) + return -1; /* nak logic? */ + + if(convert == 0) + return write(test->ofile, writebuf, count); + + p = writebuf; + ct = count; + while(ct--) { /* loop over the buffer */ + c = *p++; /* pick up a character */ + if(prevchar == '\r') { /* if prev char was cr */ + if(c == '\n') /* if have cr,lf then just */ + lseek(test->ofile, -1, SEEK_CUR); /* smash lf on top of the cr */ + else + if(c == '\0') /* if have cr,nul then */ + goto skipit; /* just skip over the putc */ + /* else just fall through and allow it */ + } + /* formerly + putc(c, file); */ + if(1 != write(test->ofile, &c, 1)) + break; + skipit: + prevchar = c; + } + return count; +} + +/* When an error has occurred, it is possible that the two sides are out of + * synch. Ie: that what I think is the other side's response to packet N is + * really their response to packet N-1. + * + * So, to try to prevent that, we flush all the input queued up for us on the + * network connection on our host. + * + * We return the number of packets we flushed (mostly for reporting when trace + * is active). + */ + +static int synchnet(curl_socket_t f /* socket to flush */) +{ + +#if defined(HAVE_IOCTLSOCKET) + unsigned long i; +#else + int i; +#endif + int j = 0; + char rbuf[PKTSIZE]; + srvr_sockaddr_union_t fromaddr; + curl_socklen_t fromaddrlen; + + for(;;) { +#if defined(HAVE_IOCTLSOCKET) + (void) ioctlsocket(f, FIONREAD, &i); +#else + (void) ioctl(f, FIONREAD, &i); +#endif + if(i) { + j++; +#ifdef ENABLE_IPV6 + if(!use_ipv6) +#endif + fromaddrlen = sizeof(fromaddr.sa4); +#ifdef ENABLE_IPV6 + else + fromaddrlen = sizeof(fromaddr.sa6); +#endif + (void) recvfrom(f, rbuf, sizeof(rbuf), 0, + &fromaddr.sa, &fromaddrlen); + } + else + break; + } + return j; +} + +int main(int argc, char **argv) +{ + srvr_sockaddr_union_t me; + struct tftphdr *tp; + ssize_t n = 0; + int arg = 1; + unsigned short port = DEFAULT_PORT; + curl_socket_t sock = CURL_SOCKET_BAD; + int flag; + int rc; + int error; + long pid; + struct testcase test; + int result = 0; + + memset(&test, 0, sizeof(test)); + + while(argc>arg) { + if(!strcmp("--version", argv[arg])) { + printf("tftpd IPv4%s\n", +#ifdef ENABLE_IPV6 + "/IPv6" +#else + "" +#endif + ); + return 0; + } + else if(!strcmp("--pidfile", argv[arg])) { + arg++; + if(argc>arg) + pidname = argv[arg++]; + } + else if(!strcmp("--logfile", argv[arg])) { + arg++; + if(argc>arg) + serverlogfile = argv[arg++]; + } + else if(!strcmp("--ipv4", argv[arg])) { +#ifdef ENABLE_IPV6 + ipv_inuse = "IPv4"; + use_ipv6 = FALSE; +#endif + arg++; + } + else if(!strcmp("--ipv6", argv[arg])) { +#ifdef ENABLE_IPV6 + ipv_inuse = "IPv6"; + use_ipv6 = TRUE; +#endif + arg++; + } + else if(!strcmp("--port", argv[arg])) { + arg++; + if(argc>arg) { + char *endptr; + unsigned long ulnum = strtoul(argv[arg], &endptr, 10); + if((endptr != argv[arg] + strlen(argv[arg])) || + (ulnum < 1025UL) || (ulnum > 65535UL)) { + fprintf(stderr, "tftpd: invalid --port argument (%s)\n", + argv[arg]); + return 0; + } + port = curlx_ultous(ulnum); + arg++; + } + } + else if(!strcmp("--srcdir", argv[arg])) { + arg++; + if(argc>arg) { + path = argv[arg]; + arg++; + } + } + else { + puts("Usage: tftpd [option]\n" + " --version\n" + " --logfile [file]\n" + " --pidfile [file]\n" + " --ipv4\n" + " --ipv6\n" + " --port [port]\n" + " --srcdir [path]"); + return 0; + } + } + +#ifdef WIN32 + win32_init(); + atexit(win32_cleanup); +#endif + + install_signal_handlers(); + + pid = (long)getpid(); + +#ifdef ENABLE_IPV6 + if(!use_ipv6) +#endif + sock = socket(AF_INET, SOCK_DGRAM, 0); +#ifdef ENABLE_IPV6 + else + sock = socket(AF_INET6, SOCK_DGRAM, 0); +#endif + + if(CURL_SOCKET_BAD == sock) { + error = SOCKERRNO; + logmsg("Error creating socket: (%d) %s", + error, strerror(error)); + result = 1; + goto tftpd_cleanup; + } + + flag = 1; + if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (void *)&flag, sizeof(flag))) { + error = SOCKERRNO; + logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s", + error, strerror(error)); + result = 1; + goto tftpd_cleanup; + } + +#ifdef ENABLE_IPV6 + if(!use_ipv6) { +#endif + memset(&me.sa4, 0, sizeof(me.sa4)); + me.sa4.sin_family = AF_INET; + me.sa4.sin_addr.s_addr = INADDR_ANY; + me.sa4.sin_port = htons(port); + rc = bind(sock, &me.sa, sizeof(me.sa4)); +#ifdef ENABLE_IPV6 + } + else { + memset(&me.sa6, 0, sizeof(me.sa6)); + me.sa6.sin6_family = AF_INET6; + me.sa6.sin6_addr = in6addr_any; + me.sa6.sin6_port = htons(port); + rc = bind(sock, &me.sa, sizeof(me.sa6)); + } +#endif /* ENABLE_IPV6 */ + if(0 != rc) { + error = SOCKERRNO; + logmsg("Error binding socket on port %hu: (%d) %s", + port, error, strerror(error)); + result = 1; + goto tftpd_cleanup; + } + + wrotepidfile = write_pidfile(pidname); + if(!wrotepidfile) { + result = 1; + goto tftpd_cleanup; + } + + logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port); + + for(;;) { + fromlen = sizeof(from); +#ifdef ENABLE_IPV6 + if(!use_ipv6) +#endif + fromlen = sizeof(from.sa4); +#ifdef ENABLE_IPV6 + else + fromlen = sizeof(from.sa6); +#endif + n = (ssize_t)recvfrom(sock, &buf.storage[0], sizeof(buf.storage), 0, + &from.sa, &fromlen); + if(got_exit_signal) + break; + if(n < 0) { + logmsg("recvfrom"); + result = 3; + break; + } + + set_advisor_read_lock(SERVERLOGS_LOCK); + serverlogslocked = 1; + +#ifdef ENABLE_IPV6 + if(!use_ipv6) { +#endif + from.sa4.sin_family = AF_INET; + peer = socket(AF_INET, SOCK_DGRAM, 0); + if(CURL_SOCKET_BAD == peer) { + logmsg("socket"); + result = 2; + break; + } + if(connect(peer, &from.sa, sizeof(from.sa4)) < 0) { + logmsg("connect: fail"); + result = 1; + break; + } +#ifdef ENABLE_IPV6 + } + else { + from.sa6.sin6_family = AF_INET6; + peer = socket(AF_INET6, SOCK_DGRAM, 0); + if(CURL_SOCKET_BAD == peer) { + logmsg("socket"); + result = 2; + break; + } + if(connect(peer, &from.sa, sizeof(from.sa6)) < 0) { + logmsg("connect: fail"); + result = 1; + break; + } + } +#endif + + maxtimeout = 5*TIMEOUT; + + tp = &buf.hdr; + tp->th_opcode = ntohs(tp->th_opcode); + if(tp->th_opcode == opcode_RRQ || tp->th_opcode == opcode_WRQ) { + memset(&test, 0, sizeof(test)); + if(do_tftp(&test, tp, n) < 0) + break; + free(test.buffer); + } + sclose(peer); + peer = CURL_SOCKET_BAD; + + if(test.ofile > 0) { + close(test.ofile); + test.ofile = 0; + } + + if(got_exit_signal) + break; + + if(serverlogslocked) { + serverlogslocked = 0; + clear_advisor_read_lock(SERVERLOGS_LOCK); + } + + logmsg("end of one transfer"); + + } + +tftpd_cleanup: + + if(test.ofile > 0) + close(test.ofile); + + if((peer != sock) && (peer != CURL_SOCKET_BAD)) + sclose(peer); + + if(sock != CURL_SOCKET_BAD) + sclose(sock); + + if(got_exit_signal) + logmsg("signalled to die"); + + if(wrotepidfile) + unlink(pidname); + + if(serverlogslocked) { + serverlogslocked = 0; + clear_advisor_read_lock(SERVERLOGS_LOCK); + } + + restore_signal_handlers(); + + if(got_exit_signal) { + logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)", + ipv_inuse, (int)port, pid, exit_signal); + /* + * To properly set the return status of the process we + * must raise the same signal SIGINT or SIGTERM that we + * caught and let the old handler take care of it. + */ + raise(exit_signal); + } + + logmsg("========> tftpd quits"); + return result; +} + +/* + * Handle initial connection protocol. + */ +static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size) +{ + char *cp; + int first = 1, ecode; + struct formats *pf; + char *filename, *mode = NULL; + int error; + FILE *server; +#ifdef USE_WINSOCK + DWORD recvtimeout, recvtimeoutbak; +#endif + char *option = (char *)"mode"; /* mode is implicit */ + int toggle = 1; + + /* Open request dump file. */ + server = fopen(REQUEST_DUMP, "ab"); + if(!server) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg("Error opening file: %s", REQUEST_DUMP); + return -1; + } + + /* store input protocol */ + fprintf(server, "opcode: %x\n", tp->th_opcode); + + cp = (char *)&tp->th_stuff; + filename = cp; + do { + bool endofit = true; + while(cp < &buf.storage[size]) { + if(*cp == '\0') { + endofit = false; + break; + } + cp++; + } + if(endofit) + /* no more options */ + break; + + /* before increasing pointer, make sure it is still within the legal + space */ + if((cp+1) < &buf.storage[size]) { + ++cp; + if(first) { + /* store the mode since we need it later */ + mode = cp; + first = 0; + } + if(toggle) + /* name/value pair: */ + fprintf(server, "%s: %s\n", option, cp); + else { + /* store the name pointer */ + option = cp; + } + toggle ^= 1; + } + else + /* No more options */ + break; + } while(1); + + if(*cp) { + nak(EBADOP); + fclose(server); + return 3; + } + + /* store input protocol */ + fprintf(server, "filename: %s\n", filename); + + for(cp = mode; cp && *cp; cp++) + if(ISUPPER(*cp)) + *cp = (char)tolower((int)*cp); + + /* store input protocol */ + fclose(server); + + for(pf = formata; pf->f_mode; pf++) + if(strcmp(pf->f_mode, mode) == 0) + break; + if(!pf->f_mode) { + nak(EBADOP); + return 2; + } + ecode = validate_access(test, filename, tp->th_opcode); + if(ecode) { + nak(ecode); + return 1; + } + +#ifdef USE_WINSOCK + recvtimeout = sizeof(recvtimeoutbak); + getsockopt(peer, SOL_SOCKET, SO_RCVTIMEO, + (char*)&recvtimeoutbak, (int*)&recvtimeout); + recvtimeout = TIMEOUT*1000; + setsockopt(peer, SOL_SOCKET, SO_RCVTIMEO, + (const char*)&recvtimeout, sizeof(recvtimeout)); +#endif + + if(tp->th_opcode == opcode_WRQ) + recvtftp(test, pf); + else + sendtftp(test, pf); + +#ifdef USE_WINSOCK + recvtimeout = recvtimeoutbak; + setsockopt(peer, SOL_SOCKET, SO_RCVTIMEO, + (const char*)&recvtimeout, sizeof(recvtimeout)); +#endif + + return 0; +} + +/* Based on the testno, parse the correct server commands. */ +static int parse_servercmd(struct testcase *req) +{ + FILE *stream; + char *filename; + int error; + + filename = test2file(req->testno); + + stream=fopen(filename, "rb"); + if(!stream) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg(" [1] Error opening file: %s", filename); + logmsg(" Couldn't open test file %ld", req->testno); + return 1; /* done */ + } + else { + char *orgcmd = NULL; + char *cmd = NULL; + size_t cmdsize = 0; + int num=0; + + /* get the custom server control "commands" */ + error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream); + fclose(stream); + if(error) { + logmsg("getpart() failed with error: %d", error); + return 1; /* done */ + } + + cmd = orgcmd; + while(cmd && cmdsize) { + char *check; + if(1 == sscanf(cmd, "writedelay: %d", &num)) { + logmsg("instructed to delay %d secs between packets", num); + req->writedelay = num; + } + else { + logmsg("Unknown instruction found: %s", cmd); + } + /* try to deal with CRLF or just LF */ + check = strchr(cmd, '\r'); + if(!check) + check = strchr(cmd, '\n'); + + if(check) { + /* get to the letter following the newline */ + while((*check == '\r') || (*check == '\n')) + check++; + + if(!*check) + /* if we reached a zero, get out */ + break; + cmd = check; + } + else + break; + } + free(orgcmd); + } + + return 0; /* OK! */ +} + + +/* + * Validate file access. + */ +static int validate_access(struct testcase *test, + const char *filename, int mode) +{ + char *ptr; + long testno, partno; + int error; + char partbuf[80]="data"; + + logmsg("trying to get file: %s mode %x", filename, mode); + + if(!strncmp("verifiedserver", filename, 14)) { + char weare[128]; + size_t count = snprintf(weare, sizeof(weare), + "WE ROOLZ: %ld\r\n", (long)getpid()); + + logmsg("Are-we-friendly question received"); + test->buffer = strdup(weare); + test->rptr = test->buffer; /* set read pointer */ + test->bufsize = count; /* set total count */ + test->rcount = count; /* set data left to read */ + return 0; /* fine */ + } + + /* find the last slash */ + ptr = strrchr(filename, '/'); + + if(ptr) { + char *file; + + ptr++; /* skip the slash */ + + /* skip all non-numericals following the slash */ + while(*ptr && !ISDIGIT(*ptr)) + ptr++; + + /* get the number */ + testno = strtol(ptr, &ptr, 10); + + if(testno > 10000) { + partno = testno % 10000; + testno /= 10000; + } + else + partno = 0; + + + logmsg("requested test number %ld part %ld", testno, partno); + + test->testno = testno; + + (void)parse_servercmd(test); + + file = test2file(testno); + + if(0 != partno) + snprintf(partbuf, sizeof(partbuf), "data%ld", partno); + + if(file) { + FILE *stream=fopen(file, "rb"); + if(!stream) { + error = errno; + logmsg("fopen() failed with error: %d %s", error, strerror(error)); + logmsg("Error opening file: %s", file); + logmsg("Couldn't open test file: %s", file); + return EACCESS; + } + else { + size_t count; + error = getpart(&test->buffer, &count, "reply", partbuf, stream); + fclose(stream); + if(error) { + logmsg("getpart() failed with error: %d", error); + return EACCESS; + } + if(test->buffer) { + test->rptr = test->buffer; /* set read pointer */ + test->bufsize = count; /* set total count */ + test->rcount = count; /* set data left to read */ + } + else + return EACCESS; + } + + } + else + return EACCESS; + } + else { + logmsg("no slash found in path"); + return EACCESS; /* failure */ + } + + logmsg("file opened and all is good"); + return 0; +} + +/* + * Send the requested file. + */ +static void sendtftp(struct testcase *test, struct formats *pf) +{ + int size; + ssize_t n; + /* This is volatile to live through a siglongjmp */ + volatile unsigned short sendblock; /* block count */ + struct tftphdr *sdp; /* data buffer */ + struct tftphdr *sap; /* ack buffer */ + + sendblock = 1; +#if defined(HAVE_ALARM) && defined(SIGALRM) + mysignal(SIGALRM, timer); +#endif + sdp = r_init(); + sap = &ackbuf.hdr; + do { + size = readit(test, &sdp, pf->f_convert); + if(size < 0) { + nak(errno + 100); + return; + } + sdp->th_opcode = htons((unsigned short)opcode_DATA); + sdp->th_block = htons(sendblock); + timeout = 0; +#ifdef HAVE_SIGSETJMP + (void) sigsetjmp(timeoutbuf, 1); +#endif + if(test->writedelay) { + logmsg("Pausing %d seconds before %d bytes", test->writedelay, + size); + wait_ms(1000*test->writedelay); + } + + send_data: + if(swrite(peer, sdp, size + 4) != size + 4) { + logmsg("write"); + return; + } + read_ahead(test, pf->f_convert); + for(;;) { +#ifdef HAVE_ALARM + alarm(rexmtval); /* read the ack */ +#endif + n = sread(peer, &ackbuf.storage[0], sizeof(ackbuf.storage)); +#ifdef HAVE_ALARM + alarm(0); +#endif + if(got_exit_signal) + return; + if(n < 0) { + logmsg("read: fail"); + return; + } + sap->th_opcode = ntohs((unsigned short)sap->th_opcode); + sap->th_block = ntohs(sap->th_block); + + if(sap->th_opcode == opcode_ERROR) { + logmsg("got ERROR"); + return; + } + + if(sap->th_opcode == opcode_ACK) { + if(sap->th_block == sendblock) { + break; + } + /* Re-synchronize with the other side */ + (void) synchnet(peer); + if(sap->th_block == (sendblock-1)) { + goto send_data; + } + } + + } + sendblock++; + } while(size == SEGSIZE); +} + +/* + * Receive a file. + */ +static void recvtftp(struct testcase *test, struct formats *pf) +{ + ssize_t n, size; + /* These are volatile to live through a siglongjmp */ + volatile unsigned short recvblock; /* block count */ + struct tftphdr * volatile rdp; /* data buffer */ + struct tftphdr *rap; /* ack buffer */ + + recvblock = 0; + rdp = w_init(); +#if defined(HAVE_ALARM) && defined(SIGALRM) + mysignal(SIGALRM, timer); +#endif + rap = &ackbuf.hdr; + do { + timeout = 0; + rap->th_opcode = htons((unsigned short)opcode_ACK); + rap->th_block = htons(recvblock); + recvblock++; +#ifdef HAVE_SIGSETJMP + (void) sigsetjmp(timeoutbuf, 1); +#endif +send_ack: + if(swrite(peer, &ackbuf.storage[0], 4) != 4) { + logmsg("write: fail\n"); + goto abort; + } + write_behind(test, pf->f_convert); + for(;;) { +#ifdef HAVE_ALARM + alarm(rexmtval); +#endif + n = sread(peer, rdp, PKTSIZE); +#ifdef HAVE_ALARM + alarm(0); +#endif + if(got_exit_signal) + goto abort; + if(n < 0) { /* really? */ + logmsg("read: fail\n"); + goto abort; + } + rdp->th_opcode = ntohs((unsigned short)rdp->th_opcode); + rdp->th_block = ntohs(rdp->th_block); + if(rdp->th_opcode == opcode_ERROR) + goto abort; + if(rdp->th_opcode == opcode_DATA) { + if(rdp->th_block == recvblock) { + break; /* normal */ + } + /* Re-synchronize with the other side */ + (void) synchnet(peer); + if(rdp->th_block == (recvblock-1)) + goto send_ack; /* rexmit */ + } + } + + size = writeit(test, &rdp, (int)(n - 4), pf->f_convert); + if(size != (n-4)) { /* ahem */ + if(size < 0) + nak(errno + 100); + else + nak(ENOSPACE); + goto abort; + } + } while(size == SEGSIZE); + write_behind(test, pf->f_convert); + + rap->th_opcode = htons((unsigned short)opcode_ACK); /* send the "final" + ack */ + rap->th_block = htons(recvblock); + (void) swrite(peer, &ackbuf.storage[0], 4); +#if defined(HAVE_ALARM) && defined(SIGALRM) + mysignal(SIGALRM, justtimeout); /* just abort read on timeout */ + alarm(rexmtval); +#endif + /* normally times out and quits */ + n = sread(peer, &buf.storage[0], sizeof(buf.storage)); +#ifdef HAVE_ALARM + alarm(0); +#endif + if(got_exit_signal) + goto abort; + if(n >= 4 && /* if read some data */ + rdp->th_opcode == opcode_DATA && /* and got a data block */ + recvblock == rdp->th_block) { /* then my last ack was lost */ + (void) swrite(peer, &ackbuf.storage[0], 4); /* resend final ack */ + } +abort: + return; +} + +/* + * Send a nak packet (error message). Error code passed in is one of the + * standard TFTP codes, or a Unix errno offset by 100. + */ +static void nak(int error) +{ + struct tftphdr *tp; + int length; + struct errmsg *pe; + + tp = &buf.hdr; + tp->th_opcode = htons((unsigned short)opcode_ERROR); + tp->th_code = htons((unsigned short)error); + for(pe = errmsgs; pe->e_code >= 0; pe++) + if(pe->e_code == error) + break; + if(pe->e_code < 0) { + pe->e_msg = strerror(error - 100); + tp->th_code = EUNDEF; /* set 'undef' errorcode */ + } + length = (int)strlen(pe->e_msg); + + /* we use memcpy() instead of strcpy() in order to avoid buffer overflow + * report from glibc with FORTIFY_SOURCE */ + memcpy(tp->th_msg, pe->e_msg, length + 1); + length += 5; + if(swrite(peer, &buf.storage[0], length) != length) + logmsg("nak: fail\n"); +} diff --git a/deps/curl-7.51.0/tests/server/util.c b/deps/curl-7.51.0/tests/server/util.c new file mode 100644 index 0000000000..e654707860 --- /dev/null +++ b/deps/curl-7.51.0/tests/server/util.c @@ -0,0 +1,391 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "server_setup.h" + +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef _XOPEN_SOURCE_EXTENDED +/* This define is "almost" required to build on HPUX 11 */ +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_SYS_POLL_H +#include +#elif defined(HAVE_POLL_H) +#include +#endif + +#define ENABLE_CURLX_PRINTF +/* make the curlx header define all printf() functions to use the curlx_* + versions instead */ +#include "curlx.h" /* from the private lib dir */ +#include "getpart.h" +#include "util.h" +#include "timeval.h" + +#ifdef USE_WINSOCK +#undef EINTR +#define EINTR 4 /* errno.h value */ +#undef EINVAL +#define EINVAL 22 /* errno.h value */ +#endif + +#if defined(ENABLE_IPV6) && defined(__MINGW32__) +const struct in6_addr in6addr_any = {{ IN6ADDR_ANY_INIT }}; +#endif + +/* This function returns a pointer to STATIC memory. It converts the given + * binary lump to a hex formatted string usable for output in logs or + * whatever. + */ +char *data_to_hex(char *data, size_t len) +{ + static char buf[256*3]; + size_t i; + char *optr = buf; + char *iptr = data; + + if(len > 255) + len = 255; + + for(i=0; i < len; i++) { + if((data[i] >= 0x20) && (data[i] < 0x7f)) + *optr++ = *iptr++; + else { + snprintf(optr, 4, "%%%02x", *iptr++); + optr+=3; + } + } + *optr=0; /* in case no sprintf was used */ + + return buf; +} + +void logmsg(const char *msg, ...) +{ + va_list ap; + char buffer[2048 + 1]; + FILE *logfp; + int error; + struct timeval tv; + time_t sec; + struct tm *now; + char timebuf[20]; + static time_t epoch_offset; + static int known_offset; + + if(!serverlogfile) { + fprintf(stderr, "Error: serverlogfile not set\n"); + return; + } + + tv = curlx_tvnow(); + if(!known_offset) { + epoch_offset = time(NULL) - tv.tv_sec; + known_offset = 1; + } + sec = epoch_offset + tv.tv_sec; + now = localtime(&sec); /* not thread safe but we don't care */ + + snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld", + (int)now->tm_hour, (int)now->tm_min, (int)now->tm_sec, (long)tv.tv_usec); + + va_start(ap, msg); + vsnprintf(buffer, sizeof(buffer), msg, ap); + va_end(ap); + + logfp = fopen(serverlogfile, "ab"); + if(logfp) { + fprintf(logfp, "%s %s\n", timebuf, buffer); + fclose(logfp); + } + else { + error = errno; + fprintf(stderr, "fopen() failed with error: %d %s\n", + error, strerror(error)); + fprintf(stderr, "Error opening file: %s\n", serverlogfile); + fprintf(stderr, "Msg not logged: %s %s\n", timebuf, buffer); + } +} + +#ifdef WIN32 +/* use instead of perror() on generic windows */ +void win32_perror (const char *msg) +{ + char buf[512]; + DWORD err = SOCKERRNO; + + if(!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, + LANG_NEUTRAL, buf, sizeof(buf), NULL)) + snprintf(buf, sizeof(buf), "Unknown error %lu (%#lx)", err, err); + if(msg) + fprintf(stderr, "%s: ", msg); + fprintf(stderr, "%s\n", buf); +} +#endif /* WIN32 */ + +#ifdef USE_WINSOCK +void win32_init(void) +{ + WORD wVersionRequested; + WSADATA wsaData; + int err; + wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK); + + err = WSAStartup(wVersionRequested, &wsaData); + + if(err != 0) { + perror("Winsock init failed"); + logmsg("Error initialising winsock -- aborting"); + exit(1); + } + + if(LOBYTE(wsaData.wVersion) != USE_WINSOCK || + HIBYTE(wsaData.wVersion) != USE_WINSOCK) { + WSACleanup(); + perror("Winsock init failed"); + logmsg("No suitable winsock.dll found -- aborting"); + exit(1); + } +} + +void win32_cleanup(void) +{ + WSACleanup(); +} +#endif /* USE_WINSOCK */ + +/* set by the main code to point to where the test dir is */ +const char *path="."; + +char *test2file(long testno) +{ + static char filename[256]; + snprintf(filename, sizeof(filename), TEST_DATA_PATH, path, testno); + return filename; +} + +/* + * Portable function used for waiting a specific amount of ms. + * Waiting indefinitely with this function is not allowed, a + * zero or negative timeout value will return immediately. + * + * Return values: + * -1 = system call error, or invalid timeout value + * 0 = specified timeout has elapsed + */ +int wait_ms(int timeout_ms) +{ +#if !defined(MSDOS) && !defined(USE_WINSOCK) +#ifndef HAVE_POLL_FINE + struct timeval pending_tv; +#endif + struct timeval initial_tv; + int pending_ms; + int error; +#endif + int r = 0; + + if(!timeout_ms) + return 0; + if(timeout_ms < 0) { + errno = EINVAL; + return -1; + } +#if defined(MSDOS) + delay(timeout_ms); +#elif defined(USE_WINSOCK) + Sleep(timeout_ms); +#else + pending_ms = timeout_ms; + initial_tv = curlx_tvnow(); + do { +#if defined(HAVE_POLL_FINE) + r = poll(NULL, 0, pending_ms); +#else + pending_tv.tv_sec = pending_ms / 1000; + pending_tv.tv_usec = (pending_ms % 1000) * 1000; + r = select(0, NULL, NULL, NULL, &pending_tv); +#endif /* HAVE_POLL_FINE */ + if(r != -1) + break; + error = errno; + if(error && (error != EINTR)) + break; + pending_ms = timeout_ms - (int)curlx_tvdiff(curlx_tvnow(), initial_tv); + if(pending_ms <= 0) + break; + } while(r == -1); +#endif /* USE_WINSOCK */ + if(r) + r = -1; + return r; +} + +int write_pidfile(const char *filename) +{ + FILE *pidfile; + long pid; + + pid = (long)getpid(); + pidfile = fopen(filename, "wb"); + if(!pidfile) { + logmsg("Couldn't write pid file: %s %s", filename, strerror(errno)); + return 0; /* fail */ + } + fprintf(pidfile, "%ld\n", pid); + fclose(pidfile); + logmsg("Wrote pid %ld to %s", pid, filename); + return 1; /* success */ +} + +void set_advisor_read_lock(const char *filename) +{ + FILE *lockfile; + int error = 0; + int res; + + do { + lockfile = fopen(filename, "wb"); + } while((lockfile == NULL) && ((error = errno) == EINTR)); + if(lockfile == NULL) { + logmsg("Error creating lock file %s error: %d %s", + filename, error, strerror(error)); + return; + } + + do { + res = fclose(lockfile); + } while(res && ((error = errno) == EINTR)); + if(res) + logmsg("Error closing lock file %s error: %d %s", + filename, error, strerror(error)); +} + +void clear_advisor_read_lock(const char *filename) +{ + int error = 0; + int res; + + /* + ** Log all removal failures. Even those due to file not existing. + ** This allows to detect if unexpectedly the file has already been + ** removed by a process different than the one that should do this. + */ + + do { + res = unlink(filename); + } while(res && ((error = errno) == EINTR)); + if(res) + logmsg("Error removing lock file %s error: %d %s", + filename, error, strerror(error)); +} + + +/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because + its behavior is altered by the current locale. */ +static char raw_toupper(char in) +{ +#if !defined(CURL_DOES_CONVERSIONS) + if(in >= 'a' && in <= 'z') + return (char)('A' + in - 'a'); +#else + switch (in) { + case 'a': + return 'A'; + case 'b': + return 'B'; + case 'c': + return 'C'; + case 'd': + return 'D'; + case 'e': + return 'E'; + case 'f': + return 'F'; + case 'g': + return 'G'; + case 'h': + return 'H'; + case 'i': + return 'I'; + case 'j': + return 'J'; + case 'k': + return 'K'; + case 'l': + return 'L'; + case 'm': + return 'M'; + case 'n': + return 'N'; + case 'o': + return 'O'; + case 'p': + return 'P'; + case 'q': + return 'Q'; + case 'r': + return 'R'; + case 's': + return 'S'; + case 't': + return 'T'; + case 'u': + return 'U'; + case 'v': + return 'V'; + case 'w': + return 'W'; + case 'x': + return 'X'; + case 'y': + return 'Y'; + case 'z': + return 'Z'; + } +#endif + + return in; +} + +int strncasecompare(const char *first, const char *second, size_t max) +{ + while(*first && *second && max) { + if(raw_toupper(*first) != raw_toupper(*second)) { + break; + } + max--; + first++; + second++; + } + if(0 == max) + return 1; /* they are equal this far */ + + return raw_toupper(*first) == raw_toupper(*second); +} diff --git a/deps/curl-7.51.0/tests/server/util.h b/deps/curl-7.51.0/tests/server/util.h new file mode 100644 index 0000000000..c90fcdf32a --- /dev/null +++ b/deps/curl-7.51.0/tests/server/util.h @@ -0,0 +1,68 @@ +#ifndef HEADER_CURL_SERVER_UTIL_H +#define HEADER_CURL_SERVER_UTIL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "server_setup.h" + +char *data_to_hex(char *data, size_t len); +void logmsg(const char *msg, ...); + +#define TEST_DATA_PATH "%s/data/test%ld" + +#define SERVERLOGS_LOCK "log/serverlogs.lock" + +/* global variable, where to find the 'data' dir */ +extern const char *path; + +/* global variable, log file name */ +extern const char *serverlogfile; + +#ifdef WIN32 +#include +#include + +#define sleep(sec) Sleep ((sec)*1000) + +#undef perror +#define perror(m) win32_perror(m) +void win32_perror (const char *msg); +#endif /* WIN32 */ + +#ifdef USE_WINSOCK +void win32_init(void); +void win32_cleanup(void); +#endif /* USE_WINSOCK */ + +/* returns the path name to the test case file */ +char *test2file(long testno); + +int wait_ms(int timeout_ms); + +int write_pidfile(const char *filename); + +void set_advisor_read_lock(const char *filename); + +void clear_advisor_read_lock(const char *filename); + +int strncasecompare(const char *first, const char *second, size_t max); + +#endif /* HEADER_CURL_SERVER_UTIL_H */ diff --git a/deps/curl-7.51.0/tests/serverhelp.pm b/deps/curl-7.51.0/tests/serverhelp.pm new file mode 100644 index 0000000000..d6a06508d6 --- /dev/null +++ b/deps/curl-7.51.0/tests/serverhelp.pm @@ -0,0 +1,247 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +#*************************************************************************** + +package serverhelp; + +use strict; +use warnings; +use Exporter; + + +#*************************************************************************** +# Global symbols allowed without explicit package name +# +use vars qw( + @ISA + @EXPORT_OK + ); + + +#*************************************************************************** +# Inherit Exporter's capabilities +# +@ISA = qw(Exporter); + + +#*************************************************************************** +# Global symbols this module will export upon request +# +@EXPORT_OK = qw( + serverfactors + servername_id + servername_str + servername_canon + server_pidfilename + server_logfilename + server_cmdfilename + server_inputfilename + server_outputfilename + mainsockf_pidfilename + mainsockf_logfilename + datasockf_pidfilename + datasockf_logfilename + ); + + +#*************************************************************************** +# Just for convenience, test harness uses 'https' and 'httptls' literals as +# values for 'proto' variable in order to differentiate different servers. +# 'https' literal is used for stunnel based https test servers, and 'httptls' +# is used for non-stunnel https test servers. + + +#*************************************************************************** +# Return server characterization factors given a server id string. +# +sub serverfactors { + my $server = $_[0]; + my $proto; + my $ipvnum; + my $idnum; + + if($server =~ + /^((ftp|http|imap|pop3|smtp|http-pipe)s?)(\d*)(-ipv6|)$/) { + $proto = $1; + $idnum = ($3 && ($3 > 1)) ? $3 : 1; + $ipvnum = ($4 && ($4 =~ /6$/)) ? 6 : 4; + } + elsif($server =~ + /^(tftp|sftp|socks|ssh|rtsp|gopher|httptls)(\d*)(-ipv6|)$/) { + $proto = $1; + $idnum = ($2 && ($2 > 1)) ? $2 : 1; + $ipvnum = ($3 && ($3 =~ /6$/)) ? 6 : 4; + } + else { + die "invalid server id: '$server'" + } + return($proto, $ipvnum, $idnum); +} + + +#*************************************************************************** +# Return server name string formatted for presentation purposes +# +sub servername_str { + my ($proto, $ipver, $idnum) = @_; + + $proto = uc($proto) if($proto); + die "unsupported protocol: '$proto'" unless($proto && + ($proto =~ /^(((FTP|HTTP|HTTP\/2|IMAP|POP3|SMTP|HTTP-PIPE)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP|GOPHER|HTTPTLS))$/)); + + $ipver = (not $ipver) ? 'ipv4' : lc($ipver); + die "unsupported IP version: '$ipver'" unless($ipver && + ($ipver =~ /^(4|6|ipv4|ipv6|-ipv4|-ipv6|unix)$/)); + $ipver = ($ipver =~ /6$/) ? '-IPv6' : (($ipver =~ /unix$/) ? '-unix' : ''); + + $idnum = 1 if(not $idnum); + die "unsupported ID number: '$idnum'" unless($idnum && + ($idnum =~ /^(\d+)$/)); + $idnum = '' unless($idnum > 1); + + return "${proto}${idnum}${ipver}"; +} + + +#*************************************************************************** +# Return server name string formatted for identification purposes +# +sub servername_id { + my ($proto, $ipver, $idnum) = @_; + return lc(servername_str($proto, $ipver, $idnum)); +} + + +#*************************************************************************** +# Return server name string formatted for file name purposes +# +sub servername_canon { + my ($proto, $ipver, $idnum) = @_; + my $string = lc(servername_str($proto, $ipver, $idnum)); + $string =~ tr/-/_/; + $string =~ s/\//_v/; + return $string; +} + + +#*************************************************************************** +# Return file name for server pid file. +# +sub server_pidfilename { + my ($proto, $ipver, $idnum) = @_; + my $trailer = '_server.pid'; + return '.'. servername_canon($proto, $ipver, $idnum) ."$trailer"; +} + + +#*************************************************************************** +# Return file name for server log file. +# +sub server_logfilename { + my ($logdir, $proto, $ipver, $idnum) = @_; + my $trailer = '_server.log'; + $trailer = '_stunnel.log' if(lc($proto) =~ /^(ftp|http|imap|pop3|smtp)s$/); + return "${logdir}/". servername_canon($proto, $ipver, $idnum) ."$trailer"; +} + + +#*************************************************************************** +# Return file name for server commands file. +# +sub server_cmdfilename { + my ($logdir, $proto, $ipver, $idnum) = @_; + my $trailer = '_server.cmd'; + return "${logdir}/". servername_canon($proto, $ipver, $idnum) ."$trailer"; +} + + +#*************************************************************************** +# Return file name for server input file. +# +sub server_inputfilename { + my ($logdir, $proto, $ipver, $idnum) = @_; + my $trailer = '_server.input'; + return "${logdir}/". servername_canon($proto, $ipver, $idnum) ."$trailer"; +} + + +#*************************************************************************** +# Return file name for server output file. +# +sub server_outputfilename { + my ($logdir, $proto, $ipver, $idnum) = @_; + my $trailer = '_server.output'; + return "${logdir}/". servername_canon($proto, $ipver, $idnum) ."$trailer"; +} + + +#*************************************************************************** +# Return file name for main or primary sockfilter pid file. +# +sub mainsockf_pidfilename { + my ($proto, $ipver, $idnum) = @_; + die "unsupported protocol: '$proto'" unless($proto && + (lc($proto) =~ /^(ftp|imap|pop3|smtp)s?$/)); + my $trailer = (lc($proto) =~ /^ftps?$/) ? '_sockctrl.pid':'_sockfilt.pid'; + return '.'. servername_canon($proto, $ipver, $idnum) ."$trailer"; +} + + +#*************************************************************************** +# Return file name for main or primary sockfilter log file. +# +sub mainsockf_logfilename { + my ($logdir, $proto, $ipver, $idnum) = @_; + die "unsupported protocol: '$proto'" unless($proto && + (lc($proto) =~ /^(ftp|imap|pop3|smtp)s?$/)); + my $trailer = (lc($proto) =~ /^ftps?$/) ? '_sockctrl.log':'_sockfilt.log'; + return "${logdir}/". servername_canon($proto, $ipver, $idnum) ."$trailer"; +} + + +#*************************************************************************** +# Return file name for data or secondary sockfilter pid file. +# +sub datasockf_pidfilename { + my ($proto, $ipver, $idnum) = @_; + die "unsupported protocol: '$proto'" unless($proto && + (lc($proto) =~ /^ftps?$/)); + my $trailer = '_sockdata.pid'; + return '.'. servername_canon($proto, $ipver, $idnum) ."$trailer"; +} + + +#*************************************************************************** +# Return file name for data or secondary sockfilter log file. +# +sub datasockf_logfilename { + my ($logdir, $proto, $ipver, $idnum) = @_; + die "unsupported protocol: '$proto'" unless($proto && + (lc($proto) =~ /^ftps?$/)); + my $trailer = '_sockdata.log'; + return "${logdir}/". servername_canon($proto, $ipver, $idnum) ."$trailer"; +} + + +#*************************************************************************** +# End of library +1; + diff --git a/deps/curl-7.51.0/tests/sshhelp.pm b/deps/curl-7.51.0/tests/sshhelp.pm new file mode 100644 index 0000000000..c5618a1094 --- /dev/null +++ b/deps/curl-7.51.0/tests/sshhelp.pm @@ -0,0 +1,454 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +#*************************************************************************** + +package sshhelp; + +use strict; +use warnings; +use Exporter; +use File::Spec; + + +#*************************************************************************** +# Global symbols allowed without explicit package name +# +use vars qw( + @ISA + @EXPORT_OK + $sshdexe + $sshexe + $sftpsrvexe + $sftpexe + $sshkeygenexe + $httptlssrvexe + $sshdconfig + $sshconfig + $sftpconfig + $knownhosts + $sshdlog + $sshlog + $sftplog + $sftpcmds + $hstprvkeyf + $hstpubkeyf + $cliprvkeyf + $clipubkeyf + @sftppath + @httptlssrvpath + ); + + +#*************************************************************************** +# Inherit Exporter's capabilities +# +@ISA = qw(Exporter); + + +#*************************************************************************** +# Global symbols this module will export upon request +# +@EXPORT_OK = qw( + $sshdexe + $sshexe + $sftpsrvexe + $sftpexe + $sshkeygenexe + $sshdconfig + $sshconfig + $sftpconfig + $knownhosts + $sshdlog + $sshlog + $sftplog + $sftpcmds + $hstprvkeyf + $hstpubkeyf + $cliprvkeyf + $clipubkeyf + display_sshdconfig + display_sshconfig + display_sftpconfig + display_sshdlog + display_sshlog + display_sftplog + dump_array + exe_ext + find_sshd + find_ssh + find_sftpsrv + find_sftp + find_sshkeygen + find_httptlssrv + logmsg + sshversioninfo + ); + + +#*************************************************************************** +# Global variables initialization +# +$sshdexe = 'sshd' .exe_ext(); # base name and ext of ssh daemon +$sshexe = 'ssh' .exe_ext(); # base name and ext of ssh client +$sftpsrvexe = 'sftp-server' .exe_ext(); # base name and ext of sftp-server +$sftpexe = 'sftp' .exe_ext(); # base name and ext of sftp client +$sshkeygenexe = 'ssh-keygen' .exe_ext(); # base name and ext of ssh-keygen +$httptlssrvexe = 'gnutls-serv' .exe_ext(); # base name and ext of gnutls-serv +$sshdconfig = 'curl_sshd_config'; # ssh daemon config file +$sshconfig = 'curl_ssh_config'; # ssh client config file +$sftpconfig = 'curl_sftp_config'; # sftp client config file +$sshdlog = undef; # ssh daemon log file +$sshlog = undef; # ssh client log file +$sftplog = undef; # sftp client log file +$sftpcmds = 'curl_sftp_cmds'; # sftp client commands batch file +$knownhosts = 'curl_client_knownhosts'; # ssh knownhosts file +$hstprvkeyf = 'curl_host_rsa_key'; # host private key file +$hstpubkeyf = 'curl_host_rsa_key.pub'; # host public key file +$cliprvkeyf = 'curl_client_key'; # client private key file +$clipubkeyf = 'curl_client_key.pub'; # client public key file + + +#*************************************************************************** +# Absolute paths where to look for sftp-server plugin, when not in PATH +# +@sftppath = qw( + /usr/lib/openssh + /usr/libexec/openssh + /usr/libexec + /usr/local/libexec + /opt/local/libexec + /usr/lib/ssh + /usr/libexec/ssh + /usr/sbin + /usr/lib + /usr/lib/ssh/openssh + /usr/lib64/ssh + /usr/lib64/misc + /usr/lib/misc + /usr/local/sbin + /usr/freeware/bin + /usr/freeware/sbin + /usr/freeware/libexec + /opt/ssh/sbin + /opt/ssh/libexec + ); + + +#*************************************************************************** +# Absolute paths where to look for httptlssrv (gnutls-serv), when not in PATH +# +@httptlssrvpath = qw( + /usr/sbin + /usr/libexec + /usr/lib + /usr/lib/misc + /usr/lib64/misc + /usr/local/bin + /usr/local/sbin + /usr/local/libexec + /opt/local/bin + /opt/local/sbin + /opt/local/libexec + /usr/freeware/bin + /usr/freeware/sbin + /usr/freeware/libexec + /opt/gnutls/bin + /opt/gnutls/sbin + /opt/gnutls/libexec + ); + + +#*************************************************************************** +# Return file extension for executable files on this operating system +# +sub exe_ext { + if ($^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys' || + $^O eq 'dos' || $^O eq 'os2') { + return '.exe'; + } +} + + +#*************************************************************************** +# Create or overwrite the given file with lines from an array of strings +# +sub dump_array { + my ($filename, @arr) = @_; + my $error; + + if(!$filename) { + $error = 'Error: Missing argument 1 for dump_array()'; + } + elsif(open(TEXTFH, ">$filename")) { + foreach my $line (@arr) { + $line .= "\n" unless($line =~ /\n$/); + print TEXTFH $line; + } + if(!close(TEXTFH)) { + $error = "Error: cannot close file $filename"; + } + } + else { + $error = "Error: cannot write file $filename"; + } + return $error; +} + + +#*************************************************************************** +# Display a message +# +sub logmsg { + my ($line) = @_; + chomp $line if($line); + $line .= "\n"; + print "$line"; +} + + +#*************************************************************************** +# Display contents of the given file +# +sub display_file { + my $filename = $_[0]; + print "=== Start of file $filename\n"; + if(open(DISPLAYFH, "<$filename")) { + while(my $line = ) { + print "$line"; + } + close DISPLAYFH; + } + print "=== End of file $filename\n"; +} + + +#*************************************************************************** +# Display contents of the ssh daemon config file +# +sub display_sshdconfig { + display_file($sshdconfig); +} + + +#*************************************************************************** +# Display contents of the ssh client config file +# +sub display_sshconfig { + display_file($sshconfig); +} + + +#*************************************************************************** +# Display contents of the sftp client config file +# +sub display_sftpconfig { + display_file($sftpconfig); +} + + +#*************************************************************************** +# Display contents of the ssh daemon log file +# +sub display_sshdlog { + die "error: \$sshdlog uninitialized" if(not defined $sshdlog); + display_file($sshdlog); +} + + +#*************************************************************************** +# Display contents of the ssh client log file +# +sub display_sshlog { + die "error: \$sshlog uninitialized" if(not defined $sshlog); + display_file($sshlog); +} + + +#*************************************************************************** +# Display contents of the sftp client log file +# +sub display_sftplog { + die "error: \$sftplog uninitialized" if(not defined $sftplog); + display_file($sftplog); +} + + +#*************************************************************************** +# Find a file somewhere in the given path +# +sub find_file { + my $fn = $_[0]; + shift; + my @path = @_; + foreach (@path) { + my $file = File::Spec->catfile($_, $fn); + if(-e $file && ! -d $file) { + return $file; + } + } +} + + +#*************************************************************************** +# Find an executable file somewhere in the given path +# +sub find_exe_file { + my $fn = $_[0]; + shift; + my @path = @_; + my $xext = exe_ext(); + foreach (@path) { + my $file = File::Spec->catfile($_, $fn); + if(-e $file && ! -d $file) { + return $file if(-x $file); + return $file if(($xext) && (lc($file) =~ /\Q$xext\E$/)); + } + } +} + + +#*************************************************************************** +# Find a file in environment path or in our sftppath +# +sub find_file_spath { + my $filename = $_[0]; + my @spath; + push(@spath, File::Spec->path()); + push(@spath, @sftppath); + return find_file($filename, @spath); +} + + +#*************************************************************************** +# Find an executable file in environment path or in our httptlssrvpath +# +sub find_exe_file_hpath { + my $filename = $_[0]; + my @hpath; + push(@hpath, File::Spec->path()); + push(@hpath, @httptlssrvpath); + return find_exe_file($filename, @hpath); +} + + +#*************************************************************************** +# Find ssh daemon and return canonical filename +# +sub find_sshd { + return find_file_spath($sshdexe); +} + + +#*************************************************************************** +# Find ssh client and return canonical filename +# +sub find_ssh { + return find_file_spath($sshexe); +} + + +#*************************************************************************** +# Find sftp-server plugin and return canonical filename +# +sub find_sftpsrv { + return find_file_spath($sftpsrvexe); +} + + +#*************************************************************************** +# Find sftp client and return canonical filename +# +sub find_sftp { + return find_file_spath($sftpexe); +} + + +#*************************************************************************** +# Find ssh-keygen and return canonical filename +# +sub find_sshkeygen { + return find_file_spath($sshkeygenexe); +} + + +#*************************************************************************** +# Find httptlssrv (gnutls-serv) and return canonical filename +# +sub find_httptlssrv { + return find_exe_file_hpath($httptlssrvexe); +} + + +#*************************************************************************** +# Return version info for the given ssh client or server binaries +# +sub sshversioninfo { + my $sshbin = $_[0]; # canonical filename + my $major; + my $minor; + my $patch; + my $sshid; + my $versnum; + my $versstr; + my $error; + + if(!$sshbin) { + $error = 'Error: Missing argument 1 for sshversioninfo()'; + } + elsif(! -x $sshbin) { + $error = "Error: cannot read or execute $sshbin"; + } + else { + my $cmd = ($sshbin =~ /$sshdexe$/) ? "\"$sshbin\" -?" : "\"$sshbin\" -V"; + $error = "$cmd\n"; + foreach my $tmpstr (qx($cmd 2>&1)) { + if($tmpstr =~ /OpenSSH[_-](\d+)\.(\d+)(\.(\d+))*/i) { + $major = $1; + $minor = $2; + $patch = $4?$4:0; + $sshid = 'OpenSSH'; + $versnum = (100*$major) + (10*$minor) + $patch; + $versstr = "$sshid $major.$minor.$patch"; + $error = undef; + last; + } + if($tmpstr =~ /Sun[_-]SSH[_-](\d+)\.(\d+)(\.(\d+))*/i) { + $major = $1; + $minor = $2; + $patch = $4?$4:0; + $sshid = 'SunSSH'; + $versnum = (100*$major) + (10*$minor) + $patch; + $versstr = "$sshid $major.$minor.$patch"; + $error = undef; + last; + } + $error .= $tmpstr; + } + chomp $error if($error); + } + return ($sshid, $versnum, $versstr, $error); +} + + +#*************************************************************************** +# End of library +1; + diff --git a/deps/curl-7.51.0/tests/sshserver.pl b/deps/curl-7.51.0/tests/sshserver.pl new file mode 100644 index 0000000000..3ebf8e6757 --- /dev/null +++ b/deps/curl-7.51.0/tests/sshserver.pl @@ -0,0 +1,1080 @@ +#!/usr/bin/env perl +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +#*************************************************************************** + +# Starts sshd for use in the SCP, SFTP and SOCKS curl test harness tests. +# Also creates the ssh configuration files needed for these tests. + +use strict; +use warnings; +use Cwd; +use Cwd 'abs_path'; + +#*************************************************************************** +# Variables and subs imported from sshhelp module +# +use sshhelp qw( + $sshdexe + $sshexe + $sftpsrvexe + $sftpexe + $sshkeygenexe + $sshdconfig + $sshconfig + $sftpconfig + $knownhosts + $sshdlog + $sshlog + $sftplog + $sftpcmds + $hstprvkeyf + $hstpubkeyf + $cliprvkeyf + $clipubkeyf + display_sshdconfig + display_sshconfig + display_sftpconfig + display_sshdlog + display_sshlog + display_sftplog + dump_array + find_sshd + find_ssh + find_sftpsrv + find_sftp + find_sshkeygen + logmsg + sshversioninfo + ); + +#*************************************************************************** +# Subs imported from serverhelp module +# +use serverhelp qw( + server_pidfilename + server_logfilename + ); + +use pathhelp; + +#*************************************************************************** + +my $verbose = 0; # set to 1 for debugging +my $debugprotocol = 0; # set to 1 for protocol debugging +my $port = 8999; # our default SCP/SFTP server port +my $socksport = $port + 1; # our default SOCKS4/5 server port +my $listenaddr = '127.0.0.1'; # default address on which to listen +my $ipvnum = 4; # default IP version of listener address +my $idnum = 1; # dafault ssh daemon instance number +my $proto = 'ssh'; # protocol the ssh daemon speaks +my $path = getcwd(); # current working directory +my $logdir = $path .'/log'; # directory for log files +my $username = $ENV{USER}; # default user +my $pidfile; # ssh daemon pid file +my $identity = 'curl_client_key'; # default identity file + +my $error; +my @cfgarr; + + +#*************************************************************************** +# Parse command line options +# +while(@ARGV) { + if($ARGV[0] eq '--verbose') { + $verbose = 1; + } + elsif($ARGV[0] eq '--debugprotocol') { + $verbose = 1; + $debugprotocol = 1; + } + elsif($ARGV[0] eq '--user') { + if($ARGV[1]) { + $username = $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--id') { + if($ARGV[1]) { + if($ARGV[1] =~ /^(\d+)$/) { + $idnum = $1 if($1 > 0); + shift @ARGV; + } + } + } + elsif($ARGV[0] eq '--ipv4') { + $ipvnum = 4; + $listenaddr = '127.0.0.1' if($listenaddr eq '::1'); + } + elsif($ARGV[0] eq '--ipv6') { + $ipvnum = 6; + $listenaddr = '::1' if($listenaddr eq '127.0.0.1'); + } + elsif($ARGV[0] eq '--addr') { + if($ARGV[1]) { + my $tmpstr = $ARGV[1]; + if($tmpstr =~ /^(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)$/) { + $listenaddr = "$1.$2.$3.$4" if($ipvnum == 4); + shift @ARGV; + } + elsif($ipvnum == 6) { + $listenaddr = $tmpstr; + $listenaddr =~ s/^\[(.*)\]$/$1/; + shift @ARGV; + } + } + } + elsif($ARGV[0] eq '--pidfile') { + if($ARGV[1]) { + $pidfile = "$path/". $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--sshport') { + if($ARGV[1]) { + if($ARGV[1] =~ /^(\d+)$/) { + $port = $1; + shift @ARGV; + } + } + } + elsif($ARGV[0] eq '--socksport') { + if($ARGV[1]) { + if($ARGV[1] =~ /^(\d+)$/) { + $socksport = $1; + shift @ARGV; + } + } + } + else { + print STDERR "\nWarning: sshserver.pl unknown parameter: $ARGV[0]\n"; + } + shift @ARGV; +} + + +#*************************************************************************** +# Default ssh daemon pid file name +# +if(!$pidfile) { + $pidfile = "$path/". server_pidfilename($proto, $ipvnum, $idnum); +} + + +#*************************************************************************** +# ssh, socks and sftp server log file names +# +$sshdlog = server_logfilename($logdir, 'ssh', $ipvnum, $idnum); +$sftplog = server_logfilename($logdir, 'sftp', $ipvnum, $idnum); +$sshlog = server_logfilename($logdir, 'socks', $ipvnum, $idnum); + + +#*************************************************************************** +# Logging level for ssh server and client +# +my $loglevel = $debugprotocol?'DEBUG3':'DEBUG2'; + + +#*************************************************************************** +# Validate username +# +if(!$username) { + $error = 'Will not run ssh server without a user name'; +} +elsif($username eq 'root') { + $error = 'Will not run ssh server as root to mitigate security risks'; +} +if($error) { + logmsg $error; + exit 1; +} + + +#*************************************************************************** +# Find out ssh daemon canonical file name +# +my $sshd = find_sshd(); +if(!$sshd) { + logmsg "cannot find $sshdexe"; + exit 1; +} + + +#*************************************************************************** +# Find out ssh daemon version info +# +my ($sshdid, $sshdvernum, $sshdverstr, $sshderror) = sshversioninfo($sshd); +if(!$sshdid) { + # Not an OpenSSH or SunSSH ssh daemon + logmsg $sshderror if($verbose); + logmsg 'SCP, SFTP and SOCKS tests require OpenSSH 2.9.9 or later'; + exit 1; +} +logmsg "ssh server found $sshd is $sshdverstr" if($verbose); + + +#*************************************************************************** +# ssh daemon command line options we might use and version support +# +# -e: log stderr : OpenSSH 2.9.0 and later +# -f: sshd config file : OpenSSH 1.2.1 and later +# -D: no daemon forking : OpenSSH 2.5.0 and later +# -o: command-line option : OpenSSH 3.1.0 and later +# -t: test config file : OpenSSH 2.9.9 and later +# -?: sshd version info : OpenSSH 1.2.1 and later +# +# -e: log stderr : SunSSH 1.0.0 and later +# -f: sshd config file : SunSSH 1.0.0 and later +# -D: no daemon forking : SunSSH 1.0.0 and later +# -o: command-line option : SunSSH 1.0.0 and later +# -t: test config file : SunSSH 1.0.0 and later +# -?: sshd version info : SunSSH 1.0.0 and later + + +#*************************************************************************** +# Verify minimum ssh daemon version +# +if((($sshdid =~ /OpenSSH/) && ($sshdvernum < 299)) || + (($sshdid =~ /SunSSH/) && ($sshdvernum < 100))) { + logmsg 'SCP, SFTP and SOCKS tests require OpenSSH 2.9.9 or later'; + exit 1; +} + + +#*************************************************************************** +# Find out sftp server plugin canonical file name +# +my $sftpsrv = find_sftpsrv(); +if(!$sftpsrv) { + logmsg "cannot find $sftpsrvexe"; + exit 1; +} +logmsg "sftp server plugin found $sftpsrv" if($verbose); + + +#*************************************************************************** +# Find out sftp client canonical file name +# +my $sftp = find_sftp(); +if(!$sftp) { + logmsg "cannot find $sftpexe"; + exit 1; +} +logmsg "sftp client found $sftp" if($verbose); + + +#*************************************************************************** +# Find out ssh keygen canonical file name +# +my $sshkeygen = find_sshkeygen(); +if(!$sshkeygen) { + logmsg "cannot find $sshkeygenexe"; + exit 1; +} +logmsg "ssh keygen found $sshkeygen" if($verbose); + + +#*************************************************************************** +# Find out ssh client canonical file name +# +my $ssh = find_ssh(); +if(!$ssh) { + logmsg "cannot find $sshexe"; + exit 1; +} + + +#*************************************************************************** +# Find out ssh client version info +# +my ($sshid, $sshvernum, $sshverstr, $ssherror) = sshversioninfo($ssh); +if(!$sshid) { + # Not an OpenSSH or SunSSH ssh client + logmsg $ssherror if($verbose); + logmsg 'SCP, SFTP and SOCKS tests require OpenSSH 2.9.9 or later'; + exit 1; +} +logmsg "ssh client found $ssh is $sshverstr" if($verbose); + + +#*************************************************************************** +# ssh client command line options we might use and version support +# +# -D: dynamic app port forwarding : OpenSSH 2.9.9 and later +# -F: ssh config file : OpenSSH 2.9.9 and later +# -N: no shell/command : OpenSSH 2.1.0 and later +# -p: connection port : OpenSSH 1.2.1 and later +# -v: verbose messages : OpenSSH 1.2.1 and later +# -vv: increase verbosity : OpenSSH 2.3.0 and later +# -V: ssh version info : OpenSSH 1.2.1 and later +# +# -D: dynamic app port forwarding : SunSSH 1.0.0 and later +# -F: ssh config file : SunSSH 1.0.0 and later +# -N: no shell/command : SunSSH 1.0.0 and later +# -p: connection port : SunSSH 1.0.0 and later +# -v: verbose messages : SunSSH 1.0.0 and later +# -vv: increase verbosity : SunSSH 1.0.0 and later +# -V: ssh version info : SunSSH 1.0.0 and later + + +#*************************************************************************** +# Verify minimum ssh client version +# +if((($sshid =~ /OpenSSH/) && ($sshvernum < 299)) || + (($sshid =~ /SunSSH/) && ($sshvernum < 100))) { + logmsg 'SCP, SFTP and SOCKS tests require OpenSSH 2.9.9 or later'; + exit 1; +} + + +#*************************************************************************** +# ssh keygen command line options we actually use and version support +# +# -C: identity comment : OpenSSH 1.2.1 and later +# -f: key filename : OpenSSH 1.2.1 and later +# -N: new passphrase : OpenSSH 1.2.1 and later +# -q: quiet keygen : OpenSSH 1.2.1 and later +# -t: key type : OpenSSH 2.5.0 and later +# +# -C: identity comment : SunSSH 1.0.0 and later +# -f: key filename : SunSSH 1.0.0 and later +# -N: new passphrase : SunSSH 1.0.0 and later +# -q: quiet keygen : SunSSH 1.0.0 and later +# -t: key type : SunSSH 1.0.0 and later + + +#*************************************************************************** +# Generate host and client key files for curl's tests +# +if((! -e $hstprvkeyf) || (! -s $hstprvkeyf) || + (! -e $hstpubkeyf) || (! -s $hstpubkeyf) || + (! -e $cliprvkeyf) || (! -s $cliprvkeyf) || + (! -e $clipubkeyf) || (! -s $clipubkeyf)) { + # Make sure all files are gone so ssh-keygen doesn't complain + unlink($hstprvkeyf, $hstpubkeyf, $cliprvkeyf, $clipubkeyf); + logmsg 'generating host keys...' if($verbose); + if(system "\"$sshkeygen\" -q -t rsa -f $hstprvkeyf -C 'curl test server' -N ''") { + logmsg 'Could not generate host key'; + exit 1; + } + logmsg 'generating client keys...' if($verbose); + if(system "\"$sshkeygen\" -q -t rsa -f $cliprvkeyf -C 'curl test client' -N ''") { + logmsg 'Could not generate client key'; + exit 1; + } +} + + +#*************************************************************************** +# Convert paths for curl's tests running on Windows with Cygwin/Msys OpenSSH +# +my $clipubkeyf_config = abs_path("$path/$clipubkeyf"); +my $hstprvkeyf_config = abs_path("$path/$hstprvkeyf"); +my $pidfile_config = $pidfile; +my $sftpsrv_config = $sftpsrv; + +if ($^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys') { + # Ensure to use MinGW/Cygwin paths + $clipubkeyf_config = pathhelp::build_sys_abs_path($clipubkeyf_config); + $hstprvkeyf_config = pathhelp::build_sys_abs_path($hstprvkeyf_config); + $pidfile_config = pathhelp::build_sys_abs_path($pidfile_config); + $sftpsrv_config = "internal-sftp"; +} + +#*************************************************************************** +# ssh daemon configuration file options we might use and version support +# +# AFSTokenPassing : OpenSSH 1.2.1 and later [1] +# AcceptEnv : OpenSSH 3.9.0 and later +# AddressFamily : OpenSSH 4.0.0 and later +# AllowGroups : OpenSSH 1.2.1 and later +# AllowTcpForwarding : OpenSSH 2.3.0 and later +# AllowUsers : OpenSSH 1.2.1 and later +# AuthorizedKeysFile : OpenSSH 2.9.9 and later +# AuthorizedKeysFile2 : OpenSSH 2.9.9 and later +# Banner : OpenSSH 2.5.0 and later +# ChallengeResponseAuthentication : OpenSSH 2.5.0 and later +# Ciphers : OpenSSH 2.1.0 and later [3] +# ClientAliveCountMax : OpenSSH 2.9.0 and later +# ClientAliveInterval : OpenSSH 2.9.0 and later +# Compression : OpenSSH 3.3.0 and later +# DenyGroups : OpenSSH 1.2.1 and later +# DenyUsers : OpenSSH 1.2.1 and later +# ForceCommand : OpenSSH 4.4.0 and later [3] +# GatewayPorts : OpenSSH 2.1.0 and later +# GSSAPIAuthentication : OpenSSH 3.7.0 and later [1] +# GSSAPICleanupCredentials : OpenSSH 3.8.0 and later [1] +# GSSAPIKeyExchange : SunSSH 1.0.0 and later [1] +# GSSAPIStoreDelegatedCredentials : SunSSH 1.0.0 and later [1] +# GSSCleanupCreds : SunSSH 1.0.0 and later [1] +# GSSUseSessionCredCache : SunSSH 1.0.0 and later [1] +# HostbasedAuthentication : OpenSSH 2.9.0 and later +# HostbasedUsesNameFromPacketOnly : OpenSSH 2.9.0 and later +# HostKey : OpenSSH 1.2.1 and later +# IgnoreRhosts : OpenSSH 1.2.1 and later +# IgnoreUserKnownHosts : OpenSSH 1.2.1 and later +# KbdInteractiveAuthentication : OpenSSH 2.3.0 and later +# KeepAlive : OpenSSH 1.2.1 and later +# KerberosAuthentication : OpenSSH 1.2.1 and later [1] +# KerberosGetAFSToken : OpenSSH 3.8.0 and later [1] +# KerberosOrLocalPasswd : OpenSSH 1.2.1 and later [1] +# KerberosTgtPassing : OpenSSH 1.2.1 and later [1] +# KerberosTicketCleanup : OpenSSH 1.2.1 and later [1] +# KeyRegenerationInterval : OpenSSH 1.2.1 and later +# ListenAddress : OpenSSH 1.2.1 and later +# LoginGraceTime : OpenSSH 1.2.1 and later +# LogLevel : OpenSSH 1.2.1 and later +# LookupClientHostnames : SunSSH 1.0.0 and later +# MACs : OpenSSH 2.5.0 and later [3] +# Match : OpenSSH 4.4.0 and later [3] +# MaxAuthTries : OpenSSH 3.9.0 and later +# MaxStartups : OpenSSH 2.2.0 and later +# PAMAuthenticationViaKbdInt : OpenSSH 2.9.0 and later [2] +# PasswordAuthentication : OpenSSH 1.2.1 and later +# PermitEmptyPasswords : OpenSSH 1.2.1 and later +# PermitOpen : OpenSSH 4.4.0 and later [3] +# PermitRootLogin : OpenSSH 1.2.1 and later +# PermitTunnel : OpenSSH 4.3.0 and later +# PermitUserEnvironment : OpenSSH 3.5.0 and later +# PidFile : OpenSSH 2.1.0 and later +# Port : OpenSSH 1.2.1 and later +# PrintLastLog : OpenSSH 2.9.0 and later +# PrintMotd : OpenSSH 1.2.1 and later +# Protocol : OpenSSH 2.1.0 and later +# PubkeyAuthentication : OpenSSH 2.5.0 and later +# RhostsAuthentication : OpenSSH 1.2.1 and later +# RhostsRSAAuthentication : OpenSSH 1.2.1 and later +# RSAAuthentication : OpenSSH 1.2.1 and later +# ServerKeyBits : OpenSSH 1.2.1 and later +# SkeyAuthentication : OpenSSH 1.2.1 and later [1] +# StrictModes : OpenSSH 1.2.1 and later +# Subsystem : OpenSSH 2.2.0 and later +# SyslogFacility : OpenSSH 1.2.1 and later +# TCPKeepAlive : OpenSSH 3.8.0 and later +# UseDNS : OpenSSH 3.7.0 and later +# UseLogin : OpenSSH 1.2.1 and later +# UsePAM : OpenSSH 3.7.0 and later [1][2] +# UsePrivilegeSeparation : OpenSSH 3.2.2 and later +# VerifyReverseMapping : OpenSSH 3.1.0 and later +# X11DisplayOffset : OpenSSH 1.2.1 and later [3] +# X11Forwarding : OpenSSH 1.2.1 and later +# X11UseLocalhost : OpenSSH 3.1.0 and later +# XAuthLocation : OpenSSH 2.1.1 and later [3] +# +# [1] Option only available if activated at compile time +# [2] Option specific for portable versions +# [3] Option not used in our ssh server config file + + +#*************************************************************************** +# Initialize sshd config with options actually supported in OpenSSH 2.9.9 +# +logmsg 'generating ssh server config file...' if($verbose); +@cfgarr = (); +push @cfgarr, '# This is a generated file. Do not edit.'; +push @cfgarr, "# $sshdverstr sshd configuration file for curl testing"; +push @cfgarr, '#'; +push @cfgarr, "DenyUsers !$username"; +push @cfgarr, "AllowUsers $username"; +push @cfgarr, 'DenyGroups'; +push @cfgarr, 'AllowGroups'; +push @cfgarr, '#'; +push @cfgarr, "AuthorizedKeysFile $clipubkeyf_config"; +push @cfgarr, "AuthorizedKeysFile2 $clipubkeyf_config"; +push @cfgarr, "HostKey $hstprvkeyf_config"; +push @cfgarr, "PidFile $pidfile_config"; +push @cfgarr, '#'; +push @cfgarr, "Port $port"; +push @cfgarr, "ListenAddress $listenaddr"; +push @cfgarr, 'Protocol 2'; +push @cfgarr, '#'; +push @cfgarr, 'AllowTcpForwarding yes'; +push @cfgarr, 'Banner none'; +push @cfgarr, 'ChallengeResponseAuthentication no'; +push @cfgarr, 'ClientAliveCountMax 3'; +push @cfgarr, 'ClientAliveInterval 0'; +push @cfgarr, 'GatewayPorts no'; +push @cfgarr, 'HostbasedAuthentication no'; +push @cfgarr, 'HostbasedUsesNameFromPacketOnly no'; +push @cfgarr, 'IgnoreRhosts yes'; +push @cfgarr, 'IgnoreUserKnownHosts yes'; +push @cfgarr, 'KeyRegenerationInterval 0'; +push @cfgarr, 'LoginGraceTime 30'; +push @cfgarr, "LogLevel $loglevel"; +push @cfgarr, 'MaxStartups 5'; +push @cfgarr, 'PasswordAuthentication no'; +push @cfgarr, 'PermitEmptyPasswords no'; +push @cfgarr, 'PermitRootLogin no'; +push @cfgarr, 'PrintLastLog no'; +push @cfgarr, 'PrintMotd no'; +push @cfgarr, 'PubkeyAuthentication yes'; +push @cfgarr, 'RhostsRSAAuthentication no'; +push @cfgarr, 'RSAAuthentication no'; +push @cfgarr, 'ServerKeyBits 768'; +push @cfgarr, 'StrictModes no'; +push @cfgarr, "Subsystem sftp \"$sftpsrv_config\""; +push @cfgarr, 'SyslogFacility AUTH'; +push @cfgarr, 'UseLogin no'; +push @cfgarr, 'X11Forwarding no'; +push @cfgarr, '#'; + + +#*************************************************************************** +# Write out initial sshd configuration file for curl's tests +# +$error = dump_array($sshdconfig, @cfgarr); +if($error) { + logmsg $error; + exit 1; +} + + +#*************************************************************************** +# Verifies at run time if sshd supports a given configuration file option +# +sub sshd_supports_opt { + my ($option, $value) = @_; + my $err; + # + if((($sshdid =~ /OpenSSH/) && ($sshdvernum >= 310)) || + ($sshdid =~ /SunSSH/)) { + # ssh daemon supports command line options -t -f and -o + $err = grep /((Unsupported)|(Bad configuration)|(Deprecated)) option.*$option/, + qx("$sshd" -t -f $sshdconfig -o "$option=$value" 2>&1); + return !$err; + } + if(($sshdid =~ /OpenSSH/) && ($sshdvernum >= 299)) { + # ssh daemon supports command line options -t and -f + $err = dump_array($sshdconfig, (@cfgarr, "$option $value")); + if($err) { + logmsg $err; + return 0; + } + $err = grep /((Unsupported)|(Bad configuration)|(Deprecated)) option.*$option/, + qx("$sshd" -t -f $sshdconfig 2>&1); + unlink $sshdconfig; + return !$err; + } + return 0; +} + + +#*************************************************************************** +# Kerberos Authentication support may have not been built into sshd +# +if(sshd_supports_opt('KerberosAuthentication','no')) { + push @cfgarr, 'KerberosAuthentication no'; +} +if(sshd_supports_opt('KerberosGetAFSToken','no')) { + push @cfgarr, 'KerberosGetAFSToken no'; +} +if(sshd_supports_opt('KerberosOrLocalPasswd','no')) { + push @cfgarr, 'KerberosOrLocalPasswd no'; +} +if(sshd_supports_opt('KerberosTgtPassing','no')) { + push @cfgarr, 'KerberosTgtPassing no'; +} +if(sshd_supports_opt('KerberosTicketCleanup','yes')) { + push @cfgarr, 'KerberosTicketCleanup yes'; +} + + +#*************************************************************************** +# Andrew File System support may have not been built into sshd +# +if(sshd_supports_opt('AFSTokenPassing','no')) { + push @cfgarr, 'AFSTokenPassing no'; +} + + +#*************************************************************************** +# S/Key authentication support may have not been built into sshd +# +if(sshd_supports_opt('SkeyAuthentication','no')) { + push @cfgarr, 'SkeyAuthentication no'; +} + + +#*************************************************************************** +# GSSAPI Authentication support may have not been built into sshd +# +my $sshd_builtwith_GSSAPI; +if(sshd_supports_opt('GSSAPIAuthentication','no')) { + push @cfgarr, 'GSSAPIAuthentication no'; + $sshd_builtwith_GSSAPI = 1; +} +if(sshd_supports_opt('GSSAPICleanupCredentials','yes')) { + push @cfgarr, 'GSSAPICleanupCredentials yes'; +} +if(sshd_supports_opt('GSSAPIKeyExchange','no')) { + push @cfgarr, 'GSSAPIKeyExchange no'; +} +if(sshd_supports_opt('GSSAPIStoreDelegatedCredentials','no')) { + push @cfgarr, 'GSSAPIStoreDelegatedCredentials no'; +} +if(sshd_supports_opt('GSSCleanupCreds','yes')) { + push @cfgarr, 'GSSCleanupCreds yes'; +} +if(sshd_supports_opt('GSSUseSessionCredCache','no')) { + push @cfgarr, 'GSSUseSessionCredCache no'; +} +push @cfgarr, '#'; + + +#*************************************************************************** +# Options that might be supported or not in sshd OpenSSH 2.9.9 and later +# +if(sshd_supports_opt('AcceptEnv','')) { + push @cfgarr, 'AcceptEnv'; +} +if(sshd_supports_opt('AddressFamily','any')) { + # Address family must be specified before ListenAddress + splice @cfgarr, 14, 0, 'AddressFamily any'; +} +if(sshd_supports_opt('Compression','no')) { + push @cfgarr, 'Compression no'; +} +if(sshd_supports_opt('KbdInteractiveAuthentication','no')) { + push @cfgarr, 'KbdInteractiveAuthentication no'; +} +if(sshd_supports_opt('KeepAlive','no')) { + push @cfgarr, 'KeepAlive no'; +} +if(sshd_supports_opt('LookupClientHostnames','no')) { + push @cfgarr, 'LookupClientHostnames no'; +} +if(sshd_supports_opt('MaxAuthTries','10')) { + push @cfgarr, 'MaxAuthTries 10'; +} +if(sshd_supports_opt('PAMAuthenticationViaKbdInt','no')) { + push @cfgarr, 'PAMAuthenticationViaKbdInt no'; +} +if(sshd_supports_opt('PermitTunnel','no')) { + push @cfgarr, 'PermitTunnel no'; +} +if(sshd_supports_opt('PermitUserEnvironment','no')) { + push @cfgarr, 'PermitUserEnvironment no'; +} +if(sshd_supports_opt('RhostsAuthentication','no')) { + push @cfgarr, 'RhostsAuthentication no'; +} +if(sshd_supports_opt('TCPKeepAlive','no')) { + push @cfgarr, 'TCPKeepAlive no'; +} +if(sshd_supports_opt('UseDNS','no')) { + push @cfgarr, 'UseDNS no'; +} +if(sshd_supports_opt('UsePAM','no')) { + push @cfgarr, 'UsePAM no'; +} + +if($sshdid =~ /OpenSSH/) { + # http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6492415 + if(sshd_supports_opt('UsePrivilegeSeparation','no')) { + push @cfgarr, 'UsePrivilegeSeparation no'; + } +} + +if(sshd_supports_opt('VerifyReverseMapping','no')) { + push @cfgarr, 'VerifyReverseMapping no'; +} +if(sshd_supports_opt('X11UseLocalhost','yes')) { + push @cfgarr, 'X11UseLocalhost yes'; +} +push @cfgarr, '#'; + + +#*************************************************************************** +# Write out resulting sshd configuration file for curl's tests +# +$error = dump_array($sshdconfig, @cfgarr); +if($error) { + logmsg $error; + exit 1; +} + + +#*************************************************************************** +# Verify that sshd actually supports our generated configuration file +# +if(system "\"$sshd\" -t -f $sshdconfig > $sshdlog 2>&1") { + logmsg "sshd configuration file $sshdconfig failed verification"; + display_sshdlog(); + display_sshdconfig(); + exit 1; +} + + +#*************************************************************************** +# Generate ssh client host key database file for curl's tests +# +if((! -e $knownhosts) || (! -s $knownhosts)) { + logmsg 'generating ssh client known hosts file...' if($verbose); + unlink($knownhosts); + if(open(RSAKEYFILE, "<$hstpubkeyf")) { + my @rsahostkey = do { local $/ = ' '; }; + if(close(RSAKEYFILE)) { + if(open(KNOWNHOSTS, ">$knownhosts")) { + print KNOWNHOSTS "$listenaddr ssh-rsa $rsahostkey[1]\n"; + if(!close(KNOWNHOSTS)) { + $error = "Error: cannot close file $knownhosts"; + } + } + else { + $error = "Error: cannot write file $knownhosts"; + } + } + else { + $error = "Error: cannot close file $hstpubkeyf"; + } + } + else { + $error = "Error: cannot read file $hstpubkeyf"; + } + if($error) { + logmsg $error; + exit 1; + } +} + + +#*************************************************************************** +# Convert paths for curl's tests running on Windows using Cygwin OpenSSH +# +my $identity_config = abs_path("$path/$identity"); +my $knownhosts_config = abs_path("$path/$knownhosts"); + +if ($^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys') { + # Ensure to use MinGW/Cygwin paths + $identity_config = pathhelp::build_sys_abs_path($identity_config); + $knownhosts_config = pathhelp::build_sys_abs_path($knownhosts_config); +} + + +#*************************************************************************** +# ssh client configuration file options we might use and version support +# +# AddressFamily : OpenSSH 3.7.0 and later +# BatchMode : OpenSSH 1.2.1 and later +# BindAddress : OpenSSH 2.9.9 and later +# ChallengeResponseAuthentication : OpenSSH 2.5.0 and later +# CheckHostIP : OpenSSH 1.2.1 and later +# Cipher : OpenSSH 1.2.1 and later [3] +# Ciphers : OpenSSH 2.1.0 and later [3] +# ClearAllForwardings : OpenSSH 2.9.9 and later +# Compression : OpenSSH 1.2.1 and later +# CompressionLevel : OpenSSH 1.2.1 and later [3] +# ConnectionAttempts : OpenSSH 1.2.1 and later +# ConnectTimeout : OpenSSH 3.7.0 and later +# ControlMaster : OpenSSH 3.9.0 and later +# ControlPath : OpenSSH 3.9.0 and later +# DisableBanner : SunSSH 1.2.0 and later +# DynamicForward : OpenSSH 2.9.0 and later +# EnableSSHKeysign : OpenSSH 3.6.0 and later +# EscapeChar : OpenSSH 1.2.1 and later [3] +# ExitOnForwardFailure : OpenSSH 4.4.0 and later +# ForwardAgent : OpenSSH 1.2.1 and later +# ForwardX11 : OpenSSH 1.2.1 and later +# ForwardX11Trusted : OpenSSH 3.8.0 and later +# GatewayPorts : OpenSSH 1.2.1 and later +# GlobalKnownHostsFile : OpenSSH 1.2.1 and later +# GSSAPIAuthentication : OpenSSH 3.7.0 and later [1] +# GSSAPIDelegateCredentials : OpenSSH 3.7.0 and later [1] +# HashKnownHosts : OpenSSH 4.0.0 and later +# Host : OpenSSH 1.2.1 and later +# HostbasedAuthentication : OpenSSH 2.9.0 and later +# HostKeyAlgorithms : OpenSSH 2.9.0 and later [3] +# HostKeyAlias : OpenSSH 2.5.0 and later [3] +# HostName : OpenSSH 1.2.1 and later +# IdentitiesOnly : OpenSSH 3.9.0 and later +# IdentityFile : OpenSSH 1.2.1 and later +# IgnoreIfUnknown : SunSSH 1.2.0 and later +# KeepAlive : OpenSSH 1.2.1 and later +# KbdInteractiveAuthentication : OpenSSH 2.3.0 and later +# KbdInteractiveDevices : OpenSSH 2.3.0 and later [3] +# LocalCommand : OpenSSH 4.3.0 and later [3] +# LocalForward : OpenSSH 1.2.1 and later [3] +# LogLevel : OpenSSH 1.2.1 and later +# MACs : OpenSSH 2.5.0 and later [3] +# NoHostAuthenticationForLocalhost : OpenSSH 3.0.0 and later +# NumberOfPasswordPrompts : OpenSSH 1.2.1 and later +# PasswordAuthentication : OpenSSH 1.2.1 and later +# PermitLocalCommand : OpenSSH 4.3.0 and later +# Port : OpenSSH 1.2.1 and later +# PreferredAuthentications : OpenSSH 2.5.2 and later +# Protocol : OpenSSH 2.1.0 and later +# ProxyCommand : OpenSSH 1.2.1 and later [3] +# PubkeyAuthentication : OpenSSH 2.5.0 and later +# RekeyLimit : OpenSSH 3.7.0 and later +# RemoteForward : OpenSSH 1.2.1 and later [3] +# RhostsRSAAuthentication : OpenSSH 1.2.1 and later +# RSAAuthentication : OpenSSH 1.2.1 and later +# SendEnv : OpenSSH 3.9.0 and later +# ServerAliveCountMax : OpenSSH 3.8.0 and later +# ServerAliveInterval : OpenSSH 3.8.0 and later +# SmartcardDevice : OpenSSH 2.9.9 and later [1][3] +# StrictHostKeyChecking : OpenSSH 1.2.1 and later +# TCPKeepAlive : OpenSSH 3.8.0 and later +# Tunnel : OpenSSH 4.3.0 and later +# TunnelDevice : OpenSSH 4.3.0 and later [3] +# UsePAM : OpenSSH 3.7.0 and later [1][2][3] +# UsePrivilegedPort : OpenSSH 1.2.1 and later +# User : OpenSSH 1.2.1 and later +# UserKnownHostsFile : OpenSSH 1.2.1 and later +# VerifyHostKeyDNS : OpenSSH 3.8.0 and later +# XAuthLocation : OpenSSH 2.1.1 and later [3] +# +# [1] Option only available if activated at compile time +# [2] Option specific for portable versions +# [3] Option not used in our ssh client config file + + +#*************************************************************************** +# Initialize ssh config with options actually supported in OpenSSH 2.9.9 +# +logmsg 'generating ssh client config file...' if($verbose); +@cfgarr = (); +push @cfgarr, '# This is a generated file. Do not edit.'; +push @cfgarr, "# $sshverstr ssh client configuration file for curl testing"; +push @cfgarr, '#'; +push @cfgarr, 'Host *'; +push @cfgarr, '#'; +push @cfgarr, "Port $port"; +push @cfgarr, "HostName $listenaddr"; +push @cfgarr, "User $username"; +push @cfgarr, 'Protocol 2'; +push @cfgarr, '#'; +push @cfgarr, "BindAddress $listenaddr"; +push @cfgarr, "DynamicForward $socksport"; +push @cfgarr, '#'; +push @cfgarr, "IdentityFile $identity_config"; +push @cfgarr, "UserKnownHostsFile $knownhosts_config"; +push @cfgarr, '#'; +push @cfgarr, 'BatchMode yes'; +push @cfgarr, 'ChallengeResponseAuthentication no'; +push @cfgarr, 'CheckHostIP no'; +push @cfgarr, 'ClearAllForwardings no'; +push @cfgarr, 'Compression no'; +push @cfgarr, 'ConnectionAttempts 3'; +push @cfgarr, 'ForwardAgent no'; +push @cfgarr, 'ForwardX11 no'; +push @cfgarr, 'GatewayPorts no'; +push @cfgarr, 'GlobalKnownHostsFile /dev/null'; +push @cfgarr, 'HostbasedAuthentication no'; +push @cfgarr, 'KbdInteractiveAuthentication no'; +push @cfgarr, "LogLevel $loglevel"; +push @cfgarr, 'NumberOfPasswordPrompts 0'; +push @cfgarr, 'PasswordAuthentication no'; +push @cfgarr, 'PreferredAuthentications publickey'; +push @cfgarr, 'PubkeyAuthentication yes'; +push @cfgarr, 'RhostsRSAAuthentication no'; +push @cfgarr, 'RSAAuthentication no'; + +# Disabled StrictHostKeyChecking since it makes the tests fail on my +# OpenSSH_6.0p1 on Debian Linux / Daniel +push @cfgarr, 'StrictHostKeyChecking no'; +push @cfgarr, 'UsePrivilegedPort no'; +push @cfgarr, '#'; + + +#*************************************************************************** +# Options supported in ssh client newer than OpenSSH 2.9.9 +# + +if(($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) { + push @cfgarr, 'AddressFamily any'; +} + +if((($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) || + (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) { + push @cfgarr, 'ConnectTimeout 30'; +} + +if(($sshid =~ /OpenSSH/) && ($sshvernum >= 390)) { + push @cfgarr, 'ControlMaster no'; +} + +if(($sshid =~ /OpenSSH/) && ($sshvernum >= 420)) { + push @cfgarr, 'ControlPath none'; +} + +if(($sshid =~ /SunSSH/) && ($sshvernum >= 120)) { + push @cfgarr, 'DisableBanner yes'; +} + +if(($sshid =~ /OpenSSH/) && ($sshvernum >= 360)) { + push @cfgarr, 'EnableSSHKeysign no'; +} + +if(($sshid =~ /OpenSSH/) && ($sshvernum >= 440)) { + push @cfgarr, 'ExitOnForwardFailure yes'; +} + +if((($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) || + (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) { + push @cfgarr, 'ForwardX11Trusted no'; +} + +if(($sshd_builtwith_GSSAPI) && ($sshdid eq $sshid) && + ($sshdvernum == $sshvernum)) { + push @cfgarr, 'GSSAPIAuthentication no'; + push @cfgarr, 'GSSAPIDelegateCredentials no'; + if($sshid =~ /SunSSH/) { + push @cfgarr, 'GSSAPIKeyExchange no'; + } +} + +if((($sshid =~ /OpenSSH/) && ($sshvernum >= 400)) || + (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) { + push @cfgarr, 'HashKnownHosts no'; +} + +if(($sshid =~ /OpenSSH/) && ($sshvernum >= 390)) { + push @cfgarr, 'IdentitiesOnly yes'; +} + +if(($sshid =~ /SunSSH/) && ($sshvernum >= 120)) { + push @cfgarr, 'IgnoreIfUnknown no'; +} + +if((($sshid =~ /OpenSSH/) && ($sshvernum < 380)) || + ($sshid =~ /SunSSH/)) { + push @cfgarr, 'KeepAlive no'; +} + +if((($sshid =~ /OpenSSH/) && ($sshvernum >= 300)) || + ($sshid =~ /SunSSH/)) { + push @cfgarr, 'NoHostAuthenticationForLocalhost no'; +} + +if(($sshid =~ /OpenSSH/) && ($sshvernum >= 430)) { + push @cfgarr, 'PermitLocalCommand no'; +} + +if((($sshid =~ /OpenSSH/) && ($sshvernum >= 370)) || + (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) { + push @cfgarr, 'RekeyLimit 1G'; +} + +if(($sshid =~ /OpenSSH/) && ($sshvernum >= 390)) { + push @cfgarr, 'SendEnv'; +} + +if((($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) || + (($sshid =~ /SunSSH/) && ($sshvernum >= 120))) { + push @cfgarr, 'ServerAliveCountMax 3'; + push @cfgarr, 'ServerAliveInterval 0'; +} + +if(($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) { + push @cfgarr, 'TCPKeepAlive no'; +} + +if(($sshid =~ /OpenSSH/) && ($sshvernum >= 430)) { + push @cfgarr, 'Tunnel no'; +} + +if(($sshid =~ /OpenSSH/) && ($sshvernum >= 380)) { + push @cfgarr, 'VerifyHostKeyDNS no'; +} + +push @cfgarr, '#'; + + +#*************************************************************************** +# Write out resulting ssh client configuration file for curl's tests +# +$error = dump_array($sshconfig, @cfgarr); +if($error) { + logmsg $error; + exit 1; +} + + +#*************************************************************************** +# Initialize client sftp config with options actually supported. +# +logmsg 'generating sftp client config file...' if($verbose); +splice @cfgarr, 1, 1, "# $sshverstr sftp client configuration file for curl testing"; +# +for(my $i = scalar(@cfgarr) - 1; $i > 0; $i--) { + if($cfgarr[$i] =~ /^DynamicForward/) { + splice @cfgarr, $i, 1; + next; + } + if($cfgarr[$i] =~ /^ClearAllForwardings/) { + splice @cfgarr, $i, 1, "ClearAllForwardings yes"; + next; + } +} + + +#*************************************************************************** +# Write out resulting sftp client configuration file for curl's tests +# +$error = dump_array($sftpconfig, @cfgarr); +if($error) { + logmsg $error; + exit 1; +} +@cfgarr = (); + + +#*************************************************************************** +# Generate client sftp commands batch file for sftp server verification +# +logmsg 'generating sftp client commands file...' if($verbose); +push @cfgarr, 'pwd'; +push @cfgarr, 'quit'; +$error = dump_array($sftpcmds, @cfgarr); +if($error) { + logmsg $error; + exit 1; +} +@cfgarr = (); + + +#*************************************************************************** +# Start the ssh server daemon without forking it +# +logmsg "SCP/SFTP server listening on port $port" if($verbose); +my $rc = system "\"$sshd\" -e -D -f $sshdconfig > $sshdlog 2>&1"; +if($rc == -1) { + logmsg "\"$sshd\" failed with: $!"; +} +elsif($rc & 127) { + logmsg sprintf("\"$sshd\" died with signal %d, and %s coredump", + ($rc & 127), ($rc & 128)?'a':'no'); +} +elsif($verbose && ($rc >> 8)) { + logmsg sprintf("\"$sshd\" exited with %d", $rc >> 8); +} + + +#*************************************************************************** +# Clean up once the server has stopped +# +unlink($hstprvkeyf, $hstpubkeyf, $cliprvkeyf, $clipubkeyf, $knownhosts); +unlink($sshdconfig, $sshconfig, $sftpconfig); + + +exit 0; diff --git a/deps/curl-7.51.0/tests/stunnel.pem b/deps/curl-7.51.0/tests/stunnel.pem new file mode 100644 index 0000000000..d9b9679ebb --- /dev/null +++ b/deps/curl-7.51.0/tests/stunnel.pem @@ -0,0 +1,143 @@ +# +# This file contains a private key and a certificate used for stunnel. +# The certificate contains a number of extensions essentially being +# used in the 509 test. The certificate has been generated using +# openssl with the parameters listed below up to the line +# contain [something], after that you find the result. +# +# +extensions = x509v3 +[ x509v3 ] +subjectAltName = DNS:localhost +nsCertType = server +nsComment = "CURL stunnel server test certificate" +keyUsage = digitalSignature,keyEncipherment +extendedKeyUsage = serverAuth +basicConstraints = CA:false +subjectKeyIdentifier = hash +subjectInfoAccess = AD_DVCS;URI:"https://localhost:8433/509" +authorityInfoAccess = AD_DVCS;URI:"https://localhost:8433/509" +[ req ] +default_bits = 1234 +distinguished_name = req_DN +default_md = sha1 +string_mask = pkix +[ req_DN ] +countryName = "1. Country Name (2 letter code)" +countryName_value = SE +stateOrProvinceName = "2. State or Province Name (full name) " +stateOrProvinceName_value = Solna +localityName = "3. Locality Name (eg, city) " +localityName_value = Mooo +0.organizationName = "4. Organization Name (eg, company) " +0.organizationName_value = Haxx +organizationalUnitName = "5. Organizational Unit Name (eg, section) " +organizationalUnitName_value = Coolx +commonName = "6. Common Name (eg, FQDN) " +commonName_value = "storbror" +1.commonName = "6. Common Name (eg, FQDN) " +1.commonName_value = "localhost" +[something] +-----BEGIN RSA PRIVATE KEY----- +MIIC1AIBAAKBmwNZN+oG6vJ8DAze+FvOKSS49X4xGMxALhKRLhQQb7qvM+7BcMgR +v+RKxkX7SNgcxKPLcIHf7QQ6DBIlLXuAuVHQtWW9b06q64kBElkEwh6gP5Ia9JrR +ysGbu2U6NRP+xBU33dVwZjF07ocN9Pp392W4VxEc+g3+FkRzUEaahDGOabmjgKuq +DdlKdZLzgJj7+9sEKpb7+FdG56rZAgMBAAECgZsCkK1Z1XTUz5x3m7PMuHEiVaKS +yk/B4ISq6pbO/gxpieARzhR038wNug6L+8VA8UDebXHBvGYYr9Mhb2OZUfIlr+nW +h7kmHZ+T88M3eH/hQc3jtnvnu1dGmMlIXjTLQOrKgrAn6fYaw2HAGPdGKjpatAy/ +3vRjguv/22pNJLRQmMHdozJdc8mEYY+AhqrQxXCWQT/1peZzlq/IAQJOAfhE2YWf +qB9iYNmuhxJ1PolPW4I63atXuoavqadbaRoaLm/pqLVB1QjMeyak8O/0TmO6CXk6 +878ps85fLFgARRjSYX+rYwoYNzqxK3cBAk4Bsy4oofReVT8xB+7rFZFMV4McyL7e +sOABFqecLuNIGT6CdeEU1z7TUfq8sKM1MQ25e0J1PMmoWTqDwzhnxK+ckeFsZ8Te +dgqVW+Oyy9kCTgHqyc/P/uEZkp1ioDu0WkpAR+1vZa2jeyH+vm9nhE9Z6Uty/r6F +k4otIx9lMDmTwXqeE03vINJlJshqvjShfbnCe9gK8xrUk1cFl7QPAQJOATD3LQRq +At2MniioFtiTbUN6n2ZS1C5xnHGq3fnBzxnZw4UmSfuZjG/L3gWPKkyJCK3HYe9K +ho6ZQhNB6P5d7sQQjG6f+SIRwp+VjwvpAk4AnM4do54FETeLHhY4zy47dM/zdy3u +iDjiFwoMTR+PfF03evsWe5pW3EaXolGi3FRAZ/idFA+L3Gi2y4xR44z71HkbF32L +WKaLdOuBQvI= +-----END RSA PRIVATE KEY----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + a4:17:70:09:88:8c:48:cd + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=SE, ST=Solna, L=Mooo, O=Haxx, OU=Coolx, CN=storbror, CN=localhost + Validity + Not Before: Feb 22 15:38:48 2014 GMT + Not After : Feb 20 15:38:48 2024 GMT + Subject: C=SE, ST=Solna, L=Mooo, O=Haxx, OU=Coolx, CN=storbror, CN=localhost + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1234 bit) + Modulus: + 03:59:37:ea:06:ea:f2:7c:0c:0c:de:f8:5b:ce:29: + 24:b8:f5:7e:31:18:cc:40:2e:12:91:2e:14:10:6f: + ba:af:33:ee:c1:70:c8:11:bf:e4:4a:c6:45:fb:48: + d8:1c:c4:a3:cb:70:81:df:ed:04:3a:0c:12:25:2d: + 7b:80:b9:51:d0:b5:65:bd:6f:4e:aa:eb:89:01:12: + 59:04:c2:1e:a0:3f:92:1a:f4:9a:d1:ca:c1:9b:bb: + 65:3a:35:13:fe:c4:15:37:dd:d5:70:66:31:74:ee: + 87:0d:f4:fa:77:f7:65:b8:57:11:1c:fa:0d:fe:16: + 44:73:50:46:9a:84:31:8e:69:b9:a3:80:ab:aa:0d: + d9:4a:75:92:f3:80:98:fb:fb:db:04:2a:96:fb:f8: + 57:46:e7:aa:d9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:localhost + Netscape Cert Type: + SSL Server + Netscape Comment: + CURL stunnel server test certificate + X509v3 Key Usage: + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + 35:77:35:3B:9B:98:3C:B6:C7:9A:E7:A8:04:B9:7C:70:AD:FA:37:A9 + Subject Information Access: + ad dvcs - URI:https://localhost:8433/509 + + Authority Information Access: + ad dvcs - URI:https://localhost:8433/509 + + Signature Algorithm: sha1WithRSAEncryption + 00:45:db:09:5b:08:5b:1a:ff:71:50:6c:12:ad:8e:78:32:1d: + 7d:e7:e4:d3:3e:5f:ca:20:84:aa:ff:9a:c2:b6:a9:48:93:1f: + 73:27:d1:68:05:76:36:f9:c1:53:90:ad:8a:c0:b3:12:c8:11: + 5c:2c:65:01:ac:31:d1:8e:60:6e:c6:f5:ba:9d:69:e8:f1:ac: + 4a:de:52:94:cd:06:24:45:72:64:89:0f:57:8b:26:2b:16:cf: + 0b:27:c4:e8:73:c7:d3:e5:42:38:95:57:b5:bb:83:b4:92:d4: + e0:cd:fb:c8:f5:d2:da:1d:11:fe:3c:18:20:8b:bd:22:31:1c: + 5a:82:d4:f5:71:8d:8a:e3:13:82:c5:2d:f3:9f:d0:b7:b8:4b: + d2:46:9d:8e:1a:d7:99:6e:c1:b9:a0 +-----BEGIN CERTIFICATE----- +MIIDtzCCAwWgAwIBAgIJAKQXcAmIjEjNMA0GCSqGSIb3DQEBBQUAMHIxCzAJBgNV +BAYTAlNFMQ4wDAYDVQQIEwVTb2xuYTENMAsGA1UEBxMETW9vbzENMAsGA1UEChME +SGF4eDEOMAwGA1UECxMFQ29vbHgxETAPBgNVBAMTCHN0b3Jicm9yMRIwEAYDVQQD +Ewlsb2NhbGhvc3QwHhcNMTQwMjIyMTUzODQ4WhcNMjQwMjIwMTUzODQ4WjByMQsw +CQYDVQQGEwJTRTEOMAwGA1UECBMFU29sbmExDTALBgNVBAcTBE1vb28xDTALBgNV +BAoTBEhheHgxDjAMBgNVBAsTBUNvb2x4MREwDwYDVQQDEwhzdG9yYnJvcjESMBAG +A1UEAxMJbG9jYWxob3N0MIG5MA0GCSqGSIb3DQEBAQUAA4GnADCBowKBmwNZN+oG +6vJ8DAze+FvOKSS49X4xGMxALhKRLhQQb7qvM+7BcMgRv+RKxkX7SNgcxKPLcIHf +7QQ6DBIlLXuAuVHQtWW9b06q64kBElkEwh6gP5Ia9JrRysGbu2U6NRP+xBU33dVw +ZjF07ocN9Pp392W4VxEc+g3+FkRzUEaahDGOabmjgKuqDdlKdZLzgJj7+9sEKpb7 ++FdG56rZAgMBAAGjggEeMIIBGjAUBgNVHREEDTALgglsb2NhbGhvc3QwEQYJYIZI +AYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRDVVJMIHN0dW5uZWwgc2VydmVy +IHRlc3QgY2VydGlmaWNhdGUwCwYDVR0PBAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUF +BwMBMAkGA1UdEwQCMAAwHQYDVR0OBBYEFDV3NTubmDy2x5rnqAS5fHCt+jepMDYG +CCsGAQUFBwELBCowKDAmBggrBgEFBQcwBIYaaHR0cHM6Ly9sb2NhbGhvc3Q6ODQz +My81MDkwNgYIKwYBBQUHAQEEKjAoMCYGCCsGAQUFBzAEhhpodHRwczovL2xvY2Fs +aG9zdDo4NDMzLzUwOTANBgkqhkiG9w0BAQUFAAOBnAAARdsJWwhbGv9xUGwSrY54 +Mh195+TTPl/KIISq/5rCtqlIkx9zJ9FoBXY2+cFTkK2KwLMSyBFcLGUBrDHRjmBu +xvW6nWno8axK3lKUzQYkRXJkiQ9XiyYrFs8LJ8Toc8fT5UI4lVe1u4O0ktTgzfvI +9dLaHRH+PBggi70iMRxagtT1cY2K4xOCxS3zn9C3uEvSRp2OGteZbsG5oA== +-----END CERTIFICATE----- +-----BEGIN DH PARAMETERS----- +MIGHAoGBAMq/KFGh2oy16WzkFs1U71Uz7dIEKvSYfc+zo439pYyVzcD8MkcC15Zb +ayK3jPBYf07eKzc2TvI3/ZSducmECNP8gk2gAndP1P1rmpheN+owZJS7kQVfQmHl +UmT87U99NPaMHXMNOsFj/3mbAaANndKEnd8PM2r5fg16C4+2e5KzAgEC +-----END DH PARAMETERS----- diff --git a/deps/curl-7.51.0/tests/symbol-scan.pl b/deps/curl-7.51.0/tests/symbol-scan.pl new file mode 100644 index 0000000000..5d570d8a8d --- /dev/null +++ b/deps/curl-7.51.0/tests/symbol-scan.pl @@ -0,0 +1,176 @@ +#!/usr/bin/env perl +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 2010-2011, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### +# +# This script grew out of help from Przemyslaw Iskra and Balint Szilakszi +# a late evening in the #curl IRC channel on freenode. +# + +use strict; +use warnings; +use vars qw($Cpreprocessor); + +# +# configurehelp perl module is generated by configure script +# +my $rc = eval { + require configurehelp; + configurehelp->import(qw( + $Cpreprocessor + )); + 1; +}; +# Set default values if configure has not generated a configurehelp.pm file. +# This is the case with cmake. +if (!$rc) { + $Cpreprocessor = 'cpp'; +} + +# we may get the dir root pointed out +my $root=$ARGV[0] || "."; + +# need an include directory when building out-of-tree +my $i = ($ARGV[1]) ? "-I$ARGV[1] " : ''; + +my $h = "$root/include/curl/curl.h"; +my $mh = "$root/include/curl/multi.h"; + +my $verbose=0; +my $summary=0; +my $misses=0; + +my @syms; +my %doc; +my %rem; + +open H_IN, "-|", "$Cpreprocessor $i$h" || die "Cannot preprocess curl.h"; +while ( ) { + if ( /enum\s+(\S+\s+)?{/ .. /}/ ) { + s/^\s+//; + next unless /^CURL/; + chomp; + s/[,\s].*//; + push @syms, $_; + } +} +close H_IN || die "Error preprocessing curl.h"; + +sub scanheader { + my ($f)=@_; + open H, "<$f"; + while() { + if (/^#define (CURL[A-Za-z0-9_]*)/) { + push @syms, $1; + } + } + close H; +} + +scanheader($h); +scanheader($mh); + +open S, "<$root/docs/libcurl/symbols-in-versions"; +while() { + if(/(^CURL[^ \n]*) *(.*)/) { + my ($sym, $rest)=($1, $2); + if($doc{$sym}) { + print "Detected duplicate symbol: $sym\n"; + $misses++; + next; + } + $doc{$sym}=$sym; + my @a=split(/ +/, $rest); + if($a[2]) { + # this symbol is documented to have been present the last time + # in this release + $rem{$sym}=$a[2]; + } + } +} +close S; + +my $ignored=0; +for my $e (sort @syms) { + # OBSOLETE - names that are just placeholders for a position where we + # previously had a name, that is now removed. The OBSOLETE names should + # never be used for anything. + # + # CURL_EXTERN - is a define used for libcurl functions that are external, + # public. No app or other code should ever use it. + # + # *_LAST and *_LASTENTRY are just prefix for the placeholders used for the + # last entry in many enum series. + # + + if($e =~ /(OBSOLETE|^CURL_EXTERN|_LAST\z|_LASTENTRY\z)/) { + $ignored++; + next; + } + if($doc{$e}) { + if($verbose) { + print $e."\n"; + } + $doc{$e}="used"; + next; + } + else { + print $e."\n"; + $misses++; + } +} + +# +# now scan through all symbols that were present in the symbols-in-versions +# but not in the headers +# +# If the symbols were marked 'removed' in symbols-in-versions we don't output +# anything about it since that is perfectly fine. +# + +my $anyremoved; + +for my $e (sort keys %doc) { + if(($doc{$e} ne "used") && !$rem{$e}) { + + if(!$anyremoved++) { + print "Missing symbols mentioned in symbols-in-versions\n"; + print "Add them to a header, or mark them as removed.\n"; + } + + print "$e\n"; + $misses++; + } +} + +if($summary) { + print "Summary:\n"; + printf "%d symbols in headers (out of which %d are ignored)\n", scalar(@syms), + $ignored; + printf "%d symbols in headers are interesting\n", + scalar(@syms)- $ignored; + printf "%d symbols are listed in symbols-in-versions\n (out of which %d are listed as removed)\n", scalar(keys %doc), scalar(keys %rem); + printf "%d symbols in symbols-in-versions should match the ones in headers\n", scalar(keys %doc) - scalar(keys %rem); +} + +if($misses) { + exit 2; # there are stuff to attend to! +} diff --git a/deps/curl-7.51.0/tests/testcurl.1 b/deps/curl-7.51.0/tests/testcurl.1 new file mode 100644 index 0000000000..ee07d64feb --- /dev/null +++ b/deps/curl-7.51.0/tests/testcurl.1 @@ -0,0 +1,124 @@ +.\" ************************************************************************** +.\" * _ _ ____ _ +.\" * Project ___| | | | _ \| | +.\" * / __| | | | |_) | | +.\" * | (__| |_| | _ <| |___ +.\" * \___|\___/|_| \_\_____| +.\" * +.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. +.\" * +.\" * This software is licensed as described in the file COPYING, which +.\" * you should have received as part of this distribution. The terms +.\" * are also available at https://curl.haxx.se/docs/copyright.html. +.\" * +.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell +.\" * copies of the Software, and permit persons to whom the Software is +.\" * furnished to do so, under the terms of the COPYING file. +.\" * +.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +.\" * KIND, either express or implied. +.\" * +.\" ************************************************************************** +.\" +.TH testcurl.pl 1 "24 Mar 2010" "Curl 7.20.1" "testcurl" +.SH NAME +testcurl.pl \- (automatically) test curl +.SH SYNOPSIS +.B testcurl.pl [options] [dir] > output +.SH DESCRIPTION +\fItestcurl.pl\fP is the master script to use for automatic testing of curl +off git or daily snapshots. It is written for the purpose of being run from a +crontab job or similar at a regular interval. The output is suitable to be +mailed to curl-autocompile@haxx.se to be dealt with automatically (make sure +the subject includes the word "autobuild" as the mail gets silently discarded +otherwise). The most current build status (with a reasonable backlog) will be +published on the curl site, at https://curl.haxx.se/dev/builds.html + +\fIoptions\fP may be omitted. See \fI--setup\fP for what happens then. + +\fIdir\fP is a curl source dir, possibly a daily snapshot one. Using this will +make testcurl.pl skip the 'buildconf' stage and thus it removes the dependency +on automake, autoconf, libtool, GNU m4 and possibly a few other things. + +testcurl.pl will run 'buildconf' (or similar), run configure, build curl and +libcurl in a separate build directory and then run 'make test' to test the +fresh build. +.SH OPTIONS +.IP "--configure=[options]" +Configure options passed to configure. +.IP "--crosscompile" +This is a cross-compile. Makes \fItestcurl.pl\fP skip a few things. +.IP "--desc=[desc]" +Description of your test system. Displayed on the build summary page on the +weba site. +.IP "--email=[email]" +Set email address to report as. Displayed in the build logs on the site. +.IP "--mktarball=[command]" +Generic command to run after completed test. +.IP "--name=[name]" +Set name to report as. Displayed in the build summary on the site. +.IP "--nobuildconf" +Don't run buildconf. Useful when many builds use the same source tree, as then +only one need to do this. Also, if multiple processes run tests simultaneously +on the same source tree (like several hosts on a NFS mounted dir), +simultaneous buildconf invokes may cause problems. (Added in 7.14.1) +.IP "--nogitpull" +Don't update from git even though it is a git tree. Useful to still be able to +test even though your network is down, or similar. +.IP "--runtestopts=[options]" +Options that is passed to the runtests.pl script. Useful for disabling valgrind +by force, and similar. +.IP "--setup=[file name]" +File name to read setup from (deprecated). The old style of providing info. +If info is missing when testcurl.pl is started, it will prompt you and then +store the info in a 'setup' file, which it will look for on each invoke. Use +\fI--name\fP, \fI--email\fP, \fI--configure\fP and \fI--desc\fP instead. +.IP "--target=[your os]" +Specify your target environment. Recognized strings include 'vc', 'mingw32', +\&'borland' and 'netware'. +.SH "INITIAL SETUP" +First you make a checkout from git (or you write a script that downloads daily +snapshots automatically, find inspiration in +https://curl.haxx.se/dev/autocurl.txt ): + +.nf + $ mkdir daily-curl + $ cd daily-curl + $ git clone https://github.com/curl/curl.git +.fi + +With the curl sources checked out, or downloaded, you can start testing right +away. If you want to use \fItestcurl.pl\fP without command line arguments and +to have it store and remember the config in its 'setup' file, then start it +manually now and fill in the answers to the questions it prompts you for: + +.nf + $ ./curl/tests/testcurl.pl +.fi + +Now you are ready to go. If you let the script run, it will perform a full +cycle and spit out lots of output. Mail us that output as described above. +.SH "CRONTAB EXAMPLE" +The crontab could include something like this: + +.nf +\# autobuild curl: +0 4 * * * cd daily-curl && ./testit.sh +.fi + +Where testit.sh is a shell script that could look similar to this: + +.nf +mail="mail -s autobuild curl-autocompile@haxx.se" +name="--name=whoami" +email="--email=iamme@nowhere" +desc='"--desc=supermachine Turbo 2000"' +testprog="perl ./curl/tests/testcurl.pl $name $email $desc" +opts1="--configure=--enable-debug" +opts2="--configure=--enable-ipv6" + +# run first test +$testprog $opts1 | $mail + +# run second test +$testprog $opts2 | $mail diff --git a/deps/curl-7.51.0/tests/testcurl.html b/deps/curl-7.51.0/tests/testcurl.html new file mode 100644 index 0000000000..73d38cf79c --- /dev/null +++ b/deps/curl-7.51.0/tests/testcurl.html @@ -0,0 +1,122 @@ + + +testcurl.pl man page + + + + +

NAME

+

testcurl.pl - (automatically) test curl

SYNOPSIS

+

testcurl.pl [options] [dir] > output

DESCRIPTION

+

testcurl.pl is the master script to use for automatic testing of curl off git or daily snapshots. It is written for the purpose of being run from a crontab job or similar at a regular interval. The output is suitable to be mailed to curl-autocompile@haxx.se to be dealt with automatically (make sure the subject includes the word "autobuild" as the mail gets silently discarded otherwise). The most current build status (with a reasonable backlog) will be published on the curl site, at https://curl.haxx.se/dev/builds.html +

options may be omitted. See --setup for what happens then. +

dir is a curl source dir, possibly a daily snapshot one. Using this will make testcurl.pl skip the 'buildconf' stage and thus it removes the dependency on automake, autoconf, libtool, GNU m4 and possibly a few other things. +

testcurl.pl will run 'buildconf' (or similar), run configure, build curl and libcurl in a separate build directory and then run 'make test' to test the fresh build.

OPTIONS

+

+

--configure=[options] +

Configure options passed to configure. +

--crosscompile +

This is a cross-compile. Makes testcurl.pl skip a few things. +

--desc=[desc] +

Description of your test system. Displayed on the build summary page on the weba site. +

--email=[email] +

Set email address to report as. Displayed in the build logs on the site. +

--mktarball=[command] +

Generic command to run after completed test. +

--name=[name] +

Set name to report as. Displayed in the build summary on the site. +

--nobuildconf +

Don't run buildconf. Useful when many builds use the same source tree, as then only one need to do this. Also, if multiple processes run tests simultaneously on the same source tree (like several hosts on a NFS mounted dir), simultaneous buildconf invokes may cause problems. (Added in 7.14.1) +

--nogitpull +

Don't update from git even though it is a git tree. Useful to still be able to test even though your network is down, or similar. +

--runtestopts=[options] +

Options that is passed to the runtests.pl script. Useful for disabling valgrind by force, and similar. +

--setup=[file name] +

File name to read setup from (deprecated). The old style of providing info. If info is missing when testcurl.pl is started, it will prompt you and then store the info in a 'setup' file, which it will look for on each invoke. Use --name, --email, --configure and --desc instead. +

--target=[your os] +

Specify your target environment. Recognized strings include 'vc', 'mingw32', 'borland' and 'netware'.

INITIAL SETUP

+

First you make a checkout from git (or you write a script that downloads daily snapshots automatically, find inspiration in https://curl.haxx.se/dev/autocurl.txt ): +

+  $ mkdir daily-curl
+  $ cd daily-curl
+  $ git clone https://github.com/curl/curl.git
+
+ +

+

With the curl sources checked out, or downloaded, you can start testing right away. If you want to use testcurl.pl without command line arguments and to have it store and remember the config in its 'setup' file, then start it manually now and fill in the answers to the questions it prompts you for: +

+  $ ./curl/tests/testcurl.pl
+
+ +

+

Now you are ready to go. If you let the script run, it will perform a full cycle and spit out lots of output. Mail us that output as described above.

CRONTAB EXAMPLE

+

The crontab could include something like this: +

+#35; autobuild curl:
+0 4 * * * cd daily-curl && ./testit.sh
+
+ +

+

Where testit.sh is a shell script that could look similar to this: +

+mail="mail -s autobuild curl-autocompile@haxx.se"
+name="--name=whoami"
+email="--email=iamme@nowhere"
+desc='"--desc=supermachine Turbo 2000"'
+testprog="perl ./curl/tests/testcurl.pl $name $email $desc"
+opts1="--configure=--enable-debug"
+opts2="--configure=--enable-ipv6"
+ 
+# run first test
+$testprog $opts1 | $mail
+ 
+# run second test
+$testprog $opts2 | $mail
+

+ This HTML page was made with roffit. + diff --git a/deps/curl-7.51.0/tests/testcurl.pdf b/deps/curl-7.51.0/tests/testcurl.pdf new file mode 100644 index 0000000000..e5247d7978 Binary files /dev/null and b/deps/curl-7.51.0/tests/testcurl.pdf differ diff --git a/deps/curl-7.51.0/tests/testcurl.pl b/deps/curl-7.51.0/tests/testcurl.pl new file mode 100644 index 0000000000..dc35a026d0 --- /dev/null +++ b/deps/curl-7.51.0/tests/testcurl.pl @@ -0,0 +1,829 @@ +#!/usr/bin/env perl +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### + +########################### +# What is This Script? +########################### + +# testcurl.pl is the master script to use for automatic testing of curl +# directly off its source repository. +# This is written for the purpose of being run from a crontab job or similar +# at a regular interval. The output is suitable to be mailed to +# curl-autocompile@haxx.se to be dealt with automatically (make sure the +# subject includes the word "autobuild" as the mail gets silently discarded +# otherwise). The most current build status (with a resonable backlog) will +# be published on the curl site, at https://curl.haxx.se/auto/ + +# USAGE: +# testcurl.pl [options] [curl-daily-name] > output + +# Options: +# +# --configure=[options] Configure options +# --crosscompile This is a crosscompile +# --desc=[desc] Description of your test system +# --email=[email] Set email address to report as +# --extvercmd=[command] Command to use for displaying version with cross compiles. +# --mktarball=[command] Command to run after completed test +# --name=[name] Set name to report as +# --notes=[notes] More human-readable information about this configuration +# --nocvsup Don't pull from git even though it is a git tree +# --nogitpull Don't pull from git even though it is a git tree +# --nobuildconf Don't run buildconf +# --noconfigure Don't run configure +# --runtestopts=[options] Options to pass to runtests.pl +# --setup=[file name] File name to read setup from (deprecated) +# --target=[your os] Specify your target environment. +# +# if [curl-daily-name] is omitted, a 'curl' git directory is assumed. +# + +use strict; + +use Cwd; +use File::Spec; + +# Turn on warnings (equivalent to -w, which can't be used with /usr/bin/env) +#BEGIN { $^W = 1; } + +use vars qw($version $fixed $infixed $CURLDIR $git $pwd $build $buildlog + $buildlogname $configurebuild $targetos $confheader $binext + $libext); + +use vars qw($name $email $desc $confopts $runtestopts $setupfile $mktarball + $extvercmd $nogitpull $nobuildconf $crosscompile + $timestamp $notes); + +# version of this script +$version='2014-11-25'; +$fixed=0; + +# Determine if we're running from git or a canned copy of curl, +# or if we got a specific target option or setup file option. +$CURLDIR="curl"; +if (-f ".git/config") { + $CURLDIR = "./"; +} + +$git=1; +$setupfile = 'setup'; +$configurebuild = 1; +while ($ARGV[0]) { + if ($ARGV[0] =~ /--target=/) { + $targetos = (split(/=/, shift @ARGV, 2))[1]; + } + elsif ($ARGV[0] =~ /--setup=/) { + $setupfile = (split(/=/, shift @ARGV, 2))[1]; + } + elsif ($ARGV[0] =~ /--extvercmd=/) { + $extvercmd = (split(/=/, shift @ARGV, 2))[1]; + } + elsif ($ARGV[0] =~ /--mktarball=/) { + $mktarball = (split(/=/, shift @ARGV, 2))[1]; + } + elsif ($ARGV[0] =~ /--name=/) { + $name = (split(/=/, shift @ARGV, 2))[1]; + } + elsif ($ARGV[0] =~ /--email=/) { + $email = (split(/=/, shift @ARGV, 2))[1]; + } + elsif ($ARGV[0] =~ /--desc=/) { + $desc = (split(/=/, shift @ARGV, 2))[1]; + } + elsif ($ARGV[0] =~ /--notes=/) { + $notes = (split(/=/, shift @ARGV, 2))[1]; + } + elsif ($ARGV[0] =~ /--configure=(.*)/) { + $confopts = $1; + shift @ARGV; + } + elsif (($ARGV[0] eq "--nocvsup") || ($ARGV[0] eq "--nogitpull")) { + $nogitpull=1; + shift @ARGV; + } + elsif ($ARGV[0] =~ /--nobuildconf/) { + $nobuildconf=1; + shift @ARGV; + } + elsif ($ARGV[0] =~ /--noconfigure/) { + $configurebuild=0; + shift @ARGV; + } + elsif ($ARGV[0] =~ /--crosscompile/) { + $crosscompile=1; + shift @ARGV; + } + elsif ($ARGV[0] =~ /--runtestopts=/) { + $runtestopts = (split(/=/, shift @ARGV, 2))[1]; + } + else { + $CURLDIR=shift @ARGV; + $git=0; # a given dir, assume not using git + } +} + +# Do the platform-specific stuff here +$confheader = 'curl_config.h'; +$binext = ''; +$libext = '.la'; # .la since both libcurl and libcares are made with libtool +if ($^O eq 'MSWin32' || $targetos) { + if (!$targetos) { + # If no target defined on Win32 lets assume vc + $targetos = 'vc'; + } + if ($targetos =~ /vc/ || $targetos =~ /borland/ || $targetos =~ /watcom/) { + $binext = '.exe'; + $libext = '.lib'; + } + elsif ($targetos =~ /mingw/) { + $binext = '.exe'; + if ($^O eq 'MSWin32') { + $libext = '.a'; + } + } + elsif ($targetos =~ /netware/) { + $configurebuild = 0; + $binext = '.nlm'; + if ($^O eq 'MSWin32') { + $libext = '.lib'; + } + else { + $libext = '.a'; + } + } +} + +if (($^O eq 'MSWin32' || $^O eq 'msys') && + ($targetos =~ /vc/ || $targetos =~ /mingw32/ || + $targetos =~ /borland/ || $targetos =~ /watcom/)) { + + # Set these things only when building ON Windows and for Win32 platform. + # FOR Windows since we might be cross-compiling on another system. Non- + # Windows builds still default to configure-style builds with curl_config.h. + + $configurebuild = 0; + $confheader = 'config-win32.h'; +} + +$ENV{LC_ALL}="C" if (($ENV{LC_ALL}) && ($ENV{LC_ALL} !~ /^C$/)); +$ENV{LC_CTYPE}="C" if (($ENV{LC_CTYPE}) && ($ENV{LC_CTYPE} !~ /^C$/)); +$ENV{LANG}="C"; + +sub rmtree($) { + my $target = $_[0]; + if ($^O eq 'MSWin32') { + foreach (glob($target)) { + s:/:\\:g; + system("rd /s /q $_"); + } + } else { + system("rm -rf $target"); + } +} + +sub grepfile($$) { + my ($target, $fn) = @_; + open(F, $fn) or die; + while () { + if (/$target/) { + close(F); + return 1; + } + } + close(F); + return 0; +} + +sub logit($) { + my $text=$_[0]; + if ($text) { + print "testcurl: $text\n"; + } +} + +sub logit_spaced($) { + my $text=$_[0]; + if ($text) { + print "\ntestcurl: $text\n\n"; + } +} + +sub mydie($){ + my $text=$_[0]; + logit "$text"; + chdir $pwd; # cd back to the original root dir + + if ($pwd && $build) { + # we have a build directory name, remove the dir + logit "removing the $build dir"; + rmtree "$pwd/$build"; + } + if (-r $buildlog) { + # we have a build log output file left, remove it + logit "removing the $buildlogname file"; + unlink "$buildlog"; + } + logit "ENDING HERE"; # last line logged! + exit 1; +} + +sub get_host_triplet { + my $triplet; + my $configfile = "$pwd/$build/lib/curl_config.h"; + + if(-f $configfile && -s $configfile && open(LIBCONFIGH, "<$configfile")) { + while() { + if($_ =~ /^\#define\s+OS\s+"*([^"][^"]*)"*\s*/) { + $triplet = $1; + last; + } + } + close(LIBCONFIGH); + } + return $triplet; +} + +if($name && $email && $desc) { + # having these fields set are enough to continue, skip reading the setup + # file + $infixed=4; + $fixed=4; +} +elsif (open(F, "$setupfile")) { + while () { + if (/(\w+)=(.*)/) { + eval "\$$1=$2;"; + } + } + close(F); + $infixed=$fixed; +} +else { + $infixed=0; # so that "additional args to configure" works properly first time... +} + +if (!$name) { + print "please enter your name\n"; + $name = <>; + chomp $name; + $fixed=1; +} + +if (!$email) { + print "please enter your contact email address\n"; + $email = <>; + chomp $email; + $fixed=2; +} + +if (!$desc) { + print "please enter a one line system description\n"; + $desc = <>; + chomp $desc; + $fixed=3; +} + +if (!$confopts) { + if ($infixed < 4) { + print "please enter your additional arguments to configure\n"; + print "examples: --with-ssl --enable-debug --enable-ipv6 --with-krb4\n"; + $confopts = <>; + chomp $confopts; + } +} + + +if ($fixed < 4) { + $fixed=4; + open(F, ">$setupfile") or die; + print F "name='$name'\n"; + print F "email='$email'\n"; + print F "desc='$desc'\n"; + print F "confopts='$confopts'\n"; + print F "notes='$notes'\n"; + print F "fixed='$fixed'\n"; + close(F); +} + +# Enable picky compiler warnings unless explicitly disabled +if (($confopts !~ /--enable-debug/) && + ($confopts !~ /--enable-warnings/) && + ($confopts !~ /--disable-warnings/)) { + $confopts .= " --enable-warnings"; +} + +my $str1066os = 'o' x 1066; + +# Set timestamp to the UTC this script is running. Its value might +# be changed later in the script to the value present in curlver.h +$timestamp = scalar(gmtime)." UTC"; + +logit "STARTING HERE"; # first line logged, for scripts to trigger on +logit 'TRANSFER CONTROL ==== 1120 CHAR LINE' . $str1066os . 'LINE_END'; +logit "NAME = $name"; +logit "EMAIL = $email"; +logit "DESC = $desc"; +logit "NOTES = $notes"; +logit "CONFOPTS = $confopts"; +logit "RUNTESTOPTS = ".$runtestopts; +logit "CPPFLAGS = ".$ENV{CPPFLAGS}; +logit "CFLAGS = ".$ENV{CFLAGS}; +logit "LDFLAGS = ".$ENV{LDFLAGS}; +logit "LIBS = ".$ENV{LIBS}; +logit "CC = ".$ENV{CC}; +logit "TMPDIR = ".$ENV{TMPDIR}; +logit "MAKEFLAGS = ".$ENV{MAKEFLAGS}; +logit "ACLOCAL_FLAGS = ".$ENV{ACLOCAL_FLAGS}; +logit "PKG_CONFIG_PATH = ".$ENV{PKG_CONFIG_PATH}; +logit "DYLD_LIBRARY_PATH = ".$ENV{DYLD_LIBRARY_PATH}; +logit "LD_LIBRARY_PATH = ".$ENV{LD_LIBRARY_PATH}; +logit "LIBRARY_PATH = ".$ENV{LIBRARY_PATH}; +logit "SHLIB_PATH = ".$ENV{SHLIB_PATH}; +logit "LIBPATH = ".$ENV{LIBPATH}; +logit "target = ".$targetos; +logit "version = $version"; # script version +logit "date = $timestamp"; # When the test build starts + +$str1066os = undef; + +# Make $pwd to become the path without newline. We'll use that in order to cut +# off that path from all possible logs and error messages etc. +$pwd = getcwd(); + +my $have_embedded_ares = 0; + +if (-d $CURLDIR) { + if ($git && -d "$CURLDIR/.git") { + logit "$CURLDIR is verified to be a fine git source dir"; + # remove the generated sources to force them to be re-generated each + # time we run this test + unlink "$CURLDIR/src/tool_hugehelp.c"; + # find out if curl source dir has an in-tree c-ares repo + $have_embedded_ares = 1 if (-f "$CURLDIR/ares/GIT-INFO"); + } elsif (!$git && -f "$CURLDIR/tests/testcurl.pl") { + logit "$CURLDIR is verified to be a fine daily source dir"; + # find out if curl source dir has an in-tree c-ares extracted tarball + $have_embedded_ares = 1 if (-f "$CURLDIR/ares/ares_build.h"); + } else { + mydie "$CURLDIR is not a daily source dir or checked out from git!" + } +} + +# make the path absolute so we can use it everywhere +$CURLDIR = File::Spec->rel2abs("$CURLDIR"); + +$build="build-$$"; +$buildlogname="buildlog-$$"; +$buildlog="$pwd/$buildlogname"; + +# remove any previous left-overs +rmtree "build-*"; +rmtree "buildlog-*"; + +# this is to remove old build logs that ended up in the wrong dir +foreach (glob("$CURLDIR/buildlog-*")) { unlink $_; } + +# create a dir to build in +mkdir $build, 0777; + +if (-d $build) { + logit "build dir $build was created fine"; +} else { + mydie "failed to create dir $build"; +} + +# get in the curl source tree root +chdir $CURLDIR; + +# Do the git thing, or not... +if ($git) { + my $gitstat = 0; + my @commits; + + # update quietly to the latest git + if($nogitpull) { + logit "skipping git pull (--nogitpull)"; + } else { + logit "run git pull in curl"; + system("git pull 2>&1"); + $gitstat += $?; + logit "failed to update from curl git ($?), continue anyway" if ($?); + + # Set timestamp to the UTC the git update took place. + $timestamp = scalar(gmtime)." UTC" if (!$gitstat); + } + + # get the last 5 commits for show (even if no pull was made) + @commits=`git log --pretty=oneline --abbrev-commit -5`; + logit "The most recent curl git commits:"; + for (@commits) { + chomp ($_); + logit " $_"; + } + + if (-d "ares/.git") { + chdir "ares"; + + if($nogitpull) { + logit "skipping git pull (--nogitpull) in ares"; + } else { + logit "run git pull in ares"; + system("git pull 2>&1"); + $gitstat += $?; + logit "failed to update from ares git ($?), continue anyway" if ($?); + + # Set timestamp to the UTC the git update took place. + $timestamp = scalar(gmtime)." UTC" if (!$gitstat); + } + + # get the last 5 commits for show (even if no pull was made) + @commits=`git log --pretty=oneline --abbrev-commit -5`; + logit "The most recent ares git commits:"; + for (@commits) { + chomp ($_); + logit " $_"; + } + + chdir "$CURLDIR"; + } + + if($nobuildconf) { + logit "told to not run buildconf"; + } + elsif ($configurebuild) { + # remove possible left-overs from the past + unlink "configure"; + unlink "autom4te.cache"; + + # generate the build files + logit "invoke buildconf"; + open(F, "./buildconf 2>&1 |") or die; + open(LOG, ">$buildlog") or die; + while () { + my $ll = $_; + # ignore messages pertaining to third party m4 files we don't care + next if ($ll =~ /aclocal\/gtk\.m4/); + next if ($ll =~ /aclocal\/gtkextra\.m4/); + print $ll; + print LOG $ll; + } + close(F); + close(LOG); + + if (grepfile("^buildconf: OK", $buildlog)) { + logit "buildconf was successful"; + } + else { + mydie "buildconf was NOT successful"; + } + } + else { + logit "buildconf was successful (dummy message)"; + } +} + +# Set timestamp to the one in curlver.h if this isn't a git test build. +if ((-f "include/curl/curlver.h") && + (open(F, ") { + chomp; + if ($_ =~ /^\#define\s+LIBCURL_TIMESTAMP\s+\"(.+)\".*$/) { + my $stampstring = $1; + if ($stampstring !~ /DEV/) { + $stampstring =~ s/\s+UTC//; + $timestamp = $stampstring." UTC"; + } + last; + } + } + close(F); +} + +# Show timestamp we are using for this test build. +logit "timestamp = $timestamp"; + +if ($configurebuild) { + if (-f "configure") { + logit "configure created (at least it exists)"; + } else { + mydie "no configure created/found"; + } +} else { + logit "configure created (dummy message)"; # dummy message to feign success +} + +sub findinpath { + my $c; + my $e; + my $x = ($^O eq 'MSWin32') ? '.exe' : ''; + my $s = ($^O eq 'MSWin32') ? ';' : ':'; + my $p=$ENV{'PATH'}; + my @pa = split($s, $p); + for $c (@_) { + for $e (@pa) { + if( -x "$e/$c$x") { + return $c; + } + } + } +} + +my $make = findinpath("gmake", "make", "nmake"); +if(!$make) { + mydie "Couldn't find make in the PATH"; +} +# force to 'nmake' for VC builds +$make = "nmake" if ($targetos =~ /vc/); +# force to 'wmake' for Watcom builds +$make = "wmake" if ($targetos =~ /watcom/); +logit "going with $make as make"; + +# change to build dir +chdir "$pwd/$build"; + +if ($configurebuild) { + # run configure script + print `$CURLDIR/configure $confopts 2>&1`; + + if (-f "lib/Makefile") { + logit "configure seems to have finished fine"; + } else { + mydie "configure didn't work"; + } +} else { + logit "copying files to build dir ..."; + if (($^O eq 'MSWin32') && ($targetos !~ /netware/)) { + system("xcopy /s /q \"$CURLDIR\" ."); + system("buildconf.bat"); + } + elsif ($targetos =~ /netware/) { + system("cp -afr $CURLDIR/* ."); + system("cp -af $CURLDIR/Makefile.dist Makefile"); + system("$make -i -C lib -f Makefile.netware prebuild"); + system("$make -i -C src -f Makefile.netware prebuild"); + if (-d "$CURLDIR/ares") { + system("$make -i -C ares -f Makefile.netware prebuild"); + } + } + elsif ($^O eq 'linux') { + system("cp -afr $CURLDIR/* ."); + system("cp -af $CURLDIR/Makefile.dist Makefile"); + system("cp -af $CURLDIR/include/curl/curlbuild.h.dist ./include/curl/curlbuild.h"); + system("$make -i -C lib -f Makefile.$targetos prebuild"); + system("$make -i -C src -f Makefile.$targetos prebuild"); + if (-d "$CURLDIR/ares") { + system("cp -af $CURLDIR/ares/ares_build.h.dist ./ares/ares_build.h"); + system("$make -i -C ares -f Makefile.$targetos prebuild"); + } + } +} + +if(-f "./libcurl.pc") { + logit_spaced "display libcurl.pc"; + if(open(F, "<./libcurl.pc")) { + while() { + my $ll = $_; + print $ll if(($ll !~ /^ *#/) && ($ll !~ /^ *$/)); + } + close(F); + } +} + +if(-f "./include/curl/curlbuild.h") { + logit_spaced "display include/curl/curlbuild.h"; + if(open(F, "<./include/curl/curlbuild.h")) { + while() { + my $ll = $_; + print $ll if(($ll =~ /^ *# *define *CURL_/) && ($ll !~ /__CURL_CURLBUILD_H/)); + } + close(F); + } +} +else { + mydie "no curlbuild.h created/found"; +} + +logit_spaced "display lib/$confheader"; +open(F, "lib/$confheader") or die "lib/$confheader: $!"; +while () { + print if /^ *#/; +} +close(F); + +if (($have_embedded_ares) && + (grepfile("^#define USE_ARES", "lib/$confheader"))) { + print "\n"; + logit "setup to build ares"; + + if(-f "./ares/libcares.pc") { + logit_spaced "display ares/libcares.pc"; + if(open(F, "<./ares/libcares.pc")) { + while() { + my $ll = $_; + print $ll if(($ll !~ /^ *#/) && ($ll !~ /^ *$/)); + } + close(F); + } + } + + if(-f "./ares/ares_build.h") { + logit_spaced "display ares/ares_build.h"; + if(open(F, "<./ares/ares_build.h")) { + while() { + my $ll = $_; + print $ll if(($ll =~ /^ *# *define *CARES_/) && ($ll !~ /__CARES_BUILD_H/)); + } + close(F); + } + } + else { + mydie "no ares_build.h created/found"; + } + + $confheader =~ s/curl/ares/; + logit_spaced "display ares/$confheader"; + if(open(F, "ares/$confheader")) { + while () { + print if /^ *#/; + } + close(F); + } + + print "\n"; + logit "build ares"; + chdir "ares"; + + if ($targetos && !$configurebuild) { + logit "$make -f Makefile.$targetos"; + open(F, "$make -f Makefile.$targetos 2>&1 |") or die; + } + else { + logit "$make"; + open(F, "$make 2>&1 |") or die; + } + while () { + s/$pwd//g; + print; + } + close(F); + + if (-f "libcares$libext") { + logit "ares is now built successfully (libcares$libext)"; + } else { + mydie "ares build failed (libcares$libext)"; + } + + # cd back to the curl build dir + chdir "$pwd/$build"; +} + +my $mkcmd = "$make -i" . ($targetos && !$configurebuild ? " $targetos" : ""); +logit "$mkcmd"; +open(F, "$mkcmd 2>&1 |") or die; +while () { + s/$pwd//g; + print; +} +close(F); + +if (-f "lib/libcurl$libext") { + logit "libcurl was created fine (libcurl$libext)"; +} +else { + mydie "libcurl was not created (libcurl$libext)"; +} + +if (-f "src/curl$binext") { + logit "curl was created fine (curl$binext)"; +} +else { + mydie "curl was not created (curl$binext)"; +} + +if (!$crosscompile || (($extvercmd ne '') && (-x $extvercmd))) { + logit "display curl${binext} --version output"; + my $cmd = ($extvercmd ne '' ? $extvercmd.' ' : '')."./src/curl${binext} --version|"; + open(F, $cmd); + while() { + # strip CR from output on non-win32 platforms (wine on Linux) + s/\r// if ($^O ne 'MSWin32'); + print; + } + close(F); +} + +if ($configurebuild && !$crosscompile) { + my $host_triplet = get_host_triplet(); + # build example programs for selected build targets + if(($host_triplet =~ /([^-]+)-([^-]+)-irix(.*)/) || + ($host_triplet =~ /([^-]+)-([^-]+)-aix(.*)/) || + ($host_triplet =~ /([^-]+)-([^-]+)-osf(.*)/) || + ($host_triplet =~ /([^-]+)-([^-]+)-solaris2(.*)/)) { + chdir "$pwd/$build/docs/examples"; + logit_spaced "build examples"; + open(F, "$make -i 2>&1 |") or die; + open(LOG, ">$buildlog") or die; + while () { + s/$pwd//g; + print; + print LOG; + } + close(F); + close(LOG); + chdir "$pwd/$build"; + } + # build and run full test suite + my $o; + if($runtestopts) { + $o = "TEST_F=\"$runtestopts\" "; + } + logit "$make -k ${o}test-full"; + open(F, "$make -k ${o}test-full 2>&1 |") or die; + open(LOG, ">$buildlog") or die; + while () { + s/$pwd//g; + print; + print LOG; + } + close(F); + close(LOG); + + if (grepfile("^TEST", $buildlog)) { + logit "tests were run"; + } else { + mydie "test suite failure"; + } + + if (grepfile("^TESTFAIL:", $buildlog)) { + logit "the tests were not successful"; + } else { + logit "the tests were successful!"; + } +} +else { + if($crosscompile) { + my $host_triplet = get_host_triplet(); + # build example programs for selected cross-compiles + if(($host_triplet =~ /([^-]+)-([^-]+)-mingw(.*)/) || + ($host_triplet =~ /([^-]+)-([^-]+)-android(.*)/)) { + chdir "$pwd/$build/docs/examples"; + logit_spaced "build examples"; + open(F, "$make -i 2>&1 |") or die; + open(LOG, ">$buildlog") or die; + while () { + s/$pwd//g; + print; + print LOG; + } + close(F); + close(LOG); + chdir "$pwd/$build"; + } + # build test harness programs for selected cross-compiles + if($host_triplet =~ /([^-]+)-([^-]+)-mingw(.*)/) { + chdir "$pwd/$build/tests"; + logit_spaced "build test harness"; + open(F, "$make -i 2>&1 |") or die; + open(LOG, ">$buildlog") or die; + while () { + s/$pwd//g; + print; + print LOG; + } + close(F); + close(LOG); + chdir "$pwd/$build"; + } + logit_spaced "cross-compiling, can't run tests"; + } + # dummy message to feign success + print "TESTDONE: 1 tests out of 0 (dummy message)\n"; +} + +# create a tarball if we got that option. +if (($mktarball ne '') && (-x $mktarball)) { + system($mktarball); +} + +# mydie to cleanup +mydie "ending nicely"; diff --git a/deps/curl-7.51.0/tests/tftpserver.pl b/deps/curl-7.51.0/tests/tftpserver.pl new file mode 100644 index 0000000000..a4c4e47c01 --- /dev/null +++ b/deps/curl-7.51.0/tests/tftpserver.pl @@ -0,0 +1,110 @@ +#!/usr/bin/env perl +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +#*************************************************************************** + +BEGIN { + push(@INC, $ENV{'srcdir'}) if(defined $ENV{'srcdir'}); + push(@INC, "."); +} + +use strict; +use warnings; + +use serverhelp qw( + server_pidfilename + server_logfilename + ); + +my $verbose = 0; # set to 1 for debugging +my $port = 8997; # just a default +my $ipvnum = 4; # default IP version of tftp server +my $idnum = 1; # dafault tftp server instance number +my $proto = 'tftp'; # protocol the tftp server speaks +my $pidfile; # tftp server pid file +my $logfile; # tftp server log file +my $srcdir; +my $fork; + +my $flags = ""; +my $path = '.'; +my $logdir = $path .'/log'; + +while(@ARGV) { + if($ARGV[0] eq '--pidfile') { + if($ARGV[1]) { + $pidfile = $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--logfile') { + if($ARGV[1]) { + $logfile = $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--srcdir') { + if($ARGV[1]) { + $srcdir = $ARGV[1]; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--ipv4') { + $ipvnum = 4; + } + elsif($ARGV[0] eq '--ipv6') { + $ipvnum = 6; + } + elsif($ARGV[0] eq '--port') { + if($ARGV[1] =~ /^(\d+)$/) { + $port = $1; + shift @ARGV; + } + } + elsif($ARGV[0] eq '--id') { + if($ARGV[1] =~ /^(\d+)$/) { + $idnum = $1 if($1 > 0); + shift @ARGV; + } + } + elsif($ARGV[0] eq '--verbose') { + $verbose = 1; + } + else { + print STDERR "\nWarning: tftpserver.pl unknown parameter: $ARGV[0]\n"; + } + shift @ARGV; +} + +if(!$srcdir) { + $srcdir = $ENV{'srcdir'} || '.'; +} +if(!$pidfile) { + $pidfile = "$path/". server_pidfilename($proto, $ipvnum, $idnum); +} +if(!$logfile) { + $logfile = server_logfilename($logdir, $proto, $ipvnum, $idnum); +} + +$flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" "; +$flags .= "--ipv$ipvnum --port $port --srcdir \"$srcdir\""; + +exec("server/tftpd $flags"); diff --git a/deps/curl-7.51.0/tests/unit/Makefile.am b/deps/curl-7.51.0/tests/unit/Makefile.am new file mode 100644 index 0000000000..d4987d69d0 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/Makefile.am @@ -0,0 +1,81 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### +AUTOMAKE_OPTIONS = foreign nostdinc + +# Specify our include paths here, and do it relative to $(top_srcdir) and +# $(top_builddir), to ensure that these paths which belong to the library +# being currently built and tested are searched before the library which +# might possibly already be installed in the system. +# +# $(top_builddir)/include/curl for generated curlbuild.h included from curl.h +# $(top_builddir)/include for generated curlbuild.h inc. from lib/curl_setup.h +# $(top_srcdir)/include is for libcurl's external include files +# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file +# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "borrowed" files +# $(top_builddir)/ares is for in-tree c-ares's generated ares_build.h file +# $(top_srcdir)/ares is for in-tree c-ares's external include files + +if USE_EMBEDDED_ARES +AM_CPPFLAGS = -I$(top_builddir)/include/curl \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/lib \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/tests/libtest \ + -I$(top_builddir)/ares \ + -I$(top_srcdir)/ares +else +AM_CPPFLAGS = -I$(top_builddir)/include/curl \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/lib \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/tests/libtest +endif + +EXTRA_DIST = Makefile.inc + +# Prevent LIBS from being used for all link targets +LIBS = $(BLANK_AT_MAKETIME) + +LDADD = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la \ + @LDFLAGS@ @LIBCURL_LIBS@ + +DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la + +AM_CPPFLAGS += -DCURL_STATICLIB -DUNITTESTS + +# Makefile.inc provides neat definitions +include Makefile.inc + +checksrc: + @PERL@ $(top_srcdir)/lib/checksrc.pl $(srcdir)/*.c + +if BUILD_UNITTESTS +noinst_PROGRAMS = $(UNITPROGS) +else +noinst_PROGRAMS = +endif diff --git a/deps/curl-7.51.0/tests/unit/Makefile.in b/deps/curl-7.51.0/tests/unit/Makefile.in new file mode 100644 index 0000000000..fda6330c79 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/Makefile.in @@ -0,0 +1,1705 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# these files are used in every single unit test program + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@BUILD_UNITTESTS_TRUE@noinst_PROGRAMS = $(am__EXEEXT_1) +subdir = tests/unit +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/curl-compilers.m4 \ + $(top_srcdir)/m4/curl-confopts.m4 \ + $(top_srcdir)/m4/curl-functions.m4 \ + $(top_srcdir)/m4/curl-openssl.m4 \ + $(top_srcdir)/m4/curl-override.m4 \ + $(top_srcdir)/m4/curl-reentrant.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/xc-am-iface.m4 \ + $(top_srcdir)/m4/xc-cc-check.m4 \ + $(top_srcdir)/m4/xc-lt-iface.m4 \ + $(top_srcdir)/m4/xc-translit.m4 \ + $(top_srcdir)/m4/xc-val-flgs.m4 \ + $(top_srcdir)/m4/zz40-xc-ovr.m4 \ + $(top_srcdir)/m4/zz50-xc-ovr.m4 \ + $(top_srcdir)/m4/zz60-xc-ovr.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/lib/curl_config.h \ + $(top_builddir)/include/curl/curlbuild.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__EXEEXT_1 = unit1300$(EXEEXT) unit1301$(EXEEXT) unit1302$(EXEEXT) \ + unit1303$(EXEEXT) unit1304$(EXEEXT) unit1305$(EXEEXT) \ + unit1307$(EXEEXT) unit1308$(EXEEXT) unit1309$(EXEEXT) \ + unit1330$(EXEEXT) unit1394$(EXEEXT) unit1395$(EXEEXT) \ + unit1396$(EXEEXT) unit1397$(EXEEXT) unit1398$(EXEEXT) \ + unit1600$(EXEEXT) unit1601$(EXEEXT) unit1602$(EXEEXT) \ + unit1603$(EXEEXT) unit1604$(EXEEXT) unit1605$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +am__dirstamp = $(am__leading_dot)dirstamp +am__objects_1 = ../libtest/unit1300-first.$(OBJEXT) +am_unit1300_OBJECTS = unit1300-unit1300.$(OBJEXT) $(am__objects_1) +unit1300_OBJECTS = $(am_unit1300_OBJECTS) +unit1300_LDADD = $(LDADD) +unit1300_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +am__objects_2 = ../libtest/unit1301-first.$(OBJEXT) +am_unit1301_OBJECTS = unit1301-unit1301.$(OBJEXT) $(am__objects_2) +unit1301_OBJECTS = $(am_unit1301_OBJECTS) +unit1301_LDADD = $(LDADD) +unit1301_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_3 = ../libtest/unit1302-first.$(OBJEXT) +am_unit1302_OBJECTS = unit1302-unit1302.$(OBJEXT) $(am__objects_3) +unit1302_OBJECTS = $(am_unit1302_OBJECTS) +unit1302_LDADD = $(LDADD) +unit1302_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_4 = ../libtest/unit1303-first.$(OBJEXT) +am_unit1303_OBJECTS = unit1303-unit1303.$(OBJEXT) $(am__objects_4) +unit1303_OBJECTS = $(am_unit1303_OBJECTS) +unit1303_LDADD = $(LDADD) +unit1303_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_5 = ../libtest/unit1304-first.$(OBJEXT) +am_unit1304_OBJECTS = unit1304-unit1304.$(OBJEXT) $(am__objects_5) +unit1304_OBJECTS = $(am_unit1304_OBJECTS) +unit1304_LDADD = $(LDADD) +unit1304_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_6 = ../libtest/unit1305-first.$(OBJEXT) +am_unit1305_OBJECTS = unit1305-unit1305.$(OBJEXT) $(am__objects_6) +unit1305_OBJECTS = $(am_unit1305_OBJECTS) +unit1305_LDADD = $(LDADD) +unit1305_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_7 = ../libtest/unit1307-first.$(OBJEXT) +am_unit1307_OBJECTS = unit1307-unit1307.$(OBJEXT) $(am__objects_7) +unit1307_OBJECTS = $(am_unit1307_OBJECTS) +unit1307_LDADD = $(LDADD) +unit1307_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_8 = ../libtest/unit1308-first.$(OBJEXT) +am_unit1308_OBJECTS = unit1308-unit1308.$(OBJEXT) $(am__objects_8) +unit1308_OBJECTS = $(am_unit1308_OBJECTS) +unit1308_LDADD = $(LDADD) +unit1308_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_9 = ../libtest/unit1309-first.$(OBJEXT) +am_unit1309_OBJECTS = unit1309-unit1309.$(OBJEXT) $(am__objects_9) +unit1309_OBJECTS = $(am_unit1309_OBJECTS) +unit1309_LDADD = $(LDADD) +unit1309_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_10 = ../libtest/unit1330-first.$(OBJEXT) +am_unit1330_OBJECTS = unit1330-unit1330.$(OBJEXT) $(am__objects_10) +unit1330_OBJECTS = $(am_unit1330_OBJECTS) +unit1330_LDADD = $(LDADD) +unit1330_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_11 = ../libtest/unit1394-first.$(OBJEXT) +am_unit1394_OBJECTS = unit1394-unit1394.$(OBJEXT) $(am__objects_11) +unit1394_OBJECTS = $(am_unit1394_OBJECTS) +unit1394_DEPENDENCIES = $(top_builddir)/lib/libcurl.la +unit1394_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(unit1394_LDFLAGS) $(LDFLAGS) -o $@ +am__objects_12 = ../libtest/unit1395-first.$(OBJEXT) +am_unit1395_OBJECTS = unit1395-unit1395.$(OBJEXT) $(am__objects_12) +unit1395_OBJECTS = $(am_unit1395_OBJECTS) +unit1395_LDADD = $(LDADD) +unit1395_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_13 = ../libtest/unit1396-first.$(OBJEXT) +am_unit1396_OBJECTS = unit1396-unit1396.$(OBJEXT) $(am__objects_13) +unit1396_OBJECTS = $(am_unit1396_OBJECTS) +unit1396_LDADD = $(LDADD) +unit1396_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_14 = ../libtest/unit1397-first.$(OBJEXT) +am_unit1397_OBJECTS = unit1397-unit1397.$(OBJEXT) $(am__objects_14) +unit1397_OBJECTS = $(am_unit1397_OBJECTS) +unit1397_LDADD = $(LDADD) +unit1397_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_15 = ../libtest/unit1398-first.$(OBJEXT) +am_unit1398_OBJECTS = unit1398-unit1398.$(OBJEXT) $(am__objects_15) +unit1398_OBJECTS = $(am_unit1398_OBJECTS) +unit1398_LDADD = $(LDADD) +unit1398_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_16 = ../libtest/unit1600-first.$(OBJEXT) +am_unit1600_OBJECTS = unit1600-unit1600.$(OBJEXT) $(am__objects_16) +unit1600_OBJECTS = $(am_unit1600_OBJECTS) +unit1600_LDADD = $(LDADD) +unit1600_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_17 = ../libtest/unit1601-first.$(OBJEXT) +am_unit1601_OBJECTS = unit1601-unit1601.$(OBJEXT) $(am__objects_17) +unit1601_OBJECTS = $(am_unit1601_OBJECTS) +unit1601_LDADD = $(LDADD) +unit1601_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_18 = ../libtest/unit1602-first.$(OBJEXT) +am_unit1602_OBJECTS = unit1602-unit1602.$(OBJEXT) $(am__objects_18) +unit1602_OBJECTS = $(am_unit1602_OBJECTS) +unit1602_LDADD = $(LDADD) +unit1602_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_19 = ../libtest/unit1603-first.$(OBJEXT) +am_unit1603_OBJECTS = unit1603-unit1603.$(OBJEXT) $(am__objects_19) +unit1603_OBJECTS = $(am_unit1603_OBJECTS) +unit1603_LDADD = $(LDADD) +unit1603_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_20 = ../libtest/unit1604-first.$(OBJEXT) +am_unit1604_OBJECTS = unit1604-unit1604.$(OBJEXT) $(am__objects_20) +unit1604_OBJECTS = $(am_unit1604_OBJECTS) +unit1604_LDADD = $(LDADD) +unit1604_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +am__objects_21 = ../libtest/unit1605-first.$(OBJEXT) +am_unit1605_OBJECTS = unit1605-unit1605.$(OBJEXT) $(am__objects_21) +unit1605_OBJECTS = $(am_unit1605_OBJECTS) +unit1605_LDADD = $(LDADD) +unit1605_DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(unit1300_SOURCES) $(unit1301_SOURCES) $(unit1302_SOURCES) \ + $(unit1303_SOURCES) $(unit1304_SOURCES) $(unit1305_SOURCES) \ + $(unit1307_SOURCES) $(unit1308_SOURCES) $(unit1309_SOURCES) \ + $(unit1330_SOURCES) $(unit1394_SOURCES) $(unit1395_SOURCES) \ + $(unit1396_SOURCES) $(unit1397_SOURCES) $(unit1398_SOURCES) \ + $(unit1600_SOURCES) $(unit1601_SOURCES) $(unit1602_SOURCES) \ + $(unit1603_SOURCES) $(unit1604_SOURCES) $(unit1605_SOURCES) +DIST_SOURCES = $(unit1300_SOURCES) $(unit1301_SOURCES) \ + $(unit1302_SOURCES) $(unit1303_SOURCES) $(unit1304_SOURCES) \ + $(unit1305_SOURCES) $(unit1307_SOURCES) $(unit1308_SOURCES) \ + $(unit1309_SOURCES) $(unit1330_SOURCES) $(unit1394_SOURCES) \ + $(unit1395_SOURCES) $(unit1396_SOURCES) $(unit1397_SOURCES) \ + $(unit1398_SOURCES) $(unit1600_SOURCES) $(unit1601_SOURCES) \ + $(unit1602_SOURCES) $(unit1603_SOURCES) $(unit1604_SOURCES) \ + $(unit1605_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.inc \ + $(top_srcdir)/depcomp README +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLANK_AT_MAKETIME = @BLANK_AT_MAKETIME@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CFLAG_CURL_SYMBOL_HIDING = @CFLAG_CURL_SYMBOL_HIDING@ +CONFIGURE_OPTIONS = @CONFIGURE_OPTIONS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAG_CURL_STATICLIB = @CPPFLAG_CURL_STATICLIB@ +CURLVERSION = @CURLVERSION@ +CURL_CA_BUNDLE = @CURL_CA_BUNDLE@ +CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@ +CURL_DISABLE_DICT = @CURL_DISABLE_DICT@ +CURL_DISABLE_FILE = @CURL_DISABLE_FILE@ +CURL_DISABLE_FTP = @CURL_DISABLE_FTP@ +CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@ +CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@ +CURL_DISABLE_IMAP = @CURL_DISABLE_IMAP@ +CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@ +CURL_DISABLE_LDAPS = @CURL_DISABLE_LDAPS@ +CURL_DISABLE_POP3 = @CURL_DISABLE_POP3@ +CURL_DISABLE_PROXY = @CURL_DISABLE_PROXY@ +CURL_DISABLE_RTSP = @CURL_DISABLE_RTSP@ +CURL_DISABLE_SMB = @CURL_DISABLE_SMB@ +CURL_DISABLE_SMTP = @CURL_DISABLE_SMTP@ +CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@ +CURL_DISABLE_TFTP = @CURL_DISABLE_TFTP@ +CURL_LT_SHLIB_VERSIONED_FLAVOUR = @CURL_LT_SHLIB_VERSIONED_FLAVOUR@ +CURL_NETWORK_AND_TIME_LIBS = @CURL_NETWORK_AND_TIME_LIBS@ +CURL_NETWORK_LIBS = @CURL_NETWORK_LIBS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_SHARED = @ENABLE_SHARED@ +ENABLE_STATIC = @ENABLE_STATIC@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@ +HAVE_LDAP_SSL = @HAVE_LDAP_SSL@ +HAVE_LIBZ = @HAVE_LIBZ@ +HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@ +IDN_ENABLED = @IDN_ENABLED@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPV6_ENABLED = @IPV6_ENABLED@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCURL_LIBS = @LIBCURL_LIBS@ +LIBMETALINK_CPPFLAGS = @LIBMETALINK_CPPFLAGS@ +LIBMETALINK_LDFLAGS = @LIBMETALINK_LDFLAGS@ +LIBMETALINK_LIBS = @LIBMETALINK_LIBS@ +LIBOBJS = @LIBOBJS@ + +# Prevent LIBS from being used for all link targets +LIBS = $(BLANK_AT_MAKETIME) +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MANOPT = @MANOPT@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NROFF = @NROFF@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKGADD_NAME = @PKGADD_NAME@ +PKGADD_PKG = @PKGADD_PKG@ +PKGADD_VENDOR = @PKGADD_VENDOR@ +PKGCONFIG = @PKGCONFIG@ +RANDOM_FILE = @RANDOM_FILE@ +RANLIB = @RANLIB@ +REQUIRE_LIB_DEPS = @REQUIRE_LIB_DEPS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SSL_ENABLED = @SSL_ENABLED@ +SSL_LIBS = @SSL_LIBS@ +STRIP = @STRIP@ +SUPPORT_FEATURES = @SUPPORT_FEATURES@ +SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@ +USE_ARES = @USE_ARES@ +USE_AXTLS = @USE_AXTLS@ +USE_CYASSL = @USE_CYASSL@ +USE_DARWINSSL = @USE_DARWINSSL@ +USE_GNUTLS = @USE_GNUTLS@ +USE_GNUTLS_NETTLE = @USE_GNUTLS_NETTLE@ +USE_LIBRTMP = @USE_LIBRTMP@ +USE_LIBSSH2 = @USE_LIBSSH2@ +USE_MBEDTLS = @USE_MBEDTLS@ +USE_NGHTTP2 = @USE_NGHTTP2@ +USE_NSS = @USE_NSS@ +USE_OPENLDAP = @USE_OPENLDAP@ +USE_POLARSSL = @USE_POLARSSL@ +USE_SCHANNEL = @USE_SCHANNEL@ +USE_UNIX_SOCKETS = @USE_UNIX_SOCKETS@ +USE_WINDOWS_SSPI = @USE_WINDOWS_SSPI@ +VERSION = @VERSION@ +VERSIONNUM = @VERSIONNUM@ +ZLIB_LIBS = @ZLIB_LIBS@ +ZSH_FUNCTIONS_DIR = @ZSH_FUNCTIONS_DIR@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libext = @libext@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### +AUTOMAKE_OPTIONS = foreign nostdinc +@USE_EMBEDDED_ARES_FALSE@AM_CPPFLAGS = -I$(top_builddir)/include/curl \ +@USE_EMBEDDED_ARES_FALSE@ -I$(top_builddir)/include \ +@USE_EMBEDDED_ARES_FALSE@ -I$(top_srcdir)/include \ +@USE_EMBEDDED_ARES_FALSE@ -I$(top_builddir)/lib \ +@USE_EMBEDDED_ARES_FALSE@ -I$(top_srcdir)/lib \ +@USE_EMBEDDED_ARES_FALSE@ -I$(top_srcdir)/src \ +@USE_EMBEDDED_ARES_FALSE@ -I$(top_srcdir)/tests/libtest \ +@USE_EMBEDDED_ARES_FALSE@ -DCURL_STATICLIB -DUNITTESTS + +# Specify our include paths here, and do it relative to $(top_srcdir) and +# $(top_builddir), to ensure that these paths which belong to the library +# being currently built and tested are searched before the library which +# might possibly already be installed in the system. +# +# $(top_builddir)/include/curl for generated curlbuild.h included from curl.h +# $(top_builddir)/include for generated curlbuild.h inc. from lib/curl_setup.h +# $(top_srcdir)/include is for libcurl's external include files +# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file +# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "borrowed" files +# $(top_builddir)/ares is for in-tree c-ares's generated ares_build.h file +# $(top_srcdir)/ares is for in-tree c-ares's external include files +@USE_EMBEDDED_ARES_TRUE@AM_CPPFLAGS = -I$(top_builddir)/include/curl \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_builddir)/include \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_srcdir)/include \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_builddir)/lib \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_srcdir)/lib \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_srcdir)/src \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_srcdir)/tests/libtest \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_builddir)/ares \ +@USE_EMBEDDED_ARES_TRUE@ -I$(top_srcdir)/ares -DCURL_STATICLIB \ +@USE_EMBEDDED_ARES_TRUE@ -DUNITTESTS +EXTRA_DIST = Makefile.inc +LDADD = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la \ + @LDFLAGS@ @LIBCURL_LIBS@ + +DEPENDENCIES = $(top_builddir)/src/libcurltool.la \ + $(top_builddir)/lib/libcurlu.la + +UNITFILES = curlcheck.h \ + ../libtest/test.h \ + ../libtest/first.c + + +# These are all unit test programs +UNITPROGS = unit1300 unit1301 unit1302 unit1303 unit1304 unit1305 unit1307 \ + unit1308 unit1309 unit1330 unit1394 unit1395 unit1396 unit1397 unit1398 \ + unit1600 unit1601 unit1602 unit1603 unit1604 unit1605 + +unit1300_SOURCES = unit1300.c $(UNITFILES) +unit1300_CPPFLAGS = $(AM_CPPFLAGS) +unit1301_SOURCES = unit1301.c $(UNITFILES) +unit1301_CPPFLAGS = $(AM_CPPFLAGS) +unit1302_SOURCES = unit1302.c $(UNITFILES) +unit1302_CPPFLAGS = $(AM_CPPFLAGS) +unit1303_SOURCES = unit1303.c $(UNITFILES) +unit1303_CPPFLAGS = $(AM_CPPFLAGS) +unit1304_SOURCES = unit1304.c $(UNITFILES) +unit1304_CPPFLAGS = $(AM_CPPFLAGS) +unit1305_SOURCES = unit1305.c $(UNITFILES) +unit1305_CPPFLAGS = $(AM_CPPFLAGS) +unit1307_SOURCES = unit1307.c $(UNITFILES) +unit1307_CPPFLAGS = $(AM_CPPFLAGS) +unit1308_SOURCES = unit1308.c $(UNITFILES) +unit1308_CPPFLAGS = $(AM_CPPFLAGS) +unit1309_SOURCES = unit1309.c $(UNITFILES) +unit1309_CPPFLAGS = $(AM_CPPFLAGS) +unit1330_SOURCES = unit1330.c $(UNITFILES) +unit1330_CPPFLAGS = $(AM_CPPFLAGS) +unit1394_SOURCES = unit1394.c $(UNITFILES) +unit1394_CPPFLAGS = $(AM_CPPFLAGS) $(LIBMETALINK_CPPFLAGS) +unit1394_LDADD = @LIBMETALINK_LIBS@ $(top_builddir)/lib/libcurl.la @LIBCURL_LIBS@ +unit1394_LDFLAGS = @LIBMETALINK_LDFLAGS@ $(top_builddir)/src/libcurltool.la +unit1394_LIBS = +unit1395_SOURCES = unit1395.c $(UNITFILES) +unit1395_CPPFLAGS = $(AM_CPPFLAGS) +unit1396_SOURCES = unit1396.c $(UNITFILES) +unit1396_CPPFLAGS = $(AM_CPPFLAGS) +unit1397_SOURCES = unit1397.c $(UNITFILES) +unit1397_CPPFLAGS = $(AM_CPPFLAGS) +unit1398_SOURCES = unit1398.c $(UNITFILES) +unit1398_CPPFLAGS = $(AM_CPPFLAGS) +unit1600_SOURCES = unit1600.c $(UNITFILES) +unit1600_CPPFLAGS = $(AM_CPPFLAGS) +unit1601_SOURCES = unit1601.c $(UNITFILES) +unit1601_CPPFLAGS = $(AM_CPPFLAGS) +unit1602_SOURCES = unit1602.c $(UNITFILES) +unit1602_CPPFLAGS = $(AM_CPPFLAGS) +unit1603_SOURCES = unit1603.c $(UNITFILES) +unit1603_CPPFLAGS = $(AM_CPPFLAGS) +unit1604_SOURCES = unit1604.c $(UNITFILES) +unit1604_CPPFLAGS = $(AM_CPPFLAGS) $(LIBMETALINK_CPPFLAGS) +unit1605_SOURCES = unit1605.c $(UNITFILES) +unit1605_CPPFLAGS = $(AM_CPPFLAGS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/Makefile.inc $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tests/unit/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign tests/unit/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; +$(srcdir)/Makefile.inc $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +../libtest/$(am__dirstamp): + @$(MKDIR_P) ../libtest + @: > ../libtest/$(am__dirstamp) +../libtest/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) ../libtest/$(DEPDIR) + @: > ../libtest/$(DEPDIR)/$(am__dirstamp) +../libtest/unit1300-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1300$(EXEEXT): $(unit1300_OBJECTS) $(unit1300_DEPENDENCIES) $(EXTRA_unit1300_DEPENDENCIES) + @rm -f unit1300$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1300_OBJECTS) $(unit1300_LDADD) $(LIBS) +../libtest/unit1301-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1301$(EXEEXT): $(unit1301_OBJECTS) $(unit1301_DEPENDENCIES) $(EXTRA_unit1301_DEPENDENCIES) + @rm -f unit1301$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1301_OBJECTS) $(unit1301_LDADD) $(LIBS) +../libtest/unit1302-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1302$(EXEEXT): $(unit1302_OBJECTS) $(unit1302_DEPENDENCIES) $(EXTRA_unit1302_DEPENDENCIES) + @rm -f unit1302$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1302_OBJECTS) $(unit1302_LDADD) $(LIBS) +../libtest/unit1303-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1303$(EXEEXT): $(unit1303_OBJECTS) $(unit1303_DEPENDENCIES) $(EXTRA_unit1303_DEPENDENCIES) + @rm -f unit1303$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1303_OBJECTS) $(unit1303_LDADD) $(LIBS) +../libtest/unit1304-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1304$(EXEEXT): $(unit1304_OBJECTS) $(unit1304_DEPENDENCIES) $(EXTRA_unit1304_DEPENDENCIES) + @rm -f unit1304$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1304_OBJECTS) $(unit1304_LDADD) $(LIBS) +../libtest/unit1305-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1305$(EXEEXT): $(unit1305_OBJECTS) $(unit1305_DEPENDENCIES) $(EXTRA_unit1305_DEPENDENCIES) + @rm -f unit1305$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1305_OBJECTS) $(unit1305_LDADD) $(LIBS) +../libtest/unit1307-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1307$(EXEEXT): $(unit1307_OBJECTS) $(unit1307_DEPENDENCIES) $(EXTRA_unit1307_DEPENDENCIES) + @rm -f unit1307$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1307_OBJECTS) $(unit1307_LDADD) $(LIBS) +../libtest/unit1308-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1308$(EXEEXT): $(unit1308_OBJECTS) $(unit1308_DEPENDENCIES) $(EXTRA_unit1308_DEPENDENCIES) + @rm -f unit1308$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1308_OBJECTS) $(unit1308_LDADD) $(LIBS) +../libtest/unit1309-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1309$(EXEEXT): $(unit1309_OBJECTS) $(unit1309_DEPENDENCIES) $(EXTRA_unit1309_DEPENDENCIES) + @rm -f unit1309$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1309_OBJECTS) $(unit1309_LDADD) $(LIBS) +../libtest/unit1330-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1330$(EXEEXT): $(unit1330_OBJECTS) $(unit1330_DEPENDENCIES) $(EXTRA_unit1330_DEPENDENCIES) + @rm -f unit1330$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1330_OBJECTS) $(unit1330_LDADD) $(LIBS) +../libtest/unit1394-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1394$(EXEEXT): $(unit1394_OBJECTS) $(unit1394_DEPENDENCIES) $(EXTRA_unit1394_DEPENDENCIES) + @rm -f unit1394$(EXEEXT) + $(AM_V_CCLD)$(unit1394_LINK) $(unit1394_OBJECTS) $(unit1394_LDADD) $(LIBS) +../libtest/unit1395-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1395$(EXEEXT): $(unit1395_OBJECTS) $(unit1395_DEPENDENCIES) $(EXTRA_unit1395_DEPENDENCIES) + @rm -f unit1395$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1395_OBJECTS) $(unit1395_LDADD) $(LIBS) +../libtest/unit1396-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1396$(EXEEXT): $(unit1396_OBJECTS) $(unit1396_DEPENDENCIES) $(EXTRA_unit1396_DEPENDENCIES) + @rm -f unit1396$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1396_OBJECTS) $(unit1396_LDADD) $(LIBS) +../libtest/unit1397-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1397$(EXEEXT): $(unit1397_OBJECTS) $(unit1397_DEPENDENCIES) $(EXTRA_unit1397_DEPENDENCIES) + @rm -f unit1397$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1397_OBJECTS) $(unit1397_LDADD) $(LIBS) +../libtest/unit1398-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1398$(EXEEXT): $(unit1398_OBJECTS) $(unit1398_DEPENDENCIES) $(EXTRA_unit1398_DEPENDENCIES) + @rm -f unit1398$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1398_OBJECTS) $(unit1398_LDADD) $(LIBS) +../libtest/unit1600-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1600$(EXEEXT): $(unit1600_OBJECTS) $(unit1600_DEPENDENCIES) $(EXTRA_unit1600_DEPENDENCIES) + @rm -f unit1600$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1600_OBJECTS) $(unit1600_LDADD) $(LIBS) +../libtest/unit1601-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1601$(EXEEXT): $(unit1601_OBJECTS) $(unit1601_DEPENDENCIES) $(EXTRA_unit1601_DEPENDENCIES) + @rm -f unit1601$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1601_OBJECTS) $(unit1601_LDADD) $(LIBS) +../libtest/unit1602-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1602$(EXEEXT): $(unit1602_OBJECTS) $(unit1602_DEPENDENCIES) $(EXTRA_unit1602_DEPENDENCIES) + @rm -f unit1602$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1602_OBJECTS) $(unit1602_LDADD) $(LIBS) +../libtest/unit1603-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1603$(EXEEXT): $(unit1603_OBJECTS) $(unit1603_DEPENDENCIES) $(EXTRA_unit1603_DEPENDENCIES) + @rm -f unit1603$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1603_OBJECTS) $(unit1603_LDADD) $(LIBS) +../libtest/unit1604-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1604$(EXEEXT): $(unit1604_OBJECTS) $(unit1604_DEPENDENCIES) $(EXTRA_unit1604_DEPENDENCIES) + @rm -f unit1604$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1604_OBJECTS) $(unit1604_LDADD) $(LIBS) +../libtest/unit1605-first.$(OBJEXT): ../libtest/$(am__dirstamp) \ + ../libtest/$(DEPDIR)/$(am__dirstamp) + +unit1605$(EXEEXT): $(unit1605_OBJECTS) $(unit1605_DEPENDENCIES) $(EXTRA_unit1605_DEPENDENCIES) + @rm -f unit1605$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit1605_OBJECTS) $(unit1605_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f ../libtest/*.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1300-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1301-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1302-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1303-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1304-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1305-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1307-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1308-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1309-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1330-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1394-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1395-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1396-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1397-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1398-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1600-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1601-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1602-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1603-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1604-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../libtest/$(DEPDIR)/unit1605-first.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1300-unit1300.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1301-unit1301.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1302-unit1302.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1303-unit1303.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1304-unit1304.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1305-unit1305.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1307-unit1307.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1308-unit1308.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1309-unit1309.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1330-unit1330.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1394-unit1394.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1395-unit1395.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1396-unit1396.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1397-unit1397.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1398-unit1398.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1600-unit1600.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1601-unit1601.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1602-unit1602.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1603-unit1603.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1604-unit1604.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit1605-unit1605.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +unit1300-unit1300.o: unit1300.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1300_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1300-unit1300.o -MD -MP -MF $(DEPDIR)/unit1300-unit1300.Tpo -c -o unit1300-unit1300.o `test -f 'unit1300.c' || echo '$(srcdir)/'`unit1300.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1300-unit1300.Tpo $(DEPDIR)/unit1300-unit1300.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1300.c' object='unit1300-unit1300.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1300_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1300-unit1300.o `test -f 'unit1300.c' || echo '$(srcdir)/'`unit1300.c + +unit1300-unit1300.obj: unit1300.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1300_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1300-unit1300.obj -MD -MP -MF $(DEPDIR)/unit1300-unit1300.Tpo -c -o unit1300-unit1300.obj `if test -f 'unit1300.c'; then $(CYGPATH_W) 'unit1300.c'; else $(CYGPATH_W) '$(srcdir)/unit1300.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1300-unit1300.Tpo $(DEPDIR)/unit1300-unit1300.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1300.c' object='unit1300-unit1300.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1300_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1300-unit1300.obj `if test -f 'unit1300.c'; then $(CYGPATH_W) 'unit1300.c'; else $(CYGPATH_W) '$(srcdir)/unit1300.c'; fi` + +../libtest/unit1300-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1300_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1300-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1300-first.Tpo -c -o ../libtest/unit1300-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1300-first.Tpo ../libtest/$(DEPDIR)/unit1300-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1300-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1300_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1300-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1300-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1300_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1300-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1300-first.Tpo -c -o ../libtest/unit1300-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1300-first.Tpo ../libtest/$(DEPDIR)/unit1300-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1300-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1300_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1300-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1301-unit1301.o: unit1301.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1301_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1301-unit1301.o -MD -MP -MF $(DEPDIR)/unit1301-unit1301.Tpo -c -o unit1301-unit1301.o `test -f 'unit1301.c' || echo '$(srcdir)/'`unit1301.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1301-unit1301.Tpo $(DEPDIR)/unit1301-unit1301.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1301.c' object='unit1301-unit1301.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1301_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1301-unit1301.o `test -f 'unit1301.c' || echo '$(srcdir)/'`unit1301.c + +unit1301-unit1301.obj: unit1301.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1301_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1301-unit1301.obj -MD -MP -MF $(DEPDIR)/unit1301-unit1301.Tpo -c -o unit1301-unit1301.obj `if test -f 'unit1301.c'; then $(CYGPATH_W) 'unit1301.c'; else $(CYGPATH_W) '$(srcdir)/unit1301.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1301-unit1301.Tpo $(DEPDIR)/unit1301-unit1301.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1301.c' object='unit1301-unit1301.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1301_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1301-unit1301.obj `if test -f 'unit1301.c'; then $(CYGPATH_W) 'unit1301.c'; else $(CYGPATH_W) '$(srcdir)/unit1301.c'; fi` + +../libtest/unit1301-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1301_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1301-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1301-first.Tpo -c -o ../libtest/unit1301-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1301-first.Tpo ../libtest/$(DEPDIR)/unit1301-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1301-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1301_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1301-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1301-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1301_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1301-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1301-first.Tpo -c -o ../libtest/unit1301-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1301-first.Tpo ../libtest/$(DEPDIR)/unit1301-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1301-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1301_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1301-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1302-unit1302.o: unit1302.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1302_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1302-unit1302.o -MD -MP -MF $(DEPDIR)/unit1302-unit1302.Tpo -c -o unit1302-unit1302.o `test -f 'unit1302.c' || echo '$(srcdir)/'`unit1302.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1302-unit1302.Tpo $(DEPDIR)/unit1302-unit1302.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1302.c' object='unit1302-unit1302.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1302_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1302-unit1302.o `test -f 'unit1302.c' || echo '$(srcdir)/'`unit1302.c + +unit1302-unit1302.obj: unit1302.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1302_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1302-unit1302.obj -MD -MP -MF $(DEPDIR)/unit1302-unit1302.Tpo -c -o unit1302-unit1302.obj `if test -f 'unit1302.c'; then $(CYGPATH_W) 'unit1302.c'; else $(CYGPATH_W) '$(srcdir)/unit1302.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1302-unit1302.Tpo $(DEPDIR)/unit1302-unit1302.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1302.c' object='unit1302-unit1302.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1302_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1302-unit1302.obj `if test -f 'unit1302.c'; then $(CYGPATH_W) 'unit1302.c'; else $(CYGPATH_W) '$(srcdir)/unit1302.c'; fi` + +../libtest/unit1302-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1302_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1302-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1302-first.Tpo -c -o ../libtest/unit1302-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1302-first.Tpo ../libtest/$(DEPDIR)/unit1302-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1302-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1302_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1302-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1302-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1302_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1302-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1302-first.Tpo -c -o ../libtest/unit1302-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1302-first.Tpo ../libtest/$(DEPDIR)/unit1302-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1302-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1302_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1302-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1303-unit1303.o: unit1303.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1303_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1303-unit1303.o -MD -MP -MF $(DEPDIR)/unit1303-unit1303.Tpo -c -o unit1303-unit1303.o `test -f 'unit1303.c' || echo '$(srcdir)/'`unit1303.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1303-unit1303.Tpo $(DEPDIR)/unit1303-unit1303.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1303.c' object='unit1303-unit1303.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1303_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1303-unit1303.o `test -f 'unit1303.c' || echo '$(srcdir)/'`unit1303.c + +unit1303-unit1303.obj: unit1303.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1303_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1303-unit1303.obj -MD -MP -MF $(DEPDIR)/unit1303-unit1303.Tpo -c -o unit1303-unit1303.obj `if test -f 'unit1303.c'; then $(CYGPATH_W) 'unit1303.c'; else $(CYGPATH_W) '$(srcdir)/unit1303.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1303-unit1303.Tpo $(DEPDIR)/unit1303-unit1303.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1303.c' object='unit1303-unit1303.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1303_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1303-unit1303.obj `if test -f 'unit1303.c'; then $(CYGPATH_W) 'unit1303.c'; else $(CYGPATH_W) '$(srcdir)/unit1303.c'; fi` + +../libtest/unit1303-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1303_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1303-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1303-first.Tpo -c -o ../libtest/unit1303-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1303-first.Tpo ../libtest/$(DEPDIR)/unit1303-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1303-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1303_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1303-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1303-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1303_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1303-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1303-first.Tpo -c -o ../libtest/unit1303-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1303-first.Tpo ../libtest/$(DEPDIR)/unit1303-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1303-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1303_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1303-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1304-unit1304.o: unit1304.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1304_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1304-unit1304.o -MD -MP -MF $(DEPDIR)/unit1304-unit1304.Tpo -c -o unit1304-unit1304.o `test -f 'unit1304.c' || echo '$(srcdir)/'`unit1304.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1304-unit1304.Tpo $(DEPDIR)/unit1304-unit1304.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1304.c' object='unit1304-unit1304.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1304_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1304-unit1304.o `test -f 'unit1304.c' || echo '$(srcdir)/'`unit1304.c + +unit1304-unit1304.obj: unit1304.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1304_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1304-unit1304.obj -MD -MP -MF $(DEPDIR)/unit1304-unit1304.Tpo -c -o unit1304-unit1304.obj `if test -f 'unit1304.c'; then $(CYGPATH_W) 'unit1304.c'; else $(CYGPATH_W) '$(srcdir)/unit1304.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1304-unit1304.Tpo $(DEPDIR)/unit1304-unit1304.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1304.c' object='unit1304-unit1304.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1304_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1304-unit1304.obj `if test -f 'unit1304.c'; then $(CYGPATH_W) 'unit1304.c'; else $(CYGPATH_W) '$(srcdir)/unit1304.c'; fi` + +../libtest/unit1304-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1304_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1304-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1304-first.Tpo -c -o ../libtest/unit1304-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1304-first.Tpo ../libtest/$(DEPDIR)/unit1304-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1304-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1304_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1304-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1304-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1304_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1304-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1304-first.Tpo -c -o ../libtest/unit1304-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1304-first.Tpo ../libtest/$(DEPDIR)/unit1304-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1304-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1304_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1304-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1305-unit1305.o: unit1305.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1305_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1305-unit1305.o -MD -MP -MF $(DEPDIR)/unit1305-unit1305.Tpo -c -o unit1305-unit1305.o `test -f 'unit1305.c' || echo '$(srcdir)/'`unit1305.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1305-unit1305.Tpo $(DEPDIR)/unit1305-unit1305.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1305.c' object='unit1305-unit1305.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1305_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1305-unit1305.o `test -f 'unit1305.c' || echo '$(srcdir)/'`unit1305.c + +unit1305-unit1305.obj: unit1305.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1305_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1305-unit1305.obj -MD -MP -MF $(DEPDIR)/unit1305-unit1305.Tpo -c -o unit1305-unit1305.obj `if test -f 'unit1305.c'; then $(CYGPATH_W) 'unit1305.c'; else $(CYGPATH_W) '$(srcdir)/unit1305.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1305-unit1305.Tpo $(DEPDIR)/unit1305-unit1305.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1305.c' object='unit1305-unit1305.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1305_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1305-unit1305.obj `if test -f 'unit1305.c'; then $(CYGPATH_W) 'unit1305.c'; else $(CYGPATH_W) '$(srcdir)/unit1305.c'; fi` + +../libtest/unit1305-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1305_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1305-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1305-first.Tpo -c -o ../libtest/unit1305-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1305-first.Tpo ../libtest/$(DEPDIR)/unit1305-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1305-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1305_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1305-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1305-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1305_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1305-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1305-first.Tpo -c -o ../libtest/unit1305-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1305-first.Tpo ../libtest/$(DEPDIR)/unit1305-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1305-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1305_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1305-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1307-unit1307.o: unit1307.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1307_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1307-unit1307.o -MD -MP -MF $(DEPDIR)/unit1307-unit1307.Tpo -c -o unit1307-unit1307.o `test -f 'unit1307.c' || echo '$(srcdir)/'`unit1307.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1307-unit1307.Tpo $(DEPDIR)/unit1307-unit1307.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1307.c' object='unit1307-unit1307.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1307_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1307-unit1307.o `test -f 'unit1307.c' || echo '$(srcdir)/'`unit1307.c + +unit1307-unit1307.obj: unit1307.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1307_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1307-unit1307.obj -MD -MP -MF $(DEPDIR)/unit1307-unit1307.Tpo -c -o unit1307-unit1307.obj `if test -f 'unit1307.c'; then $(CYGPATH_W) 'unit1307.c'; else $(CYGPATH_W) '$(srcdir)/unit1307.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1307-unit1307.Tpo $(DEPDIR)/unit1307-unit1307.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1307.c' object='unit1307-unit1307.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1307_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1307-unit1307.obj `if test -f 'unit1307.c'; then $(CYGPATH_W) 'unit1307.c'; else $(CYGPATH_W) '$(srcdir)/unit1307.c'; fi` + +../libtest/unit1307-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1307_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1307-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1307-first.Tpo -c -o ../libtest/unit1307-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1307-first.Tpo ../libtest/$(DEPDIR)/unit1307-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1307-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1307_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1307-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1307-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1307_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1307-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1307-first.Tpo -c -o ../libtest/unit1307-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1307-first.Tpo ../libtest/$(DEPDIR)/unit1307-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1307-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1307_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1307-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1308-unit1308.o: unit1308.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1308_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1308-unit1308.o -MD -MP -MF $(DEPDIR)/unit1308-unit1308.Tpo -c -o unit1308-unit1308.o `test -f 'unit1308.c' || echo '$(srcdir)/'`unit1308.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1308-unit1308.Tpo $(DEPDIR)/unit1308-unit1308.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1308.c' object='unit1308-unit1308.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1308_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1308-unit1308.o `test -f 'unit1308.c' || echo '$(srcdir)/'`unit1308.c + +unit1308-unit1308.obj: unit1308.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1308_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1308-unit1308.obj -MD -MP -MF $(DEPDIR)/unit1308-unit1308.Tpo -c -o unit1308-unit1308.obj `if test -f 'unit1308.c'; then $(CYGPATH_W) 'unit1308.c'; else $(CYGPATH_W) '$(srcdir)/unit1308.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1308-unit1308.Tpo $(DEPDIR)/unit1308-unit1308.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1308.c' object='unit1308-unit1308.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1308_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1308-unit1308.obj `if test -f 'unit1308.c'; then $(CYGPATH_W) 'unit1308.c'; else $(CYGPATH_W) '$(srcdir)/unit1308.c'; fi` + +../libtest/unit1308-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1308_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1308-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1308-first.Tpo -c -o ../libtest/unit1308-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1308-first.Tpo ../libtest/$(DEPDIR)/unit1308-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1308-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1308_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1308-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1308-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1308_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1308-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1308-first.Tpo -c -o ../libtest/unit1308-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1308-first.Tpo ../libtest/$(DEPDIR)/unit1308-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1308-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1308_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1308-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1309-unit1309.o: unit1309.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1309_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1309-unit1309.o -MD -MP -MF $(DEPDIR)/unit1309-unit1309.Tpo -c -o unit1309-unit1309.o `test -f 'unit1309.c' || echo '$(srcdir)/'`unit1309.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1309-unit1309.Tpo $(DEPDIR)/unit1309-unit1309.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1309.c' object='unit1309-unit1309.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1309_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1309-unit1309.o `test -f 'unit1309.c' || echo '$(srcdir)/'`unit1309.c + +unit1309-unit1309.obj: unit1309.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1309_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1309-unit1309.obj -MD -MP -MF $(DEPDIR)/unit1309-unit1309.Tpo -c -o unit1309-unit1309.obj `if test -f 'unit1309.c'; then $(CYGPATH_W) 'unit1309.c'; else $(CYGPATH_W) '$(srcdir)/unit1309.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1309-unit1309.Tpo $(DEPDIR)/unit1309-unit1309.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1309.c' object='unit1309-unit1309.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1309_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1309-unit1309.obj `if test -f 'unit1309.c'; then $(CYGPATH_W) 'unit1309.c'; else $(CYGPATH_W) '$(srcdir)/unit1309.c'; fi` + +../libtest/unit1309-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1309_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1309-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1309-first.Tpo -c -o ../libtest/unit1309-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1309-first.Tpo ../libtest/$(DEPDIR)/unit1309-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1309-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1309_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1309-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1309-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1309_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1309-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1309-first.Tpo -c -o ../libtest/unit1309-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1309-first.Tpo ../libtest/$(DEPDIR)/unit1309-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1309-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1309_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1309-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1330-unit1330.o: unit1330.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1330_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1330-unit1330.o -MD -MP -MF $(DEPDIR)/unit1330-unit1330.Tpo -c -o unit1330-unit1330.o `test -f 'unit1330.c' || echo '$(srcdir)/'`unit1330.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1330-unit1330.Tpo $(DEPDIR)/unit1330-unit1330.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1330.c' object='unit1330-unit1330.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1330_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1330-unit1330.o `test -f 'unit1330.c' || echo '$(srcdir)/'`unit1330.c + +unit1330-unit1330.obj: unit1330.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1330_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1330-unit1330.obj -MD -MP -MF $(DEPDIR)/unit1330-unit1330.Tpo -c -o unit1330-unit1330.obj `if test -f 'unit1330.c'; then $(CYGPATH_W) 'unit1330.c'; else $(CYGPATH_W) '$(srcdir)/unit1330.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1330-unit1330.Tpo $(DEPDIR)/unit1330-unit1330.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1330.c' object='unit1330-unit1330.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1330_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1330-unit1330.obj `if test -f 'unit1330.c'; then $(CYGPATH_W) 'unit1330.c'; else $(CYGPATH_W) '$(srcdir)/unit1330.c'; fi` + +../libtest/unit1330-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1330_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1330-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1330-first.Tpo -c -o ../libtest/unit1330-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1330-first.Tpo ../libtest/$(DEPDIR)/unit1330-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1330-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1330_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1330-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1330-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1330_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1330-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1330-first.Tpo -c -o ../libtest/unit1330-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1330-first.Tpo ../libtest/$(DEPDIR)/unit1330-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1330-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1330_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1330-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1394-unit1394.o: unit1394.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1394_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1394-unit1394.o -MD -MP -MF $(DEPDIR)/unit1394-unit1394.Tpo -c -o unit1394-unit1394.o `test -f 'unit1394.c' || echo '$(srcdir)/'`unit1394.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1394-unit1394.Tpo $(DEPDIR)/unit1394-unit1394.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1394.c' object='unit1394-unit1394.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1394_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1394-unit1394.o `test -f 'unit1394.c' || echo '$(srcdir)/'`unit1394.c + +unit1394-unit1394.obj: unit1394.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1394_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1394-unit1394.obj -MD -MP -MF $(DEPDIR)/unit1394-unit1394.Tpo -c -o unit1394-unit1394.obj `if test -f 'unit1394.c'; then $(CYGPATH_W) 'unit1394.c'; else $(CYGPATH_W) '$(srcdir)/unit1394.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1394-unit1394.Tpo $(DEPDIR)/unit1394-unit1394.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1394.c' object='unit1394-unit1394.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1394_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1394-unit1394.obj `if test -f 'unit1394.c'; then $(CYGPATH_W) 'unit1394.c'; else $(CYGPATH_W) '$(srcdir)/unit1394.c'; fi` + +../libtest/unit1394-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1394_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1394-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1394-first.Tpo -c -o ../libtest/unit1394-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1394-first.Tpo ../libtest/$(DEPDIR)/unit1394-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1394-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1394_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1394-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1394-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1394_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1394-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1394-first.Tpo -c -o ../libtest/unit1394-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1394-first.Tpo ../libtest/$(DEPDIR)/unit1394-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1394-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1394_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1394-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1395-unit1395.o: unit1395.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1395_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1395-unit1395.o -MD -MP -MF $(DEPDIR)/unit1395-unit1395.Tpo -c -o unit1395-unit1395.o `test -f 'unit1395.c' || echo '$(srcdir)/'`unit1395.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1395-unit1395.Tpo $(DEPDIR)/unit1395-unit1395.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1395.c' object='unit1395-unit1395.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1395_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1395-unit1395.o `test -f 'unit1395.c' || echo '$(srcdir)/'`unit1395.c + +unit1395-unit1395.obj: unit1395.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1395_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1395-unit1395.obj -MD -MP -MF $(DEPDIR)/unit1395-unit1395.Tpo -c -o unit1395-unit1395.obj `if test -f 'unit1395.c'; then $(CYGPATH_W) 'unit1395.c'; else $(CYGPATH_W) '$(srcdir)/unit1395.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1395-unit1395.Tpo $(DEPDIR)/unit1395-unit1395.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1395.c' object='unit1395-unit1395.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1395_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1395-unit1395.obj `if test -f 'unit1395.c'; then $(CYGPATH_W) 'unit1395.c'; else $(CYGPATH_W) '$(srcdir)/unit1395.c'; fi` + +../libtest/unit1395-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1395_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1395-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1395-first.Tpo -c -o ../libtest/unit1395-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1395-first.Tpo ../libtest/$(DEPDIR)/unit1395-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1395-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1395_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1395-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1395-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1395_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1395-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1395-first.Tpo -c -o ../libtest/unit1395-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1395-first.Tpo ../libtest/$(DEPDIR)/unit1395-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1395-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1395_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1395-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1396-unit1396.o: unit1396.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1396_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1396-unit1396.o -MD -MP -MF $(DEPDIR)/unit1396-unit1396.Tpo -c -o unit1396-unit1396.o `test -f 'unit1396.c' || echo '$(srcdir)/'`unit1396.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1396-unit1396.Tpo $(DEPDIR)/unit1396-unit1396.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1396.c' object='unit1396-unit1396.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1396_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1396-unit1396.o `test -f 'unit1396.c' || echo '$(srcdir)/'`unit1396.c + +unit1396-unit1396.obj: unit1396.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1396_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1396-unit1396.obj -MD -MP -MF $(DEPDIR)/unit1396-unit1396.Tpo -c -o unit1396-unit1396.obj `if test -f 'unit1396.c'; then $(CYGPATH_W) 'unit1396.c'; else $(CYGPATH_W) '$(srcdir)/unit1396.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1396-unit1396.Tpo $(DEPDIR)/unit1396-unit1396.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1396.c' object='unit1396-unit1396.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1396_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1396-unit1396.obj `if test -f 'unit1396.c'; then $(CYGPATH_W) 'unit1396.c'; else $(CYGPATH_W) '$(srcdir)/unit1396.c'; fi` + +../libtest/unit1396-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1396_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1396-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1396-first.Tpo -c -o ../libtest/unit1396-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1396-first.Tpo ../libtest/$(DEPDIR)/unit1396-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1396-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1396_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1396-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1396-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1396_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1396-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1396-first.Tpo -c -o ../libtest/unit1396-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1396-first.Tpo ../libtest/$(DEPDIR)/unit1396-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1396-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1396_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1396-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1397-unit1397.o: unit1397.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1397_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1397-unit1397.o -MD -MP -MF $(DEPDIR)/unit1397-unit1397.Tpo -c -o unit1397-unit1397.o `test -f 'unit1397.c' || echo '$(srcdir)/'`unit1397.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1397-unit1397.Tpo $(DEPDIR)/unit1397-unit1397.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1397.c' object='unit1397-unit1397.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1397_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1397-unit1397.o `test -f 'unit1397.c' || echo '$(srcdir)/'`unit1397.c + +unit1397-unit1397.obj: unit1397.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1397_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1397-unit1397.obj -MD -MP -MF $(DEPDIR)/unit1397-unit1397.Tpo -c -o unit1397-unit1397.obj `if test -f 'unit1397.c'; then $(CYGPATH_W) 'unit1397.c'; else $(CYGPATH_W) '$(srcdir)/unit1397.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1397-unit1397.Tpo $(DEPDIR)/unit1397-unit1397.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1397.c' object='unit1397-unit1397.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1397_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1397-unit1397.obj `if test -f 'unit1397.c'; then $(CYGPATH_W) 'unit1397.c'; else $(CYGPATH_W) '$(srcdir)/unit1397.c'; fi` + +../libtest/unit1397-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1397_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1397-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1397-first.Tpo -c -o ../libtest/unit1397-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1397-first.Tpo ../libtest/$(DEPDIR)/unit1397-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1397-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1397_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1397-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1397-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1397_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1397-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1397-first.Tpo -c -o ../libtest/unit1397-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1397-first.Tpo ../libtest/$(DEPDIR)/unit1397-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1397-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1397_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1397-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1398-unit1398.o: unit1398.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1398_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1398-unit1398.o -MD -MP -MF $(DEPDIR)/unit1398-unit1398.Tpo -c -o unit1398-unit1398.o `test -f 'unit1398.c' || echo '$(srcdir)/'`unit1398.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1398-unit1398.Tpo $(DEPDIR)/unit1398-unit1398.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1398.c' object='unit1398-unit1398.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1398_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1398-unit1398.o `test -f 'unit1398.c' || echo '$(srcdir)/'`unit1398.c + +unit1398-unit1398.obj: unit1398.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1398_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1398-unit1398.obj -MD -MP -MF $(DEPDIR)/unit1398-unit1398.Tpo -c -o unit1398-unit1398.obj `if test -f 'unit1398.c'; then $(CYGPATH_W) 'unit1398.c'; else $(CYGPATH_W) '$(srcdir)/unit1398.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1398-unit1398.Tpo $(DEPDIR)/unit1398-unit1398.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1398.c' object='unit1398-unit1398.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1398_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1398-unit1398.obj `if test -f 'unit1398.c'; then $(CYGPATH_W) 'unit1398.c'; else $(CYGPATH_W) '$(srcdir)/unit1398.c'; fi` + +../libtest/unit1398-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1398_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1398-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1398-first.Tpo -c -o ../libtest/unit1398-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1398-first.Tpo ../libtest/$(DEPDIR)/unit1398-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1398-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1398_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1398-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1398-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1398_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1398-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1398-first.Tpo -c -o ../libtest/unit1398-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1398-first.Tpo ../libtest/$(DEPDIR)/unit1398-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1398-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1398_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1398-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1600-unit1600.o: unit1600.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1600_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1600-unit1600.o -MD -MP -MF $(DEPDIR)/unit1600-unit1600.Tpo -c -o unit1600-unit1600.o `test -f 'unit1600.c' || echo '$(srcdir)/'`unit1600.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1600-unit1600.Tpo $(DEPDIR)/unit1600-unit1600.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1600.c' object='unit1600-unit1600.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1600_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1600-unit1600.o `test -f 'unit1600.c' || echo '$(srcdir)/'`unit1600.c + +unit1600-unit1600.obj: unit1600.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1600_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1600-unit1600.obj -MD -MP -MF $(DEPDIR)/unit1600-unit1600.Tpo -c -o unit1600-unit1600.obj `if test -f 'unit1600.c'; then $(CYGPATH_W) 'unit1600.c'; else $(CYGPATH_W) '$(srcdir)/unit1600.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1600-unit1600.Tpo $(DEPDIR)/unit1600-unit1600.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1600.c' object='unit1600-unit1600.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1600_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1600-unit1600.obj `if test -f 'unit1600.c'; then $(CYGPATH_W) 'unit1600.c'; else $(CYGPATH_W) '$(srcdir)/unit1600.c'; fi` + +../libtest/unit1600-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1600_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1600-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1600-first.Tpo -c -o ../libtest/unit1600-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1600-first.Tpo ../libtest/$(DEPDIR)/unit1600-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1600-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1600_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1600-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1600-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1600_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1600-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1600-first.Tpo -c -o ../libtest/unit1600-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1600-first.Tpo ../libtest/$(DEPDIR)/unit1600-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1600-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1600_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1600-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1601-unit1601.o: unit1601.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1601_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1601-unit1601.o -MD -MP -MF $(DEPDIR)/unit1601-unit1601.Tpo -c -o unit1601-unit1601.o `test -f 'unit1601.c' || echo '$(srcdir)/'`unit1601.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1601-unit1601.Tpo $(DEPDIR)/unit1601-unit1601.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1601.c' object='unit1601-unit1601.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1601_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1601-unit1601.o `test -f 'unit1601.c' || echo '$(srcdir)/'`unit1601.c + +unit1601-unit1601.obj: unit1601.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1601_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1601-unit1601.obj -MD -MP -MF $(DEPDIR)/unit1601-unit1601.Tpo -c -o unit1601-unit1601.obj `if test -f 'unit1601.c'; then $(CYGPATH_W) 'unit1601.c'; else $(CYGPATH_W) '$(srcdir)/unit1601.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1601-unit1601.Tpo $(DEPDIR)/unit1601-unit1601.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1601.c' object='unit1601-unit1601.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1601_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1601-unit1601.obj `if test -f 'unit1601.c'; then $(CYGPATH_W) 'unit1601.c'; else $(CYGPATH_W) '$(srcdir)/unit1601.c'; fi` + +../libtest/unit1601-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1601_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1601-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1601-first.Tpo -c -o ../libtest/unit1601-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1601-first.Tpo ../libtest/$(DEPDIR)/unit1601-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1601-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1601_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1601-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1601-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1601_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1601-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1601-first.Tpo -c -o ../libtest/unit1601-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1601-first.Tpo ../libtest/$(DEPDIR)/unit1601-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1601-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1601_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1601-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1602-unit1602.o: unit1602.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1602_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1602-unit1602.o -MD -MP -MF $(DEPDIR)/unit1602-unit1602.Tpo -c -o unit1602-unit1602.o `test -f 'unit1602.c' || echo '$(srcdir)/'`unit1602.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1602-unit1602.Tpo $(DEPDIR)/unit1602-unit1602.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1602.c' object='unit1602-unit1602.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1602_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1602-unit1602.o `test -f 'unit1602.c' || echo '$(srcdir)/'`unit1602.c + +unit1602-unit1602.obj: unit1602.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1602_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1602-unit1602.obj -MD -MP -MF $(DEPDIR)/unit1602-unit1602.Tpo -c -o unit1602-unit1602.obj `if test -f 'unit1602.c'; then $(CYGPATH_W) 'unit1602.c'; else $(CYGPATH_W) '$(srcdir)/unit1602.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1602-unit1602.Tpo $(DEPDIR)/unit1602-unit1602.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1602.c' object='unit1602-unit1602.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1602_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1602-unit1602.obj `if test -f 'unit1602.c'; then $(CYGPATH_W) 'unit1602.c'; else $(CYGPATH_W) '$(srcdir)/unit1602.c'; fi` + +../libtest/unit1602-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1602_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1602-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1602-first.Tpo -c -o ../libtest/unit1602-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1602-first.Tpo ../libtest/$(DEPDIR)/unit1602-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1602-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1602_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1602-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1602-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1602_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1602-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1602-first.Tpo -c -o ../libtest/unit1602-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1602-first.Tpo ../libtest/$(DEPDIR)/unit1602-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1602-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1602_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1602-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1603-unit1603.o: unit1603.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1603_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1603-unit1603.o -MD -MP -MF $(DEPDIR)/unit1603-unit1603.Tpo -c -o unit1603-unit1603.o `test -f 'unit1603.c' || echo '$(srcdir)/'`unit1603.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1603-unit1603.Tpo $(DEPDIR)/unit1603-unit1603.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1603.c' object='unit1603-unit1603.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1603_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1603-unit1603.o `test -f 'unit1603.c' || echo '$(srcdir)/'`unit1603.c + +unit1603-unit1603.obj: unit1603.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1603_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1603-unit1603.obj -MD -MP -MF $(DEPDIR)/unit1603-unit1603.Tpo -c -o unit1603-unit1603.obj `if test -f 'unit1603.c'; then $(CYGPATH_W) 'unit1603.c'; else $(CYGPATH_W) '$(srcdir)/unit1603.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1603-unit1603.Tpo $(DEPDIR)/unit1603-unit1603.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1603.c' object='unit1603-unit1603.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1603_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1603-unit1603.obj `if test -f 'unit1603.c'; then $(CYGPATH_W) 'unit1603.c'; else $(CYGPATH_W) '$(srcdir)/unit1603.c'; fi` + +../libtest/unit1603-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1603_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1603-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1603-first.Tpo -c -o ../libtest/unit1603-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1603-first.Tpo ../libtest/$(DEPDIR)/unit1603-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1603-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1603_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1603-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1603-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1603_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1603-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1603-first.Tpo -c -o ../libtest/unit1603-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1603-first.Tpo ../libtest/$(DEPDIR)/unit1603-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1603-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1603_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1603-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1604-unit1604.o: unit1604.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1604_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1604-unit1604.o -MD -MP -MF $(DEPDIR)/unit1604-unit1604.Tpo -c -o unit1604-unit1604.o `test -f 'unit1604.c' || echo '$(srcdir)/'`unit1604.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1604-unit1604.Tpo $(DEPDIR)/unit1604-unit1604.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1604.c' object='unit1604-unit1604.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1604_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1604-unit1604.o `test -f 'unit1604.c' || echo '$(srcdir)/'`unit1604.c + +unit1604-unit1604.obj: unit1604.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1604_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1604-unit1604.obj -MD -MP -MF $(DEPDIR)/unit1604-unit1604.Tpo -c -o unit1604-unit1604.obj `if test -f 'unit1604.c'; then $(CYGPATH_W) 'unit1604.c'; else $(CYGPATH_W) '$(srcdir)/unit1604.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1604-unit1604.Tpo $(DEPDIR)/unit1604-unit1604.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1604.c' object='unit1604-unit1604.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1604_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1604-unit1604.obj `if test -f 'unit1604.c'; then $(CYGPATH_W) 'unit1604.c'; else $(CYGPATH_W) '$(srcdir)/unit1604.c'; fi` + +../libtest/unit1604-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1604_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1604-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1604-first.Tpo -c -o ../libtest/unit1604-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1604-first.Tpo ../libtest/$(DEPDIR)/unit1604-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1604-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1604_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1604-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1604-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1604_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1604-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1604-first.Tpo -c -o ../libtest/unit1604-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1604-first.Tpo ../libtest/$(DEPDIR)/unit1604-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1604-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1604_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1604-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +unit1605-unit1605.o: unit1605.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1605_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1605-unit1605.o -MD -MP -MF $(DEPDIR)/unit1605-unit1605.Tpo -c -o unit1605-unit1605.o `test -f 'unit1605.c' || echo '$(srcdir)/'`unit1605.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1605-unit1605.Tpo $(DEPDIR)/unit1605-unit1605.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1605.c' object='unit1605-unit1605.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1605_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1605-unit1605.o `test -f 'unit1605.c' || echo '$(srcdir)/'`unit1605.c + +unit1605-unit1605.obj: unit1605.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1605_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unit1605-unit1605.obj -MD -MP -MF $(DEPDIR)/unit1605-unit1605.Tpo -c -o unit1605-unit1605.obj `if test -f 'unit1605.c'; then $(CYGPATH_W) 'unit1605.c'; else $(CYGPATH_W) '$(srcdir)/unit1605.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/unit1605-unit1605.Tpo $(DEPDIR)/unit1605-unit1605.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unit1605.c' object='unit1605-unit1605.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1605_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unit1605-unit1605.obj `if test -f 'unit1605.c'; then $(CYGPATH_W) 'unit1605.c'; else $(CYGPATH_W) '$(srcdir)/unit1605.c'; fi` + +../libtest/unit1605-first.o: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1605_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1605-first.o -MD -MP -MF ../libtest/$(DEPDIR)/unit1605-first.Tpo -c -o ../libtest/unit1605-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1605-first.Tpo ../libtest/$(DEPDIR)/unit1605-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1605-first.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1605_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1605-first.o `test -f '../libtest/first.c' || echo '$(srcdir)/'`../libtest/first.c + +../libtest/unit1605-first.obj: ../libtest/first.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1605_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ../libtest/unit1605-first.obj -MD -MP -MF ../libtest/$(DEPDIR)/unit1605-first.Tpo -c -o ../libtest/unit1605-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../libtest/$(DEPDIR)/unit1605-first.Tpo ../libtest/$(DEPDIR)/unit1605-first.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../libtest/first.c' object='../libtest/unit1605-first.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(unit1605_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../libtest/unit1605-first.obj `if test -f '../libtest/first.c'; then $(CYGPATH_W) '../libtest/first.c'; else $(CYGPATH_W) '$(srcdir)/../libtest/first.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f ../libtest/$(DEPDIR)/$(am__dirstamp) + -rm -f ../libtest/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ../libtest/$(DEPDIR) ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ../libtest/$(DEPDIR) ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Makefile.inc provides neat definitions + +checksrc: + @PERL@ $(top_srcdir)/lib/checksrc.pl $(srcdir)/*.c + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/deps/curl-7.51.0/tests/unit/Makefile.inc b/deps/curl-7.51.0/tests/unit/Makefile.inc new file mode 100644 index 0000000000..e7db96f50e --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/Makefile.inc @@ -0,0 +1,76 @@ +# these files are used in every single unit test program + +UNITFILES = curlcheck.h \ + ../libtest/test.h \ + ../libtest/first.c + +# These are all unit test programs +UNITPROGS = unit1300 unit1301 unit1302 unit1303 unit1304 unit1305 unit1307 \ + unit1308 unit1309 unit1330 unit1394 unit1395 unit1396 unit1397 unit1398 \ + unit1600 unit1601 unit1602 unit1603 unit1604 unit1605 + +unit1300_SOURCES = unit1300.c $(UNITFILES) +unit1300_CPPFLAGS = $(AM_CPPFLAGS) + +unit1301_SOURCES = unit1301.c $(UNITFILES) +unit1301_CPPFLAGS = $(AM_CPPFLAGS) + +unit1302_SOURCES = unit1302.c $(UNITFILES) +unit1302_CPPFLAGS = $(AM_CPPFLAGS) + +unit1303_SOURCES = unit1303.c $(UNITFILES) +unit1303_CPPFLAGS = $(AM_CPPFLAGS) + +unit1304_SOURCES = unit1304.c $(UNITFILES) +unit1304_CPPFLAGS = $(AM_CPPFLAGS) + +unit1305_SOURCES = unit1305.c $(UNITFILES) +unit1305_CPPFLAGS = $(AM_CPPFLAGS) + +unit1307_SOURCES = unit1307.c $(UNITFILES) +unit1307_CPPFLAGS = $(AM_CPPFLAGS) + +unit1308_SOURCES = unit1308.c $(UNITFILES) +unit1308_CPPFLAGS = $(AM_CPPFLAGS) + +unit1309_SOURCES = unit1309.c $(UNITFILES) +unit1309_CPPFLAGS = $(AM_CPPFLAGS) + +unit1330_SOURCES = unit1330.c $(UNITFILES) +unit1330_CPPFLAGS = $(AM_CPPFLAGS) + +unit1394_SOURCES = unit1394.c $(UNITFILES) +unit1394_CPPFLAGS = $(AM_CPPFLAGS) $(LIBMETALINK_CPPFLAGS) +unit1394_LDADD = @LIBMETALINK_LIBS@ $(top_builddir)/lib/libcurl.la @LIBCURL_LIBS@ +unit1394_LDFLAGS = @LIBMETALINK_LDFLAGS@ $(top_builddir)/src/libcurltool.la +unit1394_LIBS = + +unit1395_SOURCES = unit1395.c $(UNITFILES) +unit1395_CPPFLAGS = $(AM_CPPFLAGS) + +unit1396_SOURCES = unit1396.c $(UNITFILES) +unit1396_CPPFLAGS = $(AM_CPPFLAGS) + +unit1397_SOURCES = unit1397.c $(UNITFILES) +unit1397_CPPFLAGS = $(AM_CPPFLAGS) + +unit1398_SOURCES = unit1398.c $(UNITFILES) +unit1398_CPPFLAGS = $(AM_CPPFLAGS) + +unit1600_SOURCES = unit1600.c $(UNITFILES) +unit1600_CPPFLAGS = $(AM_CPPFLAGS) + +unit1601_SOURCES = unit1601.c $(UNITFILES) +unit1601_CPPFLAGS = $(AM_CPPFLAGS) + +unit1602_SOURCES = unit1602.c $(UNITFILES) +unit1602_CPPFLAGS = $(AM_CPPFLAGS) + +unit1603_SOURCES = unit1603.c $(UNITFILES) +unit1603_CPPFLAGS = $(AM_CPPFLAGS) + +unit1604_SOURCES = unit1604.c $(UNITFILES) +unit1604_CPPFLAGS = $(AM_CPPFLAGS) $(LIBMETALINK_CPPFLAGS) + +unit1605_SOURCES = unit1605.c $(UNITFILES) +unit1605_CPPFLAGS = $(AM_CPPFLAGS) diff --git a/deps/curl-7.51.0/tests/unit/README b/deps/curl-7.51.0/tests/unit/README new file mode 100644 index 0000000000..301cd17d56 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/README @@ -0,0 +1,70 @@ +Unit tests +========== + +The goal is to add tests for *ALL* functions in libcurl. If functions are too +big and complicated, we should split them into smaller and testable ones. + +Build Unit Tests +================ + +'./configure --enable-debug' is required for the unit tests to build. To +enable unit tests, there will be a separate static libcurl built that will be +used exclusively for linking unit test programs. Just build everything as +normal, and then you can run the unit test cases as well. + +Run Unit Tests +============== + +Unit tests are run as part of the regular test suite. If you have built +everything to run unit tests, to can do 'make test' at the root level. Or you +can 'cd tests' and then invoke individual unit tests with ./runtests.pl NNNN +where NNNN is the specific test number. + +Debug Unit Tests +================ + +If a specific test fails you will get told. The test case then has output left +in the log/ subdirectory, but most importantly you can re-run the test again +using gdb by doing ./runtests.pl -g NNNN. That is, add a -g to make it start +up gdb and run the same case using that. + +Write Unit Tests +================ + +We put tests that focus on an area or a specific function into a single C +source file. The source file should be named 'unitNNNN.c' where NNNN is a +number that starts with 1300 and you can pick the next free number. + +You also need a separate file called tests/data/testNNNN (using the same +number) that describes your test case. See the test1300 file for inspiration +and the tests/FILEFORMAT documentation. + +For the actual C file, here's a very simple example: + +----------------------- start ------------------------------- +#include "curlcheck.h" + +#include "a libcurl header.h" /* from the lib dir */ + +static void unit_setup( void ) +{ + /* whatever you want done first */ +} + +static void unit_stop( void ) +{ + /* done before shutting down and exiting */ +} + +UNITTEST_START + + /* here you start doing things and checking that the results are good */ + + fail_unless( size == 0 , "initial size should be zero" ); + fail_if( head == NULL , "head should not be initiated to NULL" ); + + /* you end the test code like this: */ + +UNITTEST_STOP + +----------------------- end ------------------------------- diff --git a/deps/curl-7.51.0/tests/unit/curlcheck.h b/deps/curl-7.51.0/tests/unit/curlcheck.h new file mode 100644 index 0000000000..0660e2bedd --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/curlcheck.h @@ -0,0 +1,101 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "test.h" + +/* The fail macros mark the current test step as failed, and continue */ +#define fail_if(expr, msg) \ + if(expr) { \ + fprintf(stderr, "%s:%d Assertion '%s' met: %s\n", \ + __FILE__, __LINE__, #expr, msg); \ + unitfail++; \ + } + +#define fail_unless(expr, msg) \ + if(!(expr)) { \ + fprintf(stderr, "%s:%d Assertion '%s' failed: %s\n", \ + __FILE__, __LINE__, #expr, msg); \ + unitfail++; \ + } + +#define verify_memory(dynamic, check, len) \ + if(dynamic && memcmp(dynamic, check, len)) { \ + fprintf(stderr, "%s:%d Memory buffer mismatch size %d. '%s' is not\n", \ + __FILE__, __LINE__, len, hexdump((unsigned char *)check, len)); \ + fprintf(stderr, "%s:%d the same as '%s'\n", \ + __FILE__, __LINE__, hexdump((unsigned char *)dynamic, len)); \ + unitfail++; \ + } + +/* fail() is for when the test case figured out by itself that a check + proved a failure */ +#define fail(msg) do { \ + fprintf(stderr, "%s:%d test failed: '%s'\n", \ + __FILE__, __LINE__, msg); \ + unitfail++; \ + } WHILE_FALSE + + +/* The abort macros mark the current test step as failed, and exit the test */ +#define abort_if(expr, msg) \ + if(expr) { \ + fprintf(stderr, "%s:%d Abort assertion '%s' met: %s\n", \ + __FILE__, __LINE__, #expr, msg); \ + unitfail++; \ + goto unit_test_abort; \ + } + +#define abort_unless(expr, msg) \ + if(!(expr)) { \ + fprintf(stderr, "%s:%d Abort assertion '%s' failed: %s\n", \ + __FILE__, __LINE__, #expr, msg); \ + unitfail++; \ + goto unit_test_abort; \ + } + +#define abort_test(msg) do { \ + fprintf(stderr, "%s:%d test aborted: '%s'\n", \ + __FILE__, __LINE__, msg); \ + unitfail++; \ + goto unit_test_abort; \ + } WHILE_FALSE + + + +extern int unitfail; + +#define UNITTEST_START \ + int test(char *arg) \ + { \ + (void)arg; \ + if(unit_setup()) { \ + fail("unit_setup() failure"); \ + } \ + else { + +#define UNITTEST_STOP \ + goto unit_test_abort; /* avoid warning */ \ +unit_test_abort: \ + unit_stop(); \ + } \ + return unitfail; \ + } + diff --git a/deps/curl-7.51.0/tests/unit/unit1300.c b/deps/curl-7.51.0/tests/unit/unit1300.c new file mode 100644 index 0000000000..c4d9dd90d5 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1300.c @@ -0,0 +1,292 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "llist.h" + +static struct curl_llist *llist; + +static struct curl_llist *llist_destination; + +static void test_curl_llist_dtor(void *key, void *value) +{ + /* used by the llist API, does nothing here */ + (void)key; + (void)value; +} + +static CURLcode unit_setup(void) +{ + llist = Curl_llist_alloc(test_curl_llist_dtor); + if(!llist) + return CURLE_OUT_OF_MEMORY; + llist_destination = Curl_llist_alloc(test_curl_llist_dtor); + if(!llist_destination) { + Curl_llist_destroy(llist, NULL); + return CURLE_OUT_OF_MEMORY; + } + + return CURLE_OK; +} + +static void unit_stop(void) +{ + Curl_llist_destroy(llist, NULL); + Curl_llist_destroy(llist_destination, NULL); +} + +UNITTEST_START + int unusedData_case1 = 1; + int unusedData_case2 = 2; + int unusedData_case3 = 3; + struct curl_llist_element *head; + struct curl_llist_element *element_next; + struct curl_llist_element *element_prev; + struct curl_llist_element *to_remove; + size_t llist_size = Curl_llist_count(llist); + int curlErrCode = 0; + + /** + * testing llist_init + * case 1: + * list initiation + * @assumptions: + * 1: list size will be 0 + * 2: list head will be NULL + * 3: list tail will be NULL + * 4: list dtor will be NULL + */ + + fail_unless(llist->size == 0, "list initial size should be zero"); + fail_unless(llist->head == NULL, "list head should initiate to NULL"); + fail_unless(llist->tail == NULL, "list tail should intiate to NULL"); + fail_unless(llist->dtor == test_curl_llist_dtor, + "list dtor shold initiate to test_curl_llist_dtor"); + + /** + * testing Curl_llist_insert_next + * case 1: + * list is empty + * @assumptions: + * 1: list size will be 1 + * 2: list head will hold the data "unusedData_case1" + * 3: list tail will be the same as list head + */ + + curlErrCode = Curl_llist_insert_next(llist, llist->head, &unusedData_case1); + if(curlErrCode == 1) { + fail_unless(Curl_llist_count(llist) == 1, + "List size should be 1 after adding a new element"); + /*test that the list head data holds my unusedData */ + fail_unless(llist->head->ptr == &unusedData_case1, + "List size should be 1 after adding a new element"); + /*same goes for the list tail */ + fail_unless(llist->tail == llist->head, + "List size should be 1 after adding a new element"); + + /** + * testing Curl_llist_insert_next + * case 2: + * list has 1 element, adding one element after the head + * @assumptions: + * 1: the element next to head should be our newly created element + * 2: the list tail should be our newly created element + */ + + curlErrCode = Curl_llist_insert_next(llist, llist->head, + &unusedData_case3); + if(curlErrCode == 1) { + fail_unless(llist->head->next->ptr == &unusedData_case3, + "the node next to head is not getting set correctly"); + fail_unless(llist->tail->ptr == &unusedData_case3, + "the list tail is not getting set correctly"); + } + else { + printf("skipping Curl_llist_insert_next as a non " + "success error code was returned\n"); + } + + /** + * testing Curl_llist_insert_next + * case 3: + * list has >1 element, adding one element after "NULL" + * @assumptions: + * 1: the element next to head should be our newly created element + * 2: the list tail should different from newly created element + */ + + curlErrCode = Curl_llist_insert_next(llist, llist->head, + &unusedData_case2); + if(curlErrCode == 1) { + fail_unless(llist->head->next->ptr == &unusedData_case2, + "the node next to head is not getting set correctly"); + /* better safe than sorry, check that the tail isn't corrupted */ + fail_unless(llist->tail->ptr != &unusedData_case2, + "the list tail is not getting set correctly"); + } + else { + printf("skipping Curl_llist_insert_next as a non " + "success error code was returned\n"); + } + + } + else { + printf("skipping Curl_llist_insert_next as a non " + "success error code was returned\n"); + } + + /* unit tests for Curl_llist_remove */ + + /** + * case 1: + * list has >1 element, removing head + * @assumptions: + * 1: list size will be decremented by one + * 2: head will be the head->next + * 3: "new" head's previous will be NULL + */ + + head=llist->head; + abort_unless(head, "llist->head is NULL"); + element_next = head->next; + llist_size = Curl_llist_count(llist); + + Curl_llist_remove(llist, llist->head, NULL); + + fail_unless(Curl_llist_count(llist) == (llist_size-1), + "llist size not decremented as expected"); + fail_unless(llist->head == element_next, + "llist new head not modified properly"); + abort_unless(llist->head, "llist->head is NULL"); + fail_unless(llist->head->prev == NULL, + "new head previous not set to null"); + + /** + * case 2: + * removing non head element, with list having >=2 elements + * @setup: + * 1: insert another element to the list to make element >=2 + * @assumptions: + * 1: list size will be decremented by one ; tested + * 2: element->previous->next will be element->next + * 3: element->next->previous will be element->previous + */ + Curl_llist_insert_next(llist, llist->head, &unusedData_case3); + llist_size = Curl_llist_count(llist); + to_remove = llist->head->next; + abort_unless(to_remove, "to_remove is NULL"); + element_next = to_remove->next; + element_prev = to_remove->prev; + Curl_llist_remove(llist, to_remove, NULL); + fail_unless(element_prev->next == element_next, + "element previous->next is not being adjusted"); + abort_unless(element_next, "element_next is NULL"); + fail_unless(element_next->prev == element_prev, + "element next->previous is not being adjusted"); + + /** + * case 3: + * removing the tail with list having >=1 element + * @assumptions + * 1: list size will be decremented by one ;tested + * 2: element->previous->next will be element->next ;tested + * 3: element->next->previous will be element->previous ;tested + * 4: list->tail will be tail->previous + */ + + to_remove = llist->tail; + element_prev = to_remove->prev; + Curl_llist_remove(llist, to_remove, NULL); + fail_unless(llist->tail == element_prev, + "llist tail is not being adjusted when removing tail"); + + /** + * case 4: + * removing head with list having 1 element + * @assumptions: + * 1: list size will be decremented by one ;tested + * 2: list head will be null + * 3: list tail will be null + */ + + to_remove = llist->head; + Curl_llist_remove(llist, to_remove, NULL); + fail_unless(llist->head == NULL, + "llist head is not NULL while the llist is empty"); + fail_unless(llist->tail == NULL, + "llist tail is not NULL while the llist is empty"); + + /* @testing Curl_llist_move(struct curl_llist *, + * struct curl_llist_element *, struct curl_llist *, + * struct curl_llist_element *); + */ + + /** + * @case 1: + * moving head from an llist containg one element to an empty llist + * @assumptions: + * 1: llist size will be 0 + * 2: llist_destination size will be 1 + * 3: llist head will be NULL + * 4: llist_destination head == llist_destination tail != NULL + */ + + /* + * @setup + * add one element to the list + */ + + curlErrCode = Curl_llist_insert_next(llist, llist->head, &unusedData_case1); + /* necessary assertions */ + + abort_unless(curlErrCode == 1, + "Curl_llist_insert_next returned an error, Can't move on with test"); + abort_unless(Curl_llist_count(llist) == 1, + "Number of list elements is not as expected, Aborting"); + abort_unless(Curl_llist_count(llist_destination) == 0, + "Number of list elements is not as expected, Aborting"); + + /*actual testing code*/ + curlErrCode = Curl_llist_move(llist, llist->head, llist_destination, NULL); + abort_unless(curlErrCode == 1, + "Curl_llist_move returned an error, Can't move on with test"); + fail_unless(Curl_llist_count(llist) == 0, + "moving element from llist didn't decrement the size"); + + fail_unless(Curl_llist_count(llist_destination) == 1, + "moving element to llist_destination didn't increment the size"); + + fail_unless(llist->head == NULL, + "llist head not set to null after moving the head"); + + fail_unless(llist_destination->head != NULL, + "llist_destination head set to null after moving an element"); + + fail_unless(llist_destination->tail != NULL, + "llist_destination tail set to null after moving an element"); + + fail_unless(llist_destination->tail == llist_destination->tail, + "llist_destination tail doesn't equal llist_destination head"); + + + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1301.c b/deps/curl-7.51.0/tests/unit/unit1301.c new file mode 100644 index 0000000000..aa86101134 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1301.c @@ -0,0 +1,54 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "strcase.h" + +static CURLcode unit_setup(void) {return CURLE_OK;} +static void unit_stop(void) {} + +UNITTEST_START + +int rc; + +rc = curl_strequal("iii", "III"); +fail_unless(rc != 0, "return code should be zero"); + +rc = curl_strequal("iiia", "III"); +fail_unless(rc == 0, "return code should be zero"); + +rc = curl_strequal("iii", "IIIa"); +fail_unless(rc == 0, "return code should be zero"); + +rc = curl_strequal("iiiA", "IIIa"); +fail_unless(rc != 0, "return code should be non-zero"); + +rc = curl_strnequal("iii", "III", 3); +fail_unless(rc != 0, "return code should be non-zero"); + +rc = curl_strnequal("iiiABC", "IIIcba", 3); +fail_unless(rc != 0, "return code should be non-zero"); + +rc = curl_strnequal("ii", "II", 3); +fail_unless(rc != 0, "return code should be non-zero"); + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1302.c b/deps/curl-7.51.0/tests/unit/unit1302.c new file mode 100644 index 0000000000..8dae5aad11 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1302.c @@ -0,0 +1,161 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "urldata.h" +#include "url.h" /* for Curl_safefree */ +#include "curl_base64.h" +#include "memdebug.h" /* LAST include file */ + +static struct Curl_easy *data; + +static CURLcode unit_setup(void) +{ + data = curl_easy_init(); + if(!data) + return CURLE_OUT_OF_MEMORY; + return CURLE_OK; +} + +static void unit_stop(void) +{ + curl_easy_cleanup(data); +} + +UNITTEST_START + +char *output; +unsigned char *decoded; +size_t size = 0; +unsigned char anychar = 'x'; +CURLcode rc; + +rc = Curl_base64_encode(data, "i", 1, &output, &size); +fail_unless(rc == CURLE_OK, "return code should be CURLE_OK"); +fail_unless(size == 4, "size should be 4"); +verify_memory(output, "aQ==", 4); +Curl_safefree(output); + +rc = Curl_base64_encode(data, "ii", 2, &output, &size); +fail_unless(rc == CURLE_OK, "return code should be CURLE_OK"); +fail_unless(size == 4, "size should be 4"); +verify_memory(output, "aWk=", 4); +Curl_safefree(output); + +rc = Curl_base64_encode(data, "iii", 3, &output, &size); +fail_unless(rc == CURLE_OK, "return code should be CURLE_OK"); +fail_unless(size == 4, "size should be 4"); +verify_memory(output, "aWlp", 4); +Curl_safefree(output); + +rc = Curl_base64_encode(data, "iiii", 4, &output, &size); +fail_unless(rc == CURLE_OK, "return code should be CURLE_OK"); +fail_unless(size == 8, "size should be 8"); +verify_memory(output, "aWlpaQ==", 8); +Curl_safefree(output); + +rc = Curl_base64_encode(data, "\xff\x01\xfe\x02", 4, &output, &size); +fail_unless(rc == CURLE_OK, "return code should be CURLE_OK"); +fail_unless(size == 8, "size should be 8"); +verify_memory(output, "/wH+Ag==", 8); +Curl_safefree(output); + +rc = Curl_base64url_encode(data, "\xff\x01\xfe\x02", 4, &output, &size); +fail_unless(rc == CURLE_OK, "return code should be CURLE_OK"); +fail_unless(size == 8, "size should be 8"); +verify_memory(output, "_wH-Ag==", 8); +Curl_safefree(output); + +rc = Curl_base64url_encode(data, "iiii", 4, &output, &size); +fail_unless(rc == CURLE_OK, "return code should be CURLE_OK"); +fail_unless(size == 8, "size should be 8"); +verify_memory(output, "aWlpaQ==", 8); +Curl_safefree(output); + +/* 0 length makes it do strlen() */ +rc = Curl_base64_encode(data, "iiii", 0, &output, &size); +fail_unless(rc == CURLE_OK, "return code should be CURLE_OK"); +fail_unless(size == 8, "size should be 8"); +verify_memory(output, "aWlpaQ==", 8); +Curl_safefree(output); + +rc = Curl_base64_decode("aWlpaQ==", &decoded, &size); +fail_unless(rc == CURLE_OK, "return code should be CURLE_OK"); +fail_unless(size == 4, "size should be 4"); +verify_memory(decoded, "iiii", 4); +Curl_safefree(decoded); + +rc = Curl_base64_decode("aWlp", &decoded, &size); +fail_unless(rc == CURLE_OK, "return code should be CURLE_OK"); +fail_unless(size == 3, "size should be 3"); +verify_memory(decoded, "iii", 3); +Curl_safefree(decoded); + +rc = Curl_base64_decode("aWk=", &decoded, &size); +fail_unless(rc == CURLE_OK, "return code should be CURLE_OK"); +fail_unless(size == 2, "size should be 2"); +verify_memory(decoded, "ii", 2); +Curl_safefree(decoded); + +rc = Curl_base64_decode("aQ==", &decoded, &size); +fail_unless(rc == CURLE_OK, "return code should be CURLE_OK"); +fail_unless(size == 1, "size should be 1"); +verify_memory(decoded, "i", 2); +Curl_safefree(decoded); + +/* This is illegal input as the data is too short */ +size = 1; /* not zero */ +decoded = &anychar; /* not NULL */ +rc = Curl_base64_decode("aQ", &decoded, &size); +fail_unless(rc == CURLE_BAD_CONTENT_ENCODING, + "return code should be CURLE_BAD_CONTENT_ENCODING"); +fail_unless(size == 0, "size should be 0"); +fail_if(decoded, "returned pointer should be NULL"); + +/* This is illegal input as it contains three padding characters */ +size = 1; /* not zero */ +decoded = &anychar; /* not NULL */ +rc = Curl_base64_decode("a===", &decoded, &size); +fail_unless(rc == CURLE_BAD_CONTENT_ENCODING, + "return code should be CURLE_BAD_CONTENT_ENCODING"); +fail_unless(size == 0, "size should be 0"); +fail_if(decoded, "returned pointer should be NULL"); + +/* This is illegal input as it contains a padding character mid input */ +size = 1; /* not zero */ +decoded = &anychar; /* not NULL */ +rc = Curl_base64_decode("a=Q=", &decoded, &size); +fail_unless(rc == CURLE_BAD_CONTENT_ENCODING, + "return code should be CURLE_BAD_CONTENT_ENCODING"); +fail_unless(size == 0, "size should be 0"); +fail_if(decoded, "returned pointer should be NULL"); + +/* This is garbage input as it contains an illegal base64 character */ +size = 1; /* not zero */ +decoded = &anychar; /* not NULL */ +rc = Curl_base64_decode("a\x1f==", &decoded, &size); +fail_unless(rc == CURLE_BAD_CONTENT_ENCODING, + "return code should be CURLE_BAD_CONTENT_ENCODING"); +fail_unless(size == 0, "size should be 0"); +fail_if(decoded, "returned pointer should be NULL"); + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1303.c b/deps/curl-7.51.0/tests/unit/unit1303.c new file mode 100644 index 0000000000..a4bd598260 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1303.c @@ -0,0 +1,146 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "urldata.h" +#include "connect.h" +#include "memdebug.h" /* LAST include file */ + +static struct Curl_easy *data; + +static CURLcode unit_setup(void) +{ + data = curl_easy_init(); + if(!data) + return CURLE_OUT_OF_MEMORY; + return CURLE_OK; +} + +static void unit_stop(void) +{ + curl_easy_cleanup(data); +} + +/* BASE is just a define to make us fool around with decently large number so + that we aren't zero-based */ +#define BASE 1000000 + +/* macro to set the pretended current time */ +#define NOW(x,y) now.tv_sec = x; now.tv_usec = y +/* macro to set the millisecond based timeouts to use */ +#define TIMEOUTS(x,y) data->set.timeout = x; data->set.connecttimeout = y + +/* + * To test: + * + * 00/10/01/11 timeouts set + * 0/1 during connect + * T various values on the timeouts + * N various values of now + */ + +struct timetest { + int now_s; + int now_us; + int timeout_ms; + int connecttimeout_ms; + bool connecting; + long result; + const char *comment; +}; + +UNITTEST_START + +struct timeval now; +long timeout; +unsigned int i; + +const struct timetest run[] = { + /* both timeouts set, not connecting */ + {BASE + 4, 0, 10000, 8000, FALSE, 6000, "6 seconds should be left"}, + {BASE + 4, 990000, 10000, 8000, FALSE, 5010, "5010 ms should be left"}, + {BASE + 10, 0, 10000, 8000, FALSE, -1, "timeout is -1, expired"}, + {BASE + 12, 0, 10000, 8000, FALSE, -2000, "-2000, overdue 2 seconds"}, + + /* both timeouts set, connecting */ + {BASE + 4, 0, 10000, 8000, TRUE, 4000, "4 seconds should be left"}, + {BASE + 4, 990000, 10000, 8000, TRUE, 3010, "3010 ms should be left"}, + {BASE + 8, 0, 10000, 8000, TRUE, -1, "timeout is -1, expired"}, + {BASE + 10, 0, 10000, 8000, TRUE, -2000, "-2000, overdue 2 seconds"}, + + /* no connect timeout set, not connecting */ + {BASE + 4, 0, 10000, 0, FALSE, 6000, "6 seconds should be left"}, + {BASE + 4, 990000, 10000, 0, FALSE, 5010, "5010 ms should be left"}, + {BASE + 10, 0, 10000, 0, FALSE, -1, "timeout is -1, expired"}, + {BASE + 12, 0, 10000, 0, FALSE, -2000, "-2000, overdue 2 seconds"}, + + /* no connect timeout set, connecting */ + {BASE + 4, 0, 10000, 0, FALSE, 6000, "6 seconds should be left"}, + {BASE + 4, 990000, 10000, 0, FALSE, 5010, "5010 ms should be left"}, + {BASE + 10, 0, 10000, 0, FALSE, -1, "timeout is -1, expired"}, + {BASE + 12, 0, 10000, 0, FALSE, -2000, "-2000, overdue 2 seconds"}, + + /* only connect timeout set, not connecting */ + {BASE + 4, 0, 0, 10000, FALSE, 0, "no timeout active"}, + {BASE + 4, 990000, 0, 10000, FALSE, 0, "no timeout active"}, + {BASE + 10, 0, 0, 10000, FALSE, 0, "no timeout active"}, + {BASE + 12, 0, 0, 10000, FALSE, 0, "no timeout active"}, + + /* only connect timeout set, connecting */ + {BASE + 4, 0, 0, 10000, TRUE, 6000, "6 seconds should be left"}, + {BASE + 4, 990000, 0, 10000, TRUE, 5010, "5010 ms should be left"}, + {BASE + 10, 0, 0, 10000, TRUE, -1, "timeout is -1, expired"}, + {BASE + 12, 0, 0, 10000, TRUE, -2000, "-2000, overdue 2 seconds"}, + + /* no timeout set, not connecting */ + {BASE + 4, 0, 0, 0, FALSE, 0, "no timeout active"}, + {BASE + 4, 990000, 0, 0, FALSE, 0, "no timeout active"}, + {BASE + 10, 0, 0, 0, FALSE, 0, "no timeout active"}, + {BASE + 12, 0, 0, 0, FALSE, 0, "no timeout active"}, + + /* no timeout set, connecting */ + {BASE + 4, 0, 0, 0, TRUE, 296000, "no timeout active"}, + {BASE + 4, 990000, 0, 0, TRUE, 295010, "no timeout active"}, + {BASE + 10, 0, 0, 0, TRUE, 290000, "no timeout active"}, + {BASE + 12, 0, 0, 0, TRUE, 288000, "no timeout active"}, + + /* both timeouts set, connecting, connect timeout the longer one */ + {BASE + 4, 0, 10000, 12000, TRUE, 6000, "6 seconds should be left"}, + +}; + +/* this is the pretended start time of the transfer */ +data->progress.t_startsingle.tv_sec = BASE; +data->progress.t_startsingle.tv_usec = 0; +data->progress.t_startop.tv_sec = BASE; +data->progress.t_startop.tv_usec = 0; + +for(i=0; i < sizeof(run)/sizeof(run[0]); i++) { + NOW(run[i].now_s, run[i].now_us); + TIMEOUTS(run[i].timeout_ms, run[i].connecttimeout_ms); + timeout = Curl_timeleft(data, &now, run[i].connecting); + if(timeout != run[i].result) + fail(run[i].comment); +} + + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1304.c b/deps/curl-7.51.0/tests/unit/unit1304.c new file mode 100644 index 0000000000..11bba390f0 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1304.c @@ -0,0 +1,189 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" +#include "netrc.h" +#include "memdebug.h" /* LAST include file */ + +static char *login; +static char *password; +static char filename[64]; + +static CURLcode unit_setup(void) +{ + password = strdup(""); + login = strdup(""); + if(!password || !login) { + Curl_safefree(password); + Curl_safefree(login); + return CURLE_OUT_OF_MEMORY; + } + return CURLE_OK; +} + +static void unit_stop(void) +{ + Curl_safefree(password); + Curl_safefree(login); +} + +UNITTEST_START + int result; + + static const char* const filename1 = "log/netrc1304"; + memcpy(filename, filename1, strlen(filename1)); + + /* + * Test a non existent host in our netrc file. + */ + result = Curl_parsenetrc("test.example.com", &login, &password, filename); + fail_unless(result == 1, "Host not found should return 1"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(password[0] == 0, "password should not have been changed"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(login[0] == 0, "login should not have been changed"); + + /* + * Test a non existent login in our netrc file. + */ + free(login); + login = strdup("me"); + abort_unless(login != NULL, "returned NULL!"); + result = Curl_parsenetrc("example.com", &login, &password, filename); + fail_unless(result == 0, "Host should be found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(password[0] == 0, "password should not have been changed"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "me", 2) == 0, + "login should not have been changed"); + + /* + * Test a non existent login and host in our netrc file. + */ + free(login); + login = strdup("me"); + abort_unless(login != NULL, "returned NULL!"); + result = Curl_parsenetrc("test.example.com", &login, &password, filename); + fail_unless(result == 1, "Host should be found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(password[0] == 0, "password should not have been changed"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "me", 2) == 0, + "login should not have been changed"); + + /* + * Test a non existent login (substring of an existing one) in our + * netrc file. + */ + free(login); + login = strdup("admi"); + abort_unless(login != NULL, "returned NULL!"); + result = Curl_parsenetrc("example.com", &login, &password, filename); + fail_unless(result == 0, "Host should be found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(password[0] == 0, "password should not have been changed"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "admi", 4) == 0, + "login should not have been changed"); + + /* + * Test a non existent login (superstring of an existing one) + * in our netrc file. + */ + free(login); + login = strdup("adminn"); + abort_unless(login != NULL, "returned NULL!"); + result = Curl_parsenetrc("example.com", &login, &password, filename); + fail_unless(result == 0, "Host should be found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(password[0] == 0, "password should not have been changed"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "adminn", 6) == 0, + "login should not have been changed"); + + /* + * Test for the first existing host in our netrc file + * with login[0] = 0. + */ + free(login); + login = strdup(""); + abort_unless(login != NULL, "returned NULL!"); + result = Curl_parsenetrc("example.com", &login, &password, filename); + fail_unless(result == 0, "Host should have been found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(strncmp(password, "passwd", 6) == 0, + "password should be 'passwd'"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "admin", 5) == 0, "login should be 'admin'"); + + /* + * Test for the first existing host in our netrc file + * with login[0] != 0. + */ + free(password); + password = strdup(""); + abort_unless(password != NULL, "returned NULL!"); + result = Curl_parsenetrc("example.com", &login, &password, filename); + fail_unless(result == 0, "Host should have been found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(strncmp(password, "passwd", 6) == 0, + "password should be 'passwd'"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "admin", 5) == 0, "login should be 'admin'"); + + /* + * Test for the second existing host in our netrc file + * with login[0] = 0. + */ + free(password); + password = strdup(""); + abort_unless(password != NULL, "returned NULL!"); + free(login); + login = strdup(""); + abort_unless(login != NULL, "returned NULL!"); + result = Curl_parsenetrc("curl.example.com", &login, &password, filename); + fail_unless(result == 0, "Host should have been found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(strncmp(password, "none", 4) == 0, + "password should be 'none'"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "none", 4) == 0, "login should be 'none'"); + + /* + * Test for the second existing host in our netrc file + * with login[0] != 0. + */ + free(password); + password = strdup(""); + abort_unless(password != NULL, "returned NULL!"); + result = Curl_parsenetrc("curl.example.com", &login, &password, filename); + fail_unless(result == 0, "Host should have been found"); + abort_unless(password != NULL, "returned NULL!"); + fail_unless(strncmp(password, "none", 4) == 0, + "password should be 'none'"); + abort_unless(login != NULL, "returned NULL!"); + fail_unless(strncmp(login, "none", 4) == 0, "login should be 'none'"); + + /* TODO: + * Test over the size limit password / login! + * Test files with a bad format + */ +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1305.c b/deps/curl-7.51.0/tests/unit/unit1305.c new file mode 100644 index 0000000000..ad270f565d --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1305.c @@ -0,0 +1,139 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif + +#define ENABLE_CURLX_PRINTF +#include "curlx.h" + +#include "hash.h" +#include "hostip.h" + +#include "memdebug.h" /* LAST include file */ + +static struct Curl_easy *data; +static struct curl_hash hp; +static char *data_key; +static struct Curl_dns_entry *data_node; + +static CURLcode unit_setup(void) +{ + int rc; + data = curl_easy_init(); + if(!data) + return CURLE_OUT_OF_MEMORY; + + rc = Curl_mk_dnscache(&hp); + if(rc) { + curl_easy_cleanup(data); + curl_global_cleanup(); + return CURLE_OUT_OF_MEMORY; + } + return CURLE_OK; +} + +static void unit_stop(void) +{ + if(data_node) { + Curl_freeaddrinfo(data_node->addr); + free(data_node); + } + free(data_key); + Curl_hash_destroy(&hp); + + curl_easy_cleanup(data); + curl_global_cleanup(); +} + +static Curl_addrinfo *fake_ai(void) +{ + static Curl_addrinfo *ai; + int ss_size; + + ss_size = sizeof (struct sockaddr_in); + + if((ai = calloc(1, sizeof(Curl_addrinfo))) == NULL) + return NULL; + + if((ai->ai_canonname = strdup("dummy")) == NULL) { + free(ai); + return NULL; + } + + if((ai->ai_addr = calloc(1, ss_size)) == NULL) { + free(ai->ai_canonname); + free(ai); + return NULL; + } + + ai->ai_family = AF_INET; + ai->ai_addrlen = ss_size; + + return ai; +} + +static CURLcode create_node(void) +{ + data_key = aprintf("%s:%d", "dummy", 0); + if(!data_key) + return CURLE_OUT_OF_MEMORY; + + data_node = calloc(1, sizeof(struct Curl_dns_entry)); + if(!data_node) + return CURLE_OUT_OF_MEMORY; + + data_node->addr = fake_ai(); + if(!data_node->addr) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + + +UNITTEST_START + + struct Curl_dns_entry *nodep; + size_t key_len; + + /* Test 1305 exits without adding anything to the hash */ + if(strcmp(arg, "1305") != 0) { + CURLcode rc = create_node(); + abort_unless(rc == CURLE_OK, "data node creation failed"); + key_len = strlen(data_key); + + data_node->inuse = 1; /* hash will hold the reference */ + nodep = Curl_hash_add(&hp, data_key, key_len+1, data_node); + abort_unless(nodep, "insertion into hash failed"); + /* Freeing will now be done by Curl_hash_destroy */ + data_node = NULL; + } + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1307.c b/deps/curl-7.51.0/tests/unit/unit1307.c new file mode 100644 index 0000000000..5764622745 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1307.c @@ -0,0 +1,234 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "curl_fnmatch.h" + +#define MATCH CURL_FNMATCH_MATCH +#define NOMATCH CURL_FNMATCH_NOMATCH +#define RE_ERR CURL_FNMATCH_FAIL + +#define MAX_PATTERN_L 100 +#define MAX_STRING_L 100 + +struct testcase { + char pattern[MAX_PATTERN_L]; + char string[MAX_STRING_L]; + int result; +}; + +static const struct testcase tests[] = { + /* brackets syntax */ + { "\\[", "[", MATCH }, + { "[", "[", RE_ERR }, + { "[]", "[]", RE_ERR }, + { "[][]", "[", MATCH }, + { "[][]", "]", MATCH }, + { "[[]", "[", MATCH }, + { "[[[]", "[", MATCH }, + { "[[[[]", "[", MATCH }, + { "[[[[]", "[", MATCH }, + + { "[][[]", "]", MATCH }, + { "[][[[]", "[", MATCH }, + { "[[]", "]", NOMATCH }, + + { "[a-z]", "a", MATCH }, + { "[a-z]", "A", NOMATCH }, + { "?[a-z]", "?Z", NOMATCH }, + { "[A-Z]", "C", MATCH }, + { "[A-Z]", "c", NOMATCH }, + { "[0-9]", "7", MATCH }, + { "[7-8]", "7", MATCH }, + { "[7-]", "7", MATCH }, + { "[7-]", "-", MATCH }, + { "[7-]", "[", NOMATCH }, + { "[a-bA-F]", "F", MATCH }, + { "[a-bA-B9]", "9", MATCH }, + { "[a-bA-B98]", "8", MATCH }, + { "[a-bA-B98]", "C", NOMATCH }, + { "[a-bA-Z9]", "F", MATCH }, + { "[a-bA-Z9]ero*", "Zero chance.", MATCH }, + { "S[a-][x]opho*", "Saxophone", MATCH }, + { "S[a-][x]opho*", "SaXophone", NOMATCH }, + { "S[a-][x]*.txt", "S-x.txt", MATCH }, + { "[\\a-\\b]", "a", MATCH }, + { "[\\a-\\b]", "b", MATCH }, + { "[?*[][?*[][?*[]", "?*[", MATCH }, + { "[][?*-]", "]", MATCH }, + { "[][?*-]", "[", MATCH }, + { "[][?*-]", "?", MATCH }, + { "[][?*-]", "*", MATCH }, + { "[][?*-]", "-", MATCH }, + { "[]?*-]", "-", MATCH }, + { "?/b/c", "a/b/c", MATCH }, + { "^_{}~", "^_{}~", MATCH }, + { "!#%+,-./01234567889", "!#%+,-./01234567889", MATCH }, + { "PQRSTUVWXYZ]abcdefg", "PQRSTUVWXYZ]abcdefg", MATCH }, + { ":;=@ABCDEFGHIJKLMNO", ":;=@ABCDEFGHIJKLMNO", MATCH }, + + /* negate */ + { "[!a]", "b", MATCH }, + { "[!a]", "a", NOMATCH }, + { "[^a]", "b", MATCH }, + { "[^a]", "a", NOMATCH }, + { "[^a-z0-9A-Z]", "a", NOMATCH }, + { "[^a-z0-9A-Z]", "-", MATCH }, + { "curl[!a-z]lib", "curl lib", MATCH }, + { "curl[! ]lib", "curl lib", NOMATCH }, + { "[! ][ ]", " ", NOMATCH }, + { "[! ][ ]", "a ", MATCH }, + { "*[^a].t?t", "a.txt", NOMATCH }, + { "*[^a].t?t", "ba.txt", NOMATCH }, + { "*[^a].t?t", "ab.txt", MATCH }, + { "[!?*[]", "?", NOMATCH }, + { "[!!]", "!", NOMATCH }, + { "[!!]", "x", MATCH }, + + { "[[:alpha:]]", "a", MATCH }, + { "[[:alpha:]]", "9", NOMATCH }, + { "[[:alnum:]]", "a", MATCH }, + { "[[:alnum:]]", "[", NOMATCH }, + { "[[:alnum:]]", "]", NOMATCH }, + { "[[:alnum:]]", "9", MATCH }, + { "[[:digit:]]", "9", MATCH }, + { "[[:xdigit:]]", "9", MATCH }, + { "[[:xdigit:]]", "F", MATCH }, + { "[[:xdigit:]]", "G", NOMATCH }, + { "[[:upper:]]", "U", MATCH }, + { "[[:upper:]]", "u", NOMATCH }, + { "[[:lower:]]", "l", MATCH }, + { "[[:lower:]]", "L", NOMATCH }, + { "[[:print:]]", "L", MATCH }, + { "[[:print:]]", {'\10'}, NOMATCH }, + { "[[:print:]]", {'\10'}, NOMATCH }, + { "[[:space:]]", " ", MATCH }, + { "[[:space:]]", "x", NOMATCH }, + { "[[:graph:]]", " ", NOMATCH }, + { "[[:graph:]]", "x", MATCH }, + { "[[:blank:]]", {'\t'}, MATCH }, + { "[[:blank:]]", {' '}, MATCH }, + { "[[:blank:]]", {'\r'}, NOMATCH }, + { "[^[:blank:]]", {'\t'}, NOMATCH }, + { "[^[:print:]]", {'\10'}, MATCH }, + { "[[:lower:]][[:lower:]]", "ll", MATCH }, + + { "Curl[[:blank:]];-)", "Curl ;-)", MATCH }, + { "*[[:blank:]]*", " ", MATCH }, + { "*[[:blank:]]*", "", NOMATCH }, + { "*[[:blank:]]*", "hi, im_Pavel", MATCH }, + + /* common using */ + { "filename.dat", "filename.dat", MATCH }, + { "*curl*", "lets use curl!!", MATCH }, + { "filename.txt", "filename.dat", NOMATCH }, + { "*.txt", "text.txt", MATCH }, + { "*.txt", "a.txt", MATCH }, + { "*.txt", ".txt", MATCH }, + { "*.txt", "txt", NOMATCH }, + { "??.txt", "99.txt", MATCH }, + { "??.txt", "a99.txt", NOMATCH }, + { "?.???", "a.txt", MATCH }, + { "*.???", "somefile.dat", MATCH }, + { "*.???", "photo.jpeg", NOMATCH }, + { ".*", ".htaccess", MATCH }, + { ".*", ".", MATCH }, + { ".*", "..", MATCH }, + + /* many stars => one star */ + { "**.txt", "text.txt", MATCH }, + { "***.txt", "t.txt", MATCH }, + { "****.txt", ".txt", MATCH }, + + /* empty string or pattern */ + { "", "", MATCH }, + { "", "hello", NOMATCH }, + { "file", "", NOMATCH }, + { "?", "", NOMATCH }, + { "*", "", MATCH }, + { "x", "", NOMATCH }, + + /* backslash */ + { "\\", "\\", RE_ERR }, + { "\\\\", "\\", MATCH }, + { "\\\\", "\\\\", NOMATCH }, + { "\\?", "?", MATCH }, + { "\\*", "*", MATCH }, + { "?.txt", "?.txt", MATCH }, + { "*.txt", "*.txt", MATCH }, + { "\\?.txt", "?.txt", MATCH }, + { "\\*.txt", "*.txt", MATCH }, + { "\\?.txt", "x.txt", NOMATCH }, + { "\\*.txt", "x.txt", NOMATCH }, + { "\\*\\\\.txt", "*\\.txt", MATCH }, + { "*\\**\\?*\\\\*", "cc*cc?cc\\cc*cc", MATCH }, + { "*\\**\\?*\\\\*", "cc*cc?cccc", NOMATCH }, + { "*\\**\\?*\\\\*", "cc*cc?cc\\cc*cc", MATCH }, + { "*\\?*\\**", "cc?c*c", MATCH }, + { "*\\?*\\**curl*", "cc?c*curl", MATCH }, + { "*\\?*\\**", "cc?cc", NOMATCH }, + { "\\\"\\$\\&\\'\\(\\)", "\"$&'()", MATCH }, + { "\\*\\?\\[\\\\\\`\\|", "*?[\\`|", MATCH }, + { "[\\a\\b]c", "ac", MATCH }, + { "[\\a\\b]c", "bc", MATCH }, + { "[\\a\\b]d", "bc", NOMATCH }, + { "[a-bA-B\\?]", "?", MATCH }, + { "cu[a-ab-b\\r]l", "curl", MATCH }, + { "[\\a-z]", "c", MATCH }, + + { "?*?*?.*?*", "abc.c", MATCH }, + { "?*?*?.*?*", "abcc", NOMATCH }, + { "?*?*?.*?*", "abc.", NOMATCH }, + { "?*?*?.*?*", "abc.c++", MATCH }, + { "?*?*?.*?*", "abcdef.c++", MATCH }, + { "?*?*?.?", "abcdef.c", MATCH }, + { "?*?*?.?", "abcdef.cd", NOMATCH }, + + { "Lindmätarv", "Lindmätarv", MATCH }, + + { "", "", MATCH } +}; + +static CURLcode unit_setup(void) +{ + return CURLE_OK; +} + +static void unit_stop(void) +{ +} + +UNITTEST_START + + int testnum = sizeof(tests) / sizeof(struct testcase); + int i, rc; + + for(i = 0; i < testnum; i++) { + rc = Curl_fnmatch(NULL, tests[i].pattern, tests[i].string); + if(rc != tests[i].result) { + printf("Curl_fnmatch(\"%s\", \"%s\") should return %d (returns %d)\n", + tests[i].pattern, tests[i].string, tests[i].result, rc); + fail("pattern mismatch"); + } + } + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1308.c b/deps/curl-7.51.0/tests/unit/unit1308.c new file mode 100644 index 0000000000..968bcff212 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1308.c @@ -0,0 +1,95 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include + +static CURLcode unit_setup(void) +{ + return CURLE_OK; +} + +static void unit_stop(void) +{ + +} + +static size_t print_httppost_callback(void *arg, const char *buf, size_t len) +{ + fwrite(buf, len, 1, stdout); + (*(size_t *) arg) += len; + return len; +} + +UNITTEST_START + int rc; + struct curl_httppost* post = NULL; + struct curl_httppost* last = NULL; + size_t total_size = 0; + char buffer[] = "test buffer"; + + rc = curl_formadd(&post, &last, CURLFORM_COPYNAME, "name", + CURLFORM_COPYCONTENTS, "content", CURLFORM_END); + + fail_unless(rc == 0, "curl_formadd returned error"); + + /* after the first curl_formadd when there's a single entry, both pointers + should point to the same struct */ + fail_unless(post == last, "post and last weren't the same"); + + rc = curl_formadd(&post, &last, CURLFORM_COPYNAME, "htmlcode", + CURLFORM_COPYCONTENTS, "", + CURLFORM_CONTENTTYPE, "text/html", CURLFORM_END); + + fail_unless(rc == 0, "curl_formadd returned error"); + + rc = curl_formadd(&post, &last, CURLFORM_COPYNAME, "name_for_ptrcontent", + CURLFORM_PTRCONTENTS, buffer, CURLFORM_END); + + fail_unless(rc == 0, "curl_formadd returned error"); + + rc = curl_formget(post, &total_size, print_httppost_callback); + + fail_unless(rc == 0, "curl_formget returned error"); + + fail_unless(total_size == 486, "curl_formget got wrong size back"); + + curl_formfree(post); + + /* start a new formpost with a file upload and formget */ + post = last = NULL; + + rc = curl_formadd(&post, &last, + CURLFORM_PTRNAME, "name of file field", + CURLFORM_FILE, "log/test-1308", + CURLFORM_FILENAME, "custom named file", + CURLFORM_END); + + fail_unless(rc == 0, "curl_formadd returned error"); + + rc = curl_formget(post, &total_size, print_httppost_callback); + fail_unless(rc == 0, "curl_formget returned error"); + fail_unless(total_size == 847, "curl_formget got wrong size back"); + + curl_formfree(post); + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1309.c b/deps/curl-7.51.0/tests/unit/unit1309.c new file mode 100644 index 0000000000..3cf6eefbdc --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1309.c @@ -0,0 +1,110 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "splay.h" + + +static CURLcode unit_setup(void) +{ + return CURLE_OK; +} + +static void unit_stop(void) +{ + +} + +static void splayprint(struct Curl_tree * t, int d, char output) +{ + struct Curl_tree *node; + int i; + int count; + if(t == NULL) + return; + + splayprint(t->larger, d+1, output); + for(i=0; ikey.tv_sec, + (long)t->key.tv_usec, i); + } + + for(count=0, node = t->same; node; node = node->same, count++) + ; + + if(output) { + if(count) + printf(" [%d more]\n", count); + else + printf("\n"); + } + + splayprint(t->smaller, d+1, output); +} + +UNITTEST_START + +/* number of nodes to add to the splay tree */ +#define NUM_NODES 50 + + struct Curl_tree *root; + struct Curl_tree nodes[NUM_NODES]; + int rc; + int i; + root = NULL; /* the empty tree */ + + for(i = 0; i < NUM_NODES; i++) { + struct timeval key; + + key.tv_sec = 0; + key.tv_usec = (541*i)%1023; + + nodes[i].payload = (void *)key.tv_usec; /* for simplicity */ + root = Curl_splayinsert(key, root, &nodes[i]); + } + + puts("Result:"); + splayprint(root, 0, 1); + + for(i = 0; i < NUM_NODES; i++) { + int rem = (i+7)%NUM_NODES; + printf("Tree look:\n"); + splayprint(root, 0, 1); + printf("remove pointer %d, payload %ld\n", rem, + (long)(nodes[rem].payload)); + rc = Curl_splayremovebyaddr(root, &nodes[rem], &root); + if(rc) { + /* failed! */ + printf("remove %d failed!\n", rem); + fail("remove"); + } + } + +UNITTEST_STOP + + + + diff --git a/deps/curl-7.51.0/tests/unit/unit1330.c b/deps/curl-7.51.0/tests/unit/unit1330.c new file mode 100644 index 0000000000..e6431bbba6 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1330.c @@ -0,0 +1,41 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "memdebug.h" + + +static CURLcode unit_setup(void) +{ + return CURLE_OK; +} + +static void unit_stop(void) +{ +} + +UNITTEST_START + +char *ptr = malloc(1330); +Curl_safefree(ptr); + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1394.c b/deps/curl-7.51.0/tests/unit/unit1394.c new file mode 100644 index 0000000000..667991d1ee --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1394.c @@ -0,0 +1,126 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "tool_getparam.h" + +#include +#include +#include + +#include "memdebug.h" /* LAST include file */ + +static CURLcode unit_setup(void) +{ + return CURLE_OK; +} + +static void unit_stop(void) +{ + +} + +UNITTEST_START + + const char *values[] = { + /* -E parameter */ /* exp. cert name */ /* exp. passphrase */ + "foo:bar:baz", "foo", "bar:baz", + "foo\\:bar:baz", "foo:bar", "baz", + "foo\\\\:bar:baz", "foo\\", "bar:baz", + "foo:bar\\:baz", "foo", "bar\\:baz", + "foo:bar\\\\:baz", "foo", "bar\\\\:baz", + "foo\\bar\\baz", "foo\\bar\\baz", NULL, + "foo\\\\bar\\\\baz", "foo\\bar\\baz", NULL, + "foo\\", "foo\\", NULL, + "foo\\\\", "foo\\", NULL, + "foo:bar\\", "foo", "bar\\", + "foo:bar\\\\", "foo", "bar\\\\", + "foo:bar:", "foo", "bar:", + "foo\\::bar\\:", "foo:", "bar\\:", +#ifdef WIN32 + "c:\\foo:bar:baz", "c:\\foo", "bar:baz", + "c:\\foo\\:bar:baz", "c:\\foo:bar", "baz", + "c:\\foo\\\\:bar:baz", "c:\\foo\\", "bar:baz", + "c:\\foo:bar\\:baz", "c:\\foo", "bar\\:baz", + "c:\\foo:bar\\\\:baz", "c:\\foo", "bar\\\\:baz", + "c:\\foo\\bar\\baz", "c:\\foo\\bar\\baz", NULL, + "c:\\foo\\\\bar\\\\baz", "c:\\foo\\bar\\baz", NULL, + "c:\\foo\\", "c:\\foo\\", NULL, + "c:\\foo\\\\", "c:\\foo\\", NULL, + "c:\\foo:bar\\", "c:\\foo", "bar\\", + "c:\\foo:bar\\\\", "c:\\foo", "bar\\\\", + "c:\\foo:bar:", "c:\\foo", "bar:", + "c:\\foo\\::bar\\:", "c:\\foo:", "bar\\:", +#endif + NULL, NULL, NULL, + }; + const char **p; + char *certname, *passphrase; + for(p = values; *p; p += 3) { + parse_cert_parameter(p[0], &certname, &passphrase); + if(p[1]) { + if(certname) { + if(strcmp(p[1], certname)) { + printf("expected certname '%s' but got '%s' " + "for -E param '%s'\n", p[1], certname, p[0]); + fail("assertion failure"); + } + } + else { + printf("expected certname '%s' but got NULL " + "for -E param '%s'\n", p[1], p[0]); + fail("assertion failure"); + } + } + else { + if(certname) { + printf("expected certname NULL but got '%s' " + "for -E param '%s'\n", certname, p[0]); + fail("assertion failure"); + } + } + if(p[2]) { + if(passphrase) { + if(strcmp(p[2], passphrase)) { + printf("expected passphrase '%s' but got '%s'" + "for -E param '%s'\n", p[2], passphrase, p[0]); + fail("assertion failure"); + } + } + else { + printf("expected passphrase '%s' but got NULL " + "for -E param '%s'\n", p[2], p[0]); + fail("assertion failure"); + } + } + else { + if(passphrase) { + printf("expected passphrase NULL but got '%s' " + "for -E param '%s'\n", passphrase, p[0]); + fail("assertion failure"); + } + } + if(certname) free(certname); + if(passphrase) free(passphrase); + } + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1395.c b/deps/curl-7.51.0/tests/unit/unit1395.c new file mode 100644 index 0000000000..13f464134f --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1395.c @@ -0,0 +1,87 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "dotdot.h" + +#include "memdebug.h" + +static CURLcode unit_setup(void) +{ + return CURLE_OK; +} + +static void unit_stop(void) +{ + +} + +struct dotdot { + const char *input; + const char *output; +}; + +UNITTEST_START + + unsigned int i; + int fails=0; + const struct dotdot pairs[] = { + { "/a/b/c/./../../g", "/a/g" }, + { "mid/content=5/../6", "mid/6" }, + { "/hello/../moo", "/moo" }, + { "/1/../1", "/1" }, + { "/1/./1", "/1/1" }, + { "/1/..", "/" }, + { "/1/.", "/1/" }, + { "/1/./..", "/" }, + { "/1/./../2", "/2" }, + { "/hello/1/./../2", "/hello/2" }, + { "test/this", "test/this" }, + { "test/this/../now", "test/now" }, + { "/1../moo../foo", "/1../moo../foo"}, + { "/../../moo", "/moo"}, + { "/../../moo?andnot/../yay", "/moo?andnot/../yay"}, + { "/123?foo=/./&bar=/../", "/123?foo=/./&bar=/../"}, + { "/../moo/..?what", "/?what" }, + { "/", "/" }, + { "", "" }, + { "/.../", "/.../" }, + }; + + for(i=0; i < sizeof(pairs)/sizeof(pairs[0]); i++) { + char *out = Curl_dedotdotify((char *)pairs[i].input); + abort_unless(out != NULL, "returned NULL!"); + + if(strcmp(out, pairs[i].output)) { + fprintf(stderr, "Test %d: '%s' gave '%s' instead of '%s'\n", + i, pairs[i].input, out, pairs[i].output); + fail("Test case output mismatched"); + fails++; + } + else + fprintf(stderr, "Test %d: OK\n", i); + free(out); + } + + fail_if(fails, "output mismatched"); + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1396.c b/deps/curl-7.51.0/tests/unit/unit1396.c new file mode 100644 index 0000000000..84a5162dd8 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1396.c @@ -0,0 +1,111 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +CURL *hnd; + +static CURLcode unit_setup(void) +{ + return CURLE_OK; +} + +static void unit_stop(void) +{ + if(hnd) + curl_easy_cleanup(hnd); +} + +struct test { + const char *in; + int inlen; + const char *out; + int outlen; +}; + +UNITTEST_START +{ + /* unescape, this => that */ + const struct test list1[]={ + {"%61", 3, "a", 1}, + {"%61a", 4, "aa", 2}, + {"%61b", 4, "ab", 2}, + {"%6 1", 4, "%6 1", 4}, + {"%61", 1, "%", 1}, + {"%61", 2, "%6", 2}, + {"%6%a", 4, "%6%a", 4}, + {"%6a", 0, "j", 1}, + {"%FF", 0, "\xff", 1}, + {"%FF%00%ff", 9, "\xff\x00\xff", 3}, + {"%-2", 0, "%-2", 3}, + {"%FG", 0, "%FG", 3}, + {NULL, 0, NULL, 0} /* end of list marker */ + }; + /* escape, this => that */ + const struct test list2[]={ + {"a", 1, "a", 1}, + {"/", 1, "%2F", 3}, + {"a=b", 3, "a%3Db", 5}, + {"a=b", 0, "a%3Db", 5}, + {"a=b", 1, "a", 1}, + {"a=b", 2, "a%3D", 4}, + {"1/./0", 5, "1%2F.%2F0", 9}, + {"-._~!#%&", 0, "-._~%21%23%25%26", 16}, + {"a", 2, "a%00", 4}, + {"a\xff\x01g", 4, "a%FF%01g", 8}, + {NULL, 0, NULL, 0} /* end of list marker */ + }; + int i; + + hnd = curl_easy_init(); + abort_unless(hnd != NULL, "returned NULL!"); + for(i=0; list1[i].in; i++) { + int outlen; + char *out = curl_easy_unescape(hnd, + list1[i].in, list1[i].inlen, + &outlen); + + abort_unless(out != NULL, "returned NULL!"); + fail_unless(outlen == list1[i].outlen, "wrong output length returned"); + fail_unless(!memcmp(out, list1[i].out, list1[i].outlen), + "bad output data returned"); + + printf("curl_easy_unescape test %d DONE\n", i); + + curl_free(out); + } + + for(i=0; list2[i].in; i++) { + int outlen; + char *out = curl_easy_escape(hnd, list2[i].in, list2[i].inlen); + abort_unless(out != NULL, "returned NULL!"); + + outlen = (int)strlen(out); + fail_unless(outlen == list2[i].outlen, "wrong output length returned"); + fail_unless(!memcmp(out, list2[i].out, list2[i].outlen), + "bad output data returned"); + + printf("curl_easy_escape test %d DONE (%s)\n", i, out); + + curl_free(out); + } +} +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1397.c b/deps/curl-7.51.0/tests/unit/unit1397.c new file mode 100644 index 0000000000..539433ca03 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1397.c @@ -0,0 +1,79 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "hostcheck.h" /* from the lib dir */ + +static CURLcode unit_setup(void) +{ + return CURLE_OK; +} + +static void unit_stop(void) +{ + /* done before shutting down and exiting */ +} + +UNITTEST_START + +/* only these backends define the tested functions */ +#if defined(USE_OPENSSL) || defined(USE_AXTLS) || defined(USE_GSKIT) + + /* here you start doing things and checking that the results are good */ + +fail_unless(Curl_cert_hostcheck("www.example.com", "www.example.com"), + "good 1"); +fail_unless(Curl_cert_hostcheck("*.example.com", "www.example.com"), + "good 2"); +fail_unless(Curl_cert_hostcheck("xxx*.example.com", "xxxwww.example.com"), + "good 3"); +fail_unless(Curl_cert_hostcheck("f*.example.com", "foo.example.com"), + "good 4"); +fail_unless(Curl_cert_hostcheck("192.168.0.0", "192.168.0.0"), + "good 5"); + +fail_if(Curl_cert_hostcheck("xxx.example.com", "www.example.com"), "bad 1"); +fail_if(Curl_cert_hostcheck("*", "www.example.com"), "bad 2"); +fail_if(Curl_cert_hostcheck("*.*.com", "www.example.com"), "bad 3"); +fail_if(Curl_cert_hostcheck("*.example.com", "baa.foo.example.com"), "bad 4"); +fail_if(Curl_cert_hostcheck("f*.example.com", "baa.example.com"), "bad 5"); +fail_if(Curl_cert_hostcheck("*.com", "example.com"), "bad 6"); +fail_if(Curl_cert_hostcheck("*fail.com", "example.com"), "bad 7"); +fail_if(Curl_cert_hostcheck("*.example.", "www.example."), "bad 8"); +fail_if(Curl_cert_hostcheck("*.example.", "www.example"), "bad 9"); +fail_if(Curl_cert_hostcheck("", "www"), "bad 10"); +fail_if(Curl_cert_hostcheck("*", "www"), "bad 11"); +fail_if(Curl_cert_hostcheck("*.168.0.0", "192.168.0.0"), "bad 12"); +fail_if(Curl_cert_hostcheck("www.example.com", "192.168.0.0"), "bad 13"); + +#ifdef ENABLE_IPV6 +fail_if(Curl_cert_hostcheck("*::3285:a9ff:fe46:b619", + "fe80::3285:a9ff:fe46:b619"), "bad 14"); +fail_unless(Curl_cert_hostcheck("fe80::3285:a9ff:fe46:b619", + "fe80::3285:a9ff:fe46:b619"), "good 6"); +#endif + +#endif + + /* you end the test code like this: */ + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1398.c b/deps/curl-7.51.0/tests/unit/unit1398.c new file mode 100644 index 0000000000..9491c46c58 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1398.c @@ -0,0 +1,91 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "curl/mprintf.h" + +static CURLcode unit_setup(void) {return CURLE_OK;} +static void unit_stop(void) {} + +UNITTEST_START + +int rc; +char buf[3] = {'b', 'u', 'g'}; +const char *str="bug"; +int width = 3; +char output[24]; + +/*#define curl_msnprintf snprintf */ + +/* without a trailing zero */ +rc = curl_msnprintf(output, 4, "%.*s", width, buf); +fail_unless(rc == 3, "return code should be 3"); +fail_unless(!strcmp(output, "bug"), "wrong output"); + +/* with a trailing zero */ +rc = curl_msnprintf(output, 4, "%.*s", width, str); +fail_unless(rc == 3, "return code should be 3"); +fail_unless(!strcmp(output, "bug"), "wrong output"); + +width = 2; +/* one byte less */ +rc = curl_msnprintf(output, 4, "%.*s", width, buf); +fail_unless(rc == 2, "return code should be 2"); +fail_unless(!strcmp(output, "bu"), "wrong output"); + +/* string with larger precision */ +rc = curl_msnprintf(output, 8, "%.8s", str); +fail_unless(rc == 3, "return code should be 3"); +fail_unless(!strcmp(output, "bug"), "wrong output"); + +/* longer string with precision */ +rc = curl_msnprintf(output, 8, "%.3s", "0123456789"); +fail_unless(rc == 3, "return code should be 3"); +fail_unless(!strcmp(output, "012"), "wrong output"); + +/* negative width */ +rc = curl_msnprintf(output, 8, "%-8s", str); +fail_unless(rc == 8, "return code should be 8"); +fail_unless(!strcmp(output, "bug "), "wrong output"); + +/* larger width that string length */ +rc = curl_msnprintf(output, 8, "%8s", str); +fail_unless(rc == 8, "return code should be 8"); +fail_unless(!strcmp(output, " bu"), "wrong output"); + +/* output a number in a limited output */ +rc = curl_msnprintf(output, 4, "%d", 10240); +/* TODO: this should return 5 to be POSIX/snprintf compliant! */ +fail_unless(rc == 4, "return code should be 4"); +fail_unless(!strcmp(output, "102"), "wrong output"); + +/* padded strings */ +rc = curl_msnprintf(output, 16, "%8s%8s", str, str); +fail_unless(rc == 16, "return code should be 16"); +fail_unless(!strcmp(output, " bug bu"), "wrong output"); + +/* padded numbers */ +rc = curl_msnprintf(output, 16, "%8d%8d", 1234, 5678); +fail_unless(rc == 16, "return code should be 16"); +fail_unless(!strcmp(output, " 1234 567"), "wrong output"); + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1600.c b/deps/curl-7.51.0/tests/unit/unit1600.c new file mode 100644 index 0000000000..f0f9cc1f4c --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1600.c @@ -0,0 +1,65 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "urldata.h" +#include "curl_ntlm_core.h" + +CURL *easy; + +static CURLcode unit_setup(void) +{ + easy = curl_easy_init(); + return CURLE_OK; +} + +static void unit_stop(void) +{ + curl_easy_cleanup(easy); +} + +UNITTEST_START + +#if defined(USE_NTLM) && (!defined(USE_WINDOWS_SSPI) || \ + defined(USE_WIN32_CRYPTO)) + unsigned char output[21]; + unsigned char *testp = output; + Curl_ntlm_core_mk_nt_hash(easy, "1", output); + + verify_memory(testp, + "\x69\x94\x3c\x5e\x63\xb4\xd2\xc1\x04\xdb" + "\xbc\xc1\x51\x38\xb7\x2b\x00\x00\x00\x00\x00", 21); + + Curl_ntlm_core_mk_nt_hash(easy, "hello-you-fool", output); + + verify_memory(testp, + "\x39\xaf\x87\xa6\x75\x0a\x7a\x00\xba\xa0" + "\xd3\x4f\x04\x9e\xc1\xd0\x00\x00\x00\x00\x00", 21); + +/* !checksrc! disable LONGLINE 2 */ + Curl_ntlm_core_mk_nt_hash(easy, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", output); + + verify_memory(testp, + "\x36\x9d\xae\x06\x84\x7e\xe1\xc1\x4a\x94\x39\xea\x6f\x44\x8c\x65\x00\x00\x00\x00\x00", 21); +#endif + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1601.c b/deps/curl-7.51.0/tests/unit/unit1601.c new file mode 100644 index 0000000000..a6120e1c26 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1601.c @@ -0,0 +1,54 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "curl_md5.h" + +static CURLcode unit_setup(void) +{ + return CURLE_OK; +} + +static void unit_stop(void) +{ + +} + +UNITTEST_START + +#ifndef CURL_DISABLE_CRYPTO_AUTH + unsigned char output[16]; + unsigned char *testp = output; + Curl_md5it(output, (const unsigned char *)"1"); + +/* !checksrc! disable LONGLINE 2 */ + verify_memory(testp, + "\xc4\xca\x42\x38\xa0\xb9\x23\x82\x0d\xcc\x50\x9a\x6f\x75\x84\x9b", 16); + + Curl_md5it(output, (const unsigned char *)"hello-you-fool"); + + verify_memory(testp, + "\x88\x67\x0b\x6d\x5d\x74\x2f\xad\xa5\xcd\xf9\xb6\x82\x87\x5f\x22", 16); +#endif + + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1602.c b/deps/curl-7.51.0/tests/unit/unit1602.c new file mode 100644 index 0000000000..c67c0a5554 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1602.c @@ -0,0 +1,78 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#define ENABLE_CURLX_PRINTF +#include "curlx.h" + +#include "hash.h" + +#include "memdebug.h" /* LAST include file */ + +static struct curl_hash hash_static; + +static void mydtor(void *p) +{ + int *ptr = (int*)p; + free(ptr); +} + +static CURLcode unit_setup(void) +{ + return Curl_hash_init(&hash_static, 7, Curl_hash_str, + Curl_str_key_compare, mydtor); +} + +static void unit_stop(void) +{ + Curl_hash_destroy(&hash_static); +} + +UNITTEST_START + int *value; + int *value2; + int *nodep; + size_t klen = sizeof(int); + + int key = 20; + int key2 = 25; + + + value = malloc(sizeof(int)); + abort_unless(value != NULL, "Out of memory"); + *value = 199; + nodep = Curl_hash_add(&hash_static, &key, klen, value); + if(!nodep) + free(value); + abort_unless(nodep, "insertion into hash failed"); + Curl_hash_clean(&hash_static); + + /* Attempt to add another key/value pair */ + value2 = malloc(sizeof(int)); + abort_unless(value2 != NULL, "Out of memory"); + *value2 = 204; + nodep = Curl_hash_add(&hash_static, &key2, klen, value2); + if(!nodep) + free(value2); + abort_unless(nodep, "insertion into hash failed"); + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1603.c b/deps/curl-7.51.0/tests/unit/unit1603.c new file mode 100644 index 0000000000..c20b20b0e7 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1603.c @@ -0,0 +1,150 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2015 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#define ENABLE_CURLX_PRINTF +#include "curlx.h" + +#include "hash.h" + +#include "memdebug.h" /* LAST include file */ + +static struct curl_hash hash_static; +static const int slots = 3; + +static void mydtor(void *p) +{ + /* Data are statically allocated */ + (void)p; /* unused */ +} + +static CURLcode unit_setup(void) +{ + return Curl_hash_init(&hash_static, slots, Curl_hash_str, + Curl_str_key_compare, mydtor); +} + +static void unit_stop(void) +{ + Curl_hash_destroy(&hash_static); +} + +UNITTEST_START + char key1[] = "key1"; + char key2[] = "key2b"; + char key3[] = "key3"; + char key4[] = "key4"; + char notakey[] = "notakey"; + char *nodep; + int rc; + + /* Ensure the key hashes are as expected in order to test both hash + collisions and a full table. Unfortunately, the hashes can vary + between architectures. */ + if(Curl_hash_str(key1, strlen(key1), slots) != 1 || + Curl_hash_str(key2, strlen(key2), slots) != 0 || + Curl_hash_str(key3, strlen(key3), slots) != 2 || + Curl_hash_str(key4, strlen(key4), slots) != 1) + fprintf(stderr, "Warning: hashes are not computed as expected on this " + "architecture; test coverage will be less comprehensive\n"); + + nodep = Curl_hash_add(&hash_static, &key1, strlen(key1), &key1); + fail_unless(nodep, "insertion into hash failed"); + nodep = Curl_hash_pick(&hash_static, &key1, strlen(key1)); + fail_unless(nodep == key1, "hash retrieval failed"); + + nodep = Curl_hash_add(&hash_static, &key2, strlen(key2), &key2); + fail_unless(nodep, "insertion into hash failed"); + nodep = Curl_hash_pick(&hash_static, &key2, strlen(key2)); + fail_unless(nodep == key2, "hash retrieval failed"); + + nodep = Curl_hash_add(&hash_static, &key3, strlen(key3), &key3); + fail_unless(nodep, "insertion into hash failed"); + nodep = Curl_hash_pick(&hash_static, &key3, strlen(key3)); + fail_unless(nodep == key3, "hash retrieval failed"); + + /* The fourth element exceeds the number of slots & collides */ + nodep = Curl_hash_add(&hash_static, &key4, strlen(key4), &key4); + fail_unless(nodep, "insertion into hash failed"); + nodep = Curl_hash_pick(&hash_static, &key4, strlen(key4)); + fail_unless(nodep == key4, "hash retrieval failed"); + + /* Make sure all elements are still accessible */ + nodep = Curl_hash_pick(&hash_static, &key1, strlen(key1)); + fail_unless(nodep == key1, "hash retrieval failed"); + nodep = Curl_hash_pick(&hash_static, &key2, strlen(key2)); + fail_unless(nodep == key2, "hash retrieval failed"); + nodep = Curl_hash_pick(&hash_static, &key3, strlen(key3)); + fail_unless(nodep == key3, "hash retrieval failed"); + nodep = Curl_hash_pick(&hash_static, &key4, strlen(key4)); + fail_unless(nodep == key4, "hash retrieval failed"); + + /* Delete the second of two entries in a bucket */ + rc = Curl_hash_delete(&hash_static, &key4, strlen(key4)); + fail_unless(rc == 0, "hash delete failed"); + nodep = Curl_hash_pick(&hash_static, &key1, strlen(key1)); + fail_unless(nodep == key1, "hash retrieval failed"); + nodep = Curl_hash_pick(&hash_static, &key4, strlen(key4)); + fail_unless(!nodep, "hash retrieval should have failed"); + + /* Insert that deleted node again */ + nodep = Curl_hash_add(&hash_static, &key4, strlen(key4), &key4); + fail_unless(nodep, "insertion into hash failed"); + nodep = Curl_hash_pick(&hash_static, &key4, strlen(key4)); + fail_unless(nodep == key4, "hash retrieval failed"); + + /* Delete the first of two entries in a bucket */ + rc = Curl_hash_delete(&hash_static, &key1, strlen(key1)); + fail_unless(rc == 0, "hash delete failed"); + nodep = Curl_hash_pick(&hash_static, &key1, strlen(key1)); + fail_unless(!nodep, "hash retrieval should have failed"); + nodep = Curl_hash_pick(&hash_static, &key4, strlen(key4)); + fail_unless(nodep == key4, "hash retrieval failed"); + + /* Delete the remaining one of two entries in a bucket */ + rc = Curl_hash_delete(&hash_static, &key4, strlen(key4)); + fail_unless(rc == 0, "hash delete failed"); + nodep = Curl_hash_pick(&hash_static, &key1, strlen(key1)); + fail_unless(!nodep, "hash retrieval should have failed"); + nodep = Curl_hash_pick(&hash_static, &key4, strlen(key4)); + fail_unless(!nodep, "hash retrieval should have failed"); + + /* Delete an already deleted node */ + rc = Curl_hash_delete(&hash_static, &key4, strlen(key4)); + fail_unless(rc, "hash delete should have failed"); + + /* Replace an existing node */ + nodep = Curl_hash_add(&hash_static, &key1, strlen(key1), ¬akey); + fail_unless(nodep, "insertion into hash failed"); + nodep = Curl_hash_pick(&hash_static, &key1, strlen(key1)); + fail_unless(nodep == notakey, "hash retrieval failed"); + + /* Make sure all remaining elements are still accessible */ + nodep = Curl_hash_pick(&hash_static, &key2, strlen(key2)); + fail_unless(nodep == key2, "hash retrieval failed"); + nodep = Curl_hash_pick(&hash_static, &key3, strlen(key3)); + fail_unless(nodep == key3, "hash retrieval failed"); + + /* Clean up */ + Curl_hash_clean(&hash_static); + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1604.c b/deps/curl-7.51.0/tests/unit/unit1604.c new file mode 100644 index 0000000000..242be0005a --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1604.c @@ -0,0 +1,346 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "tool_cfgable.h" +#include "tool_doswin.h" + +#include +#include +#include + +#include "memdebug.h" /* LAST include file */ + +static CURLcode unit_setup(void) +{ + return CURLE_OK; +} + +static void unit_stop(void) +{ + +} + +#if defined(MSDOS) || defined(WIN32) + +static char *getflagstr(int flags) { + char *buf = malloc(256); + fail_unless(buf, "out of memory"); + snprintf(buf, 256, "%s,%s,%s,%s", + ((flags & SANITIZE_ALLOW_COLONS) ? "SANITIZE_ALLOW_COLONS" : ""), + ((flags & SANITIZE_ALLOW_PATH) ? "SANITIZE_ALLOW_PATH" : ""), + ((flags & SANITIZE_ALLOW_RESERVED) ? "SANITIZE_ALLOW_RESERVED" : ""), + ((flags & SANITIZE_ALLOW_TRUNCATE) ? "SANITIZE_ALLOW_TRUNCATE" : "")); + return buf; +} + +static char *getcurlcodestr(int cc) { + char *buf = malloc(256); + fail_unless(buf, "out of memory"); + snprintf(buf, 256, "%s (%d)", + (cc == SANITIZE_ERR_OK ? "SANITIZE_ERR_OK" : + cc == SANITIZE_ERR_BAD_ARGUMENT ? "SANITIZE_ERR_BAD_ARGUMENT" : + cc == SANITIZE_ERR_INVALID_PATH ? "SANITIZE_ERR_INVALID_PATH" : + cc == SANITIZE_ERR_OUT_OF_MEMORY ? "SANITIZE_ERR_OUT_OF_MEMORY" : + "unexpected error code - add name"), + cc); + return buf; +} + +struct data { + const char *input; + int flags; + const char *expected_output; + CURLcode expected_result; +}; + +UNITTEST_START + +{ /* START sanitize_file_name */ + struct data data[] = { + { "", 0, + "", SANITIZE_ERR_OK + }, + { "normal filename", 0, + "normal filename", SANITIZE_ERR_OK + }, + { "control\tchar", 0, + "control_char", SANITIZE_ERR_OK + }, + { "banned*char", 0, + "banned_char", SANITIZE_ERR_OK + }, + { "f:foo", 0, + "f_foo", SANITIZE_ERR_OK + }, + { "f:foo", SANITIZE_ALLOW_COLONS, + "f:foo", SANITIZE_ERR_OK + }, + { "f:foo", SANITIZE_ALLOW_PATH, + "f:foo", SANITIZE_ERR_OK + }, + { "f:\\foo", 0, + "f__foo", SANITIZE_ERR_OK + }, + { "f:\\foo", SANITIZE_ALLOW_PATH, + "f:\\foo", SANITIZE_ERR_OK + }, + { "f:/foo", 0, + "f__foo", SANITIZE_ERR_OK + }, + { "f:/foo", SANITIZE_ALLOW_PATH, + "f:/foo", SANITIZE_ERR_OK + }, +#ifndef MSDOS + { "\\\\?\\C:\\foo", SANITIZE_ALLOW_PATH, + "\\\\?\\C:\\foo", SANITIZE_ERR_OK + }, + { "\\\\?\\C:\\foo", 0, + "____C__foo", SANITIZE_ERR_OK + }, +#endif + { "foo:bar", 0, + "foo_bar", SANITIZE_ERR_OK + }, + { "foo|<>/bar\\\":?*baz", 0, + "foo____bar_____baz", SANITIZE_ERR_OK + }, + { "f:foo::$DATA", 0, + "f_foo__$DATA", SANITIZE_ERR_OK + }, + { "con . air", 0, + "con _ air", SANITIZE_ERR_OK + }, + { "con.air", 0, + "con_air", SANITIZE_ERR_OK + }, + { "con:/x", 0, + "con__x", SANITIZE_ERR_OK + }, + { "file . . . . .. .", 0, + "file", SANITIZE_ERR_OK + }, + { "foo . . ? . . ", 0, + "foo . . _", SANITIZE_ERR_OK + }, + { "com1", 0, + "_com1", SANITIZE_ERR_OK + }, + { "com1", SANITIZE_ALLOW_RESERVED, + "com1", SANITIZE_ERR_OK + }, + { "f:\\com1", 0, + "f__com1", SANITIZE_ERR_OK + }, + { "f:\\com1", SANITIZE_ALLOW_PATH, + "f:\\_com1", SANITIZE_ERR_OK + }, + { "f:\\com1", SANITIZE_ALLOW_RESERVED, + "f__com1", SANITIZE_ERR_OK + }, + { "f:\\com1", SANITIZE_ALLOW_RESERVED | SANITIZE_ALLOW_COLONS, + "f:_com1", SANITIZE_ERR_OK + }, + { "f:\\com1", SANITIZE_ALLOW_RESERVED | SANITIZE_ALLOW_PATH, + "f:\\com1", SANITIZE_ERR_OK + }, + { "com1:\\com1", SANITIZE_ALLOW_PATH, + "_com1:\\_com1", SANITIZE_ERR_OK + }, + { "com1:\\com1", SANITIZE_ALLOW_RESERVED | SANITIZE_ALLOW_PATH, + "com1:\\com1", SANITIZE_ERR_OK + }, + { "com1:\\com1", SANITIZE_ALLOW_RESERVED, + "com1__com1", SANITIZE_ERR_OK + }, +#ifndef MSDOS + { "\\com1", SANITIZE_ALLOW_PATH, + "\\_com1", SANITIZE_ERR_OK + }, + { "\\\\com1", SANITIZE_ALLOW_PATH, + "\\\\com1", SANITIZE_ERR_OK + }, + { "\\\\?\\C:\\com1", SANITIZE_ALLOW_PATH, + "\\\\?\\C:\\com1", SANITIZE_ERR_OK + }, +#endif + { "CoM1", 0, + "_CoM1", SANITIZE_ERR_OK + }, + { "CoM1", SANITIZE_ALLOW_RESERVED, + "CoM1", SANITIZE_ERR_OK + }, + { "COM56", 0, + "COM56", SANITIZE_ERR_OK + }, + /* At the moment we expect a maximum path length of 259. I assume MSDOS + has variable max path lengths depending on compiler that are shorter + so currently these "good" truncate tests won't run on MSDOS */ +#ifndef MSDOS + { "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" + "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + SANITIZE_ALLOW_TRUNCATE, + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" + "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" + "FFFFF", SANITIZE_ERR_OK + }, + { "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" + "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" + "FFF\\FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + SANITIZE_ALLOW_TRUNCATE | SANITIZE_ALLOW_PATH, + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" + "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" + "FFF\\FFFFF", SANITIZE_ERR_OK + }, + { "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" + "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" + "FFF\\FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + SANITIZE_ALLOW_TRUNCATE, + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" + "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" + "FFF_F", SANITIZE_ERR_OK + }, +#endif /* !MSDOS */ + { "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" + "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + 0, + NULL, SANITIZE_ERR_INVALID_PATH + }, + { "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" + "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" + "FFFF\\FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + SANITIZE_ALLOW_TRUNCATE, + NULL, SANITIZE_ERR_INVALID_PATH + }, + { "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" + "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" + "FFFFFFFFFFFFFFFFFFFFFFFFF\\FFFFFFFFFFFFFFFFFFFFFFFF", + SANITIZE_ALLOW_TRUNCATE | SANITIZE_ALLOW_PATH, + NULL, SANITIZE_ERR_INVALID_PATH + }, + { "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" + "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" + "FFF\\FFFFFFFFFFFFFFFFFFFFF:FFFFFFFFFFFFFFFFFFFFFFFF", + SANITIZE_ALLOW_TRUNCATE | SANITIZE_ALLOW_PATH, + NULL, SANITIZE_ERR_INVALID_PATH + }, + { "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" + "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" + "FF\\F:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + SANITIZE_ALLOW_TRUNCATE | SANITIZE_ALLOW_PATH, + NULL, SANITIZE_ERR_INVALID_PATH + }, + { NULL, 0, + NULL, SANITIZE_ERR_BAD_ARGUMENT + }, + }; + + size_t i; + + for(i = 0; i < sizeof data / sizeof data[0]; ++i) { + char *output = NULL; + char *flagstr = NULL; + char *received_ccstr = NULL; + char *expected_ccstr = NULL; + + CURLcode res = sanitize_file_name(&output, data[i].input, data[i].flags); + + if(res == data[i].expected_result && + ((!output && !data[i].expected_output) || + (output && data[i].expected_output && + !strcmp(output, data[i].expected_output)))) { /* OK */ + free(output); + continue; + } + + flagstr = getflagstr(data[i].flags); + received_ccstr = getcurlcodestr(res); + expected_ccstr = getcurlcodestr(data[i].expected_result); + + unitfail++; + fprintf(stderr, "\n" + "%s:%d sanitize_file_name failed.\n" + "input: %s\n" + "flags: %s\n" + "output: %s\n" + "result: %s\n" + "expected output: %s\n" + "expected result: %s\n", + __FILE__, __LINE__, + data[i].input, + flagstr, + (output ? output : "(null)"), + received_ccstr, + (data[i].expected_output ? data[i].expected_output : "(null)"), + expected_ccstr); + + free(output); + free(flagstr); + free(received_ccstr); + free(expected_ccstr); + } +} /* END sanitize_file_name */ + +#else +UNITTEST_START + +{ + fprintf(stderr, "Skipped test not for this platform\n"); +} +#endif /* MSDOS || WIN32 */ + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/unit/unit1605.c b/deps/curl-7.51.0/tests/unit/unit1605.c new file mode 100644 index 0000000000..c807cb3f27 --- /dev/null +++ b/deps/curl-7.51.0/tests/unit/unit1605.c @@ -0,0 +1,49 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curlcheck.h" + +#include "llist.h" + +static CURLcode unit_setup(void) +{ + return CURLE_OK; +} + +static void unit_stop(void) +{ + +} + +UNITTEST_START + CURL *easy = curl_easy_init(); + int len; + char *esc; + + esc = curl_easy_escape(easy, "", -1); + fail_unless(esc == NULL, "negative string length can't work"); + + esc = curl_easy_unescape(easy, "%41%41%41%41", -1, &len); + fail_unless(esc == NULL, "negative string length can't work"); + + curl_easy_cleanup(easy); + +UNITTEST_STOP diff --git a/deps/curl-7.51.0/tests/valgrind.pm b/deps/curl-7.51.0/tests/valgrind.pm new file mode 100644 index 0000000000..838183b442 --- /dev/null +++ b/deps/curl-7.51.0/tests/valgrind.pm @@ -0,0 +1,117 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### + +use File::Basename; + +sub valgrindparse { + my ($srcdir, # the dir in which the runtests script resides + $sslenabled, + $file) = @_; + my $leak; + my $invalidread; + my $uninitedvar; + my $error; + my $partial; + my $us; + + my @o; + + my $bt=0; + my $nssinit=0; + + open(VAL, "<$file"); + while() { + if($bt) { + # back trace parsing + if($_ =~ /^==(\d+)== *(at|by) 0x([0-9A-F]+): (.*)/) { + my $w = $4; + if($w =~ /(.*) \(([^:]*):(\d+)/) { + my ($func, $source, $line)=($1, $2, $3); + my $sourcename = basename($source); + if(-f "$srcdir/../src/$sourcename" || + -f "$srcdir/../lib/$sourcename") { + # this is our source + # print "$func() at $source:$line\n"; + $us++; + } #else {print "Not our source: $func, $source, $line\n";} + } + + # the memory leakage within NSS_InitContext is not a bug of curl + if($w =~ /NSS_InitContext/) { + $nssinit++; + } + } + else { + if($us and not $nssinit) { + # the stack trace included source details about us + + $error++; + if($leak) { + push @o, "\n Leaked $leak bytes\n"; + } + if($invalidread) { + push @o, "\n Read $invalidread invalid bytes\n"; + } + if($uninitedvar) { + push @o, "\n Conditional jump or move depends on uninitialised value(s)\n"; + } + } + $bt = 0; # no more backtrace + $us = 0; + $nssinit = 0; + } + } + else { + if($_ =~ /(\d+) bytes in (\d+) blocks are definitely lost/) { + $leak = $1; + if($leak) { + $error++; + } + $bt = 1; + } + elsif($_ =~ /Invalid read of size (\d+)/) { + $invalidread = $1; + $error++; + $bt = 1; + } + elsif($_ =~ /Conditional jump or move/) { + # If we require SSL, this test case most probaly makes + # us use OpenSSL. OpenSSL produces numerous valgrind + # errors of this kind, rendering it impossible for us to + # detect (valid) reports on actual curl or libcurl code. + + if(!$sslenabled) { + $uninitedvar = 1; + $error++; + $bt = 1; + } + else { + $partial=1; + } + } + } + } + close(VAL); + return @o; +} + +1; diff --git a/deps/curl-7.51.0/tests/valgrind.supp b/deps/curl-7.51.0/tests/valgrind.supp new file mode 100644 index 0000000000..8c81327e1f --- /dev/null +++ b/deps/curl-7.51.0/tests/valgrind.supp @@ -0,0 +1,89 @@ +{ + libidn-idna_to_ascii-error + Memcheck:Addr4 + fun:idna_to_ascii_4z + fun:idna_to_ascii_8z + fun:idna_to_ascii_lz + fun:fix_hostname + fun:resolve_server + fun:create_conn + fun:Curl_connect + fun:multi_runsingle + fun:curl_multi_perform + fun:easy_transfer + fun:easy_perform + fun:curl_easy_perform + fun:operate_do + fun:operate + fun:main +} + +{ + libidn-idna_to_ascii-error-eventbased + Memcheck:Addr4 + fun:idna_to_ascii_4z + fun:idna_to_ascii_8z + fun:idna_to_ascii_lz + fun:fix_hostname + fun:resolve_server + fun:create_conn + fun:Curl_connect + fun:multi_runsingle + fun:multi_socket + fun:curl_multi_socket_action + fun:wait_or_timeout + fun:easy_events + fun:easy_perform + fun:curl_easy_perform_ev + fun:operate_do + fun:operate + fun:main +} + +{ + libidn-idna_to_ascii-error-inlined-functions + Memcheck:Addr4 + fun:idna_to_ascii_4z + fun:idna_to_ascii_8z + fun:idna_to_ascii_lz + fun:fix_hostname + fun:Curl_connect + fun:multi_runsingle + fun:curl_multi_perform + fun:easy_perform.part.4 + fun:operate_do + fun:operate + fun:main +} + +{ + libidn-idna_to_ascii-error-inlined-functions-alt + Memcheck:Addr4 + fun:idna_to_ascii_4z + fun:idna_to_ascii_8z + fun:idna_to_ascii_lz + fun:fix_hostname + fun:Curl_connect + fun:multi_runsingle + fun:curl_multi_perform + fun:easy_perform + fun:operate_do.isra.0 + fun:operate + fun:main +} + +{ + libidn-idna_to_ascii-error-inlined-functions-alt2 + Memcheck:Addr4 + fun:idna_to_ascii_4z + fun:idna_to_ascii_8z + fun:idna_to_ascii_lz + fun:fix_hostname + fun:Curl_connect + fun:multi_runsingle + fun:curl_multi_perform + fun:easy_perform + fun:operate_do + fun:operate + fun:main +} diff --git a/deps/curl-7.51.0/winbuild/BUILD.WINDOWS.txt b/deps/curl-7.51.0/winbuild/BUILD.WINDOWS.txt new file mode 100644 index 0000000000..322aa65eb5 --- /dev/null +++ b/deps/curl-7.51.0/winbuild/BUILD.WINDOWS.txt @@ -0,0 +1,99 @@ +Building with Visual C++, prerequisites +======================================= + + This document describes how to compile, build and install curl and libcurl + from sources using the Visual C++ build tool. To build with VC++, you will + of course have to first install VC++. The minimum required version of + VC is 6 (part of Visual Studio 6). However using a more recent version is + strongly recommended. + + VC++ is also part of the Windows Platform SDK. You do not have to install + the full Visual Studio or Visual C++ if all you want is to build curl. + + The latest Platform SDK can be downloaded freely from: + + https://msdn.microsoft.com/en-us/windows/bb980924 + + If you are building with VC6 then you will also need the February 2003 + Edition of the Platform SDK which can be downloaded from: + + https://www.microsoft.com/en-us/download/details.aspx?id=12261 + + If you wish to support zlib, openssl, c-ares, ssh2, you will have to download + them separately and copy them to the deps directory as shown below: + + somedirectory\ + |_curl-src + | |_winbuild + | + |_deps + |_ lib + |_ include + |_ bin + + It is also possible to create the deps directory in some other random + places and tell the Makefile its location using the WITH_DEVEL option. + +Building with Visual C++ +======================== + +Open a Visual Studio Command prompt or the SDK CMD shell. + + Using the CMD Shell: + choose the right environment via the setenv command (see setenv /?) + for the full list of options. setenv /xp /x86 /release for example. + + Using the Visual Studio command prompt Shell: + Everything is already pre-configured by calling one of the command + prompt. + +Once you are in the console, go to the winbuild directory in the Curl +sources: + cd curl-src\winbuild + +Then you can call nmake /f Makefile.vc with the desired options (see below). +The builds will be in the top src directory, builds\ directory, in +a directory named using the options given to the nmake call. + +nmake /f Makefile.vc mode= + +where is one or many of: + VC=<6,7,8,9,10,11,12,14> - VC versions + WITH_DEVEL= - Paths for the development files (SSL, zlib, etc.) + Defaults to sibbling directory deps: ../deps + Libraries can be fetched at http://windows.php.net/downloads/php-sdk/deps/ + Uncompress them into the deps folder. + WITH_SSL= - Enable OpenSSL support, DLL or static + WITH_MBEDTLS= - Enable mbedTLS support, DLL or static + WITH_CARES= - Enable c-ares support, DLL or static + WITH_ZLIB= - Enable zlib support, DLL or static + WITH_SSH2= - Enable libSSH2 support, DLL or static + ENABLE_SSPI= - Enable SSPI support, defaults to yes + ENABLE_IPV6= - Enable IPv6, defaults to yes + ENABLE_IDN= - Enable use of Windows IDN APIs, defaults to yes + Requires Windows Vista or later, or installation from: + https://www.microsoft.com/downloads/details.aspx?FamilyID=AD6158D7-DDBA-416A-9109-07607425A815 + ENABLE_WINSSL= - Enable native Windows SSL support, defaults to yes + GEN_PDB= - Generate Program Database (debug symbols for release build) + DEBUG= - Debug builds + MACHINE= - Target architecture (default is x86) + +Static linking of Microsoft's C RunTime (CRT): +============================================== +If you are using mode=static nmake will create and link to the static build of +libcurl but *not* the static CRT. If you must you can force nmake to link in +the static CRT by passing RTLIBCFG=static. Typically you shouldn't use that +option, and nmake will default to the DLL CRT. RTLIBCFG is rarely used and +therefore rarely tested. When passing RTLIBCFG for a configuration that was +already built but not with that option, or if the option was specified +differently, you must destroy the build directory containing the configuration +so that nmake can build it from scratch. + +Legacy Windows and SSL +====================== +When you build curl using the build files in this directory the default SSL +backend will be WinSSL (Windows SSPI, more specifically Schannel), the native +SSL library that comes with the Windows OS. WinSSL in Windows <= XP is not able +to connect to servers that no longer support the legacy handshakes and +algorithms used by those versions. If you will be using curl in one of those +earlier versions of Windows you should choose another SSL backend like OpenSSL. diff --git a/deps/curl-7.51.0/winbuild/Makefile.msvc.names b/deps/curl-7.51.0/winbuild/Makefile.msvc.names new file mode 100644 index 0000000000..8d8413ea6a --- /dev/null +++ b/deps/curl-7.51.0/winbuild/Makefile.msvc.names @@ -0,0 +1,81 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1999 - 2010, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +#*************************************************************************** + +# +# This file is included from MSVC makefiles located in lib and src, +# providing libcurl common file names required by these makefiles. +# + +# ------------------ +# libcurl base name +# ------------------ + +!IF !DEFINED(LIB_NAME) || "$(LIB_NAME)" == "" +LIB_NAME = libcurl +!ENDIF + +# ------------------------------------------------- +# libcurl static and dynamic libraries common base +# file names for release and debug configurations +# ------------------------------------------------- + +!IF !DEFINED(LIB_NAME_STA_REL) || "$(LIB_NAME_STA_REL)" == "" +LIB_NAME_STA_REL = $(LIB_NAME) +!ENDIF + +!IF !DEFINED(LIB_NAME_STA_DBG) || "$(LIB_NAME_STA_DBG)" == "" +LIB_NAME_STA_DBG = $(LIB_NAME_STA_REL)d +!ENDIF + +!IF !DEFINED(LIB_NAME_DYN_REL) || "$(LIB_NAME_DYN_REL)" == "" +LIB_NAME_DYN_REL = $(LIB_NAME) +!ENDIF + +!IF !DEFINED(LIB_NAME_DYN_DBG) || "$(LIB_NAME_DYN_DBG)" == "" +LIB_NAME_DYN_DBG = $(LIB_NAME_DYN_REL)d +!ENDIF + +# -------------------------------------------- +# Base names for libcurl DLL import libraries +# -------------------------------------------- + +!IF !DEFINED(LIB_NAME_IMP_REL) || "$(LIB_NAME_IMP_REL)" == "" +LIB_NAME_IMP_REL = $(LIB_NAME_DYN_REL)_imp +!ENDIF + +!IF !DEFINED(LIB_NAME_IMP_DBG) || "$(LIB_NAME_IMP_DBG)" == "" +LIB_NAME_IMP_DBG = $(LIB_NAME_DYN_DBG)_imp +!ENDIF + +# -------------------------------------- +# File names with extension and no path +# -------------------------------------- + +LIBCURL_STA_LIB_REL = $(LIB_NAME_STA_REL).lib +LIBCURL_STA_LIB_DBG = $(LIB_NAME_STA_DBG).lib +LIBCURL_DYN_LIB_REL = $(LIB_NAME_DYN_REL).dll +LIBCURL_DYN_LIB_DBG = $(LIB_NAME_DYN_DBG).dll +LIBCURL_IMP_LIB_REL = $(LIB_NAME_IMP_REL).lib +LIBCURL_IMP_LIB_DBG = $(LIB_NAME_IMP_DBG).lib +LIBCURL_DYN_LIB_PDB = $(LIB_NAME_IMP_DBG).pdb + +# End of Makefile.msvc.names diff --git a/deps/curl-7.51.0/winbuild/Makefile.vc b/deps/curl-7.51.0/winbuild/Makefile.vc new file mode 100644 index 0000000000..bd5c8e0323 --- /dev/null +++ b/deps/curl-7.51.0/winbuild/Makefile.vc @@ -0,0 +1,224 @@ +!IF "$(MODE)"=="static" +TARGET = $(LIB_NAME_STATIC) +AS_DLL = false +CFGSET=true +!ELSEIF "$(MODE)"=="dll" +TARGET = $(LIB_NAME_DLL) +AS_DLL = true +CFGSET=true +!ELSE +!MESSAGE Invalid mode: $(MODE) + +####################### +# Usage +# + +!MESSAGE Usage: nmake /f Makefile.vc mode= +!MESSAGE where is one or many of: +!MESSAGE VC=<6,7,8,9,10,11,12,14> - VC versions +!MESSAGE WITH_DEVEL= - Paths for the development files (SSL, zlib, etc.) +!MESSAGE Defaults to sibbling directory deps: ../deps +!MESSAGE Libraries can be fetched at http://pecl2.php.net/downloads/php-windows-builds/ +!MESSAGE Uncompress them into the deps folder. +!MESSAGE WITH_SSL= - Enable OpenSSL support, DLL or static +!MESSAGE WITH_CARES= - Enable c-ares support, DLL or static +!MESSAGE WITH_ZLIB= - Enable zlib support, DLL or static +!MESSAGE WITH_SSH2= - Enable libSSH2 support, DLL or static +!MESSAGE WITH_MBEDTLS= - Enable mbedTLS support, DLL or static +!MESSAGE ENABLE_IDN= - Enable use of Windows IDN APIs, defaults to yes +!MESSAGE Requires Windows Vista or later, or installation from: +!MESSAGE https://www.microsoft.com/en-us/download/details.aspx?id=734 +!MESSAGE ENABLE_IPV6= - Enable IPv6, defaults to yes +!MESSAGE ENABLE_SSPI= - Enable SSPI support, defaults to yes +!MESSAGE ENABLE_WINSSL= - Enable native Windows SSL support, defaults to yes +!MESSAGE GEN_PDB= - Generate Program Database (debug symbols for release build) +!MESSAGE DEBUG= - Debug builds +!MESSAGE MACHINE= - Target architecture (default x64 on AMD64, x86 on others) +!ERROR please choose a valid mode + +!ENDIF + +!INCLUDE "../lib/Makefile.inc" +LIBCURL_OBJS=$(CSOURCES:.c=.obj) + +!INCLUDE "../src/Makefile.inc" + +# tool_hugehelp has a special rule +CURL_OBJS=$(CURL_CFILES:tool_hugehelp.c=) + +CURL_OBJS=$(CURL_OBJS:.c=.obj) + + +# backwards compatible check for USE_SSPI +!IFDEF USE_SSPI +ENABLE_SSPI = $(USE_SSPI) +!ENDIF + +# default options +!IFNDEF MACHINE +!IF "$(PROCESSOR_ARCHITECTURE)"=="AMD64" +MACHINE = x64 +!ELSE +MACHINE = x86 +!ENDIF +!ENDIF + +!IFNDEF ENABLE_IDN +USE_IDN = true +!ELSEIF "$(ENABLE_IDN)"=="yes" +USE_IDN = true +!ELSEIF "$(ENABLE_IDN)"=="no" +USE_IDN = false +!ENDIF + +!IFNDEF ENABLE_IPV6 +USE_IPV6 = true +!ELSEIF "$(ENABLE_IPV6)"=="yes" +USE_IPV6 = true +!ELSEIF "$(ENABLE_IPV6)"=="no" +USE_IPV6 = false +!ENDIF + +!IFNDEF ENABLE_SSPI +USE_SSPI = true +!ELSEIF "$(ENABLE_SSPI)"=="yes" +USE_SSPI = true +!ELSEIF "$(ENABLE_SSPI)"=="no" +USE_SSPI = false +!ENDIF + +!IFNDEF ENABLE_WINSSL +!IF DEFINED(WITH_SSL) || DEFINED(WITH_MBEDTLS) +USE_WINSSL = false +!ELSE +USE_WINSSL = $(USE_SSPI) +!ENDIF +!ELSEIF "$(ENABLE_WINSSL)"=="yes" +USE_WINSSL = true +!ELSEIF "$(ENABLE_WINSSL)"=="no" +USE_WINSSL = false +!ENDIF + +CONFIG_NAME_LIB = libcurl + +!IF "$(WITH_SSL)"=="dll" +USE_SSL = true +SSL = dll +!ELSEIF "$(WITH_SSL)"=="static" +USE_SSL = true +SSL = static +!ENDIF + +!IF "$(WITH_MBEDTLS)"=="dll" || "$(WITH_MBEDTLS)"=="static" +USE_MBEDTLS = true +MBEDTLS = $(WITH_MBEDTLS) +!ENDIF + +!IF ( "$(USE_SSL)"=="true" && "$(USE_WINSSL)"=="true" ) \ + || ( "$(USE_SSL)"=="true" && "$(USE_MBEDTLS)"=="true" ) \ + || ( "$(USE_MBEDTLS)"=="true" && "$(USE_WINSSL)"=="true" ) +!ERROR SSL, MBEDTLS and WINSSL are mutual exclusive options. +!ENDIF + +!IF "$(WITH_CARES)"=="dll" +USE_CARES = true +CARES = dll +!ELSEIF "$(WITH_CARES)"=="static" +USE_CARES = true +CARES = static +!ENDIF + +!IF "$(WITH_ZLIB)"=="dll" +USE_ZLIB = true +ZLIB = dll +!ELSEIF "$(WITH_ZLIB)"=="static" +USE_ZLIB = true +ZLIB = static +!ENDIF + +!IF "$(WITH_SSH2)"=="dll" +USE_SSH2 = true +SSH2 = dll +!ELSEIF "$(WITH_SSH2)"=="static" +USE_SSH2 = true +SSH2 = static +!ENDIF + +CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-vc$(VC)-$(MACHINE) + +!IF "$(DEBUG)"=="yes" +CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-debug +!ELSE +CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-release +!ENDIF + +!IF "$(AS_DLL)"=="true" +CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-dll +!ELSE +CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-static +!ENDIF + +!IF "$(USE_SSL)"=="true" +CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-ssl-$(SSL) +!ENDIF + +!IF "$(USE_MBEDTLS)"=="true" +CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-mbedtls-$(MBEDTLS) +!ENDIF + +!IF "$(USE_CARES)"=="true" +CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-cares-$(CARES) +!ENDIF + +!IF "$(USE_ZLIB)"=="true" +CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-zlib-$(ZLIB) +!ENDIF + +!IF "$(USE_SSH2)"=="true" +CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-ssh2-$(SSH2) +!ENDIF + +!IF "$(USE_IPV6)"=="true" +CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-ipv6 +!ENDIF + +!IF "$(USE_SSPI)"=="true" +CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-sspi +!ENDIF + +!IF "$(USE_WINSSL)"=="true" +CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-winssl +!ENDIF + +!MESSAGE configuration name: $(CONFIG_NAME_LIB) + +BUILD_DIR=../builds/$(CONFIG_NAME_LIB) +LIBCURL_DIROBJ = ..\builds\$(CONFIG_NAME_LIB)-obj-lib +CURL_DIROBJ = ..\builds\$(CONFIG_NAME_LIB)-obj-curl +DIRDIST = ..\builds\$(CONFIG_NAME_LIB)\ + +$(MODE): + @IF NOT EXIST ..\include\curl\curlbuild.h ( \ + CALL ..\buildconf.bat \ + ) + @SET DIROBJ=$(LIBCURL_DIROBJ) + @SET MACRO_NAME=LIBCURL_OBJS + @SET OUTFILE=LIBCURL_OBJS.inc + @gen_resp_file.bat $(LIBCURL_OBJS) + + @SET DIROBJ=$(CURL_DIROBJ) + @SET MACRO_NAME=CURL_OBJS + @SET OUTFILE=CURL_OBJS.inc + @gen_resp_file.bat $(CURL_OBJS) + + @SET CONFIG_NAME_LIB=$(CONFIG_NAME_LIB) + @SET MACHINE=$(MACHINE) + @SET USE_IDN=$(USE_IDN) + @SET USE_IPV6=$(USE_IPV6) + @SET USE_SSPI=$(USE_SSPI) + @SET USE_WINSSL=$(USE_WINSSL) + @$(MAKE) /NOLOGO /F MakefileBuild.vc + +copy_from_lib: + echo copying .c... + FOR %%i IN ($(CURLX_CFILES:/=\)) DO copy %%i ..\src\ diff --git a/deps/curl-7.51.0/winbuild/MakefileBuild.vc b/deps/curl-7.51.0/winbuild/MakefileBuild.vc new file mode 100644 index 0000000000..3ce68769a2 --- /dev/null +++ b/deps/curl-7.51.0/winbuild/MakefileBuild.vc @@ -0,0 +1,521 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1999 - 2016, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +#*************************************************************************** + +########################################################################### +# +# Makefile for building libcurl with MSVC 6, 7, 8, 9, 10, 11, 12 and 14 +# +# Usage: see usage message below +# Should be invoked from winbuild directory +# Edit the paths and desired library name +# SSL path is only required if you intend compiling +# with SSL. +# +# This make file leaves the result either a .lib or .dll file +# in the \lib directory. It should be called from the \lib +# directory. +# +# An option would have been to allow the source directory to +# be specified, but I saw no requirement. +# +# Another option would have been to leave the .lib and .dll +# files in the "cfg" directory, but then the make file +# in \src would need to be changed. +# +############################################################## + +CFGSET=FALSE +WINBUILD_DIR=`cd` +ZIP = zip.exe + +# Allow changing C compiler via environment variable CC (default cl.exe) +# This command macro is not set by default: https://msdn.microsoft.com/en-us/library/ms933742.aspx +!If "$(CC)" == "" +CC = cl.exe +!Endif + +!IF "$(VC)"=="6" +CC_NODEBUG = $(CC) /O2 /DNDEBUG +CC_DEBUG = $(CC) /Od /Gm /Zi /D_DEBUG /GZ +CFLAGS = /I. /I../lib /I../include /nologo /W3 /GX /DWIN32 /YX /FD /c /DBUILDING_LIBCURL +!ELSE +CC_NODEBUG = $(CC) /O2 /DNDEBUG +CC_DEBUG = $(CC) /Od /D_DEBUG /RTC1 /Z7 /LDd /W3 +CFLAGS = /I. /I ../lib /I../include /nologo /W3 /EHsc /DWIN32 /FD /c /DBUILDING_LIBCURL +!ENDIF + +LFLAGS = /nologo /machine:$(MACHINE) +LNKDLL = link.exe /DLL +LNKLIB = link.exe /lib + +CFLAGS_PDB = /Zi +LFLAGS_PDB = /incremental:no /opt:ref,icf + +CFLAGS_LIBCURL_STATIC = /DCURL_STATICLIB + +WIN_LIBS = ws2_32.lib wldap32.lib advapi32.lib + +BASE_NAME = libcurl +BASE_NAME_DEBUG = $(BASE_NAME)_debug +BASE_NAME_STATIC = $(BASE_NAME)_a +BASE_NAME_STATIC_DEBUG = $(BASE_NAME_STATIC)_debug + +LIB_NAME_STATIC = $(BASE_NAME_STATIC).lib +LIB_NAME_STATIC_DEBUG = $(BASE_NAME_STATIC_DEBUG).lib +LIB_NAME_DLL = $(BASE_NAME).dll +LIB_NAME_IMP = $(BASE_NAME).lib +LIB_NAME_DLL_DEBUG = $(BASE_NAME_DEBUG).dll +LIB_NAME_IMP_DEBUG = $(BASE_NAME_DEBUG).lib + +PDB_NAME_STATIC = $(BASE_NAME_STATIC).pdb +PDB_NAME_STATIC_DEBUG = $(BASE_NAME_STATIC_DEBUG).pdb +PDB_NAME_DLL = $(BASE_NAME).pdb +PDB_NAME_DLL_DEBUG = $(BASE_NAME_DEBUG).pdb + +# CURL Command section +PROGRAM_NAME = curl.exe +CURL_CFLAGS = /I../lib /I../include /nologo /W3 /EHsc /DWIN32 /FD /c +CURL_LFLAGS = /nologo /out:$(DIRDIST)\bin\$(PROGRAM_NAME) /subsystem:console /machine:$(MACHINE) +CURL_RESFLAGS = /i../include + +############################################################# +## Nothing more to do below this line! +LIBCURL_SRC_DIR = ..\lib +CURL_SRC_DIR = ..\src + +!IFNDEF WITH_DEVEL +WITH_DEVEL = ../../deps +!ENDIF +DEVEL_INCLUDE = $(WITH_DEVEL)/include +DEVEL_LIB = $(WITH_DEVEL)/lib +DEVEL_BIN = $(WITH_DEVEL)/bin + +CFLAGS = $(CFLAGS) /I"$(DEVEL_INCLUDE)" +LFLAGS = $(LFLAGS) "/LIBPATH:$(DEVEL_LIB)" + + +!IF "$(WITH_SSL)"=="dll" +SSL_LIBS = libeay32.lib ssleay32.lib +USE_SSL = true +SSL = dll +!ELSEIF "$(WITH_SSL)"=="static" +SSL_LIBS = libeay32.lib ssleay32.lib gdi32.lib user32.lib crypt32.lib +USE_SSL = true +SSL = static +!ENDIF + +!IFDEF USE_SSL +SSL_CFLAGS = /DUSE_OPENSSL /I"$(DEVEL_INCLUDE)/openssl" +!ENDIF + +!IF "$(WITH_MBEDTLS)"=="dll" || "$(WITH_MBEDTLS)"=="static" +USE_MBEDTLS = true +MBEDTLS = $(WITH_MBEDTLS) +MBEDTLS_CFLAGS = /DUSE_MBEDTLS +MBEDTLS_LIBS = mbedtls.lib mbedcrypto.lib mbedx509.lib +!ENDIF + + +!IF "$(WITH_CARES)"=="dll" +!IF "$(DEBUG)"=="yes" +CARES_LIBS = caresd.lib +!ELSE +CARES_LIBS = cares.lib +!ENDIF +USE_CARES = true +CARES = dll +!ELSEIF "$(WITH_CARES)"=="static" +!IF "$(DEBUG)"=="yes" +CARES_LIBS = libcaresd.lib +!ELSE +CARES_LIBS = libcares.lib +!ENDIF +USE_CARES = true +CARES = static +!ENDIF + +!IFDEF USE_CARES +CARES_CFLAGS = /DUSE_ARES /I"$(DEVEL_INCLUDE)/cares" +!ENDIF + +!IF "$(WITH_ZLIB)"=="dll" +ZLIB_LIBS = zlib.lib +USE_ZLIB = true +ZLIB = dll +!ELSEIF "$(WITH_ZLIB)"=="static" +ZLIB_LIBS = zlib_a.lib +USE_ZLIB = true +ZLIB = static +!ENDIF + +!IFDEF USE_ZLIB +ZLIB_CFLAGS = /DHAVE_ZLIB_H /DHAVE_ZLIB /DHAVE_LIBZ +!ENDIF + + +!IF "$(WITH_SSH2)"=="dll" +SSH2_LIBS = libssh2.lib +USE_SSH2 = true +SSH2 = dll +!ELSEIF "$(WITH_SSH2)"=="static" +SSH2_LIBS = libssh2_a.lib user32.lib +USE_SSH2 = true +SSH2 = static +!ENDIF + +!IFDEF USE_SSH2 +SSH2_CFLAGS = /DHAVE_LIBSSH2 /DHAVE_LIBSSH2_H /DLIBSSH2_WIN32 /DLIBSSH2_LIBRARY /DUSE_LIBSSH2 +SSH2_CFLAGS = $(SSH2_CFLAGS) /I$(WITH_DEVEL)/include/libssh2 +!ENDIF + + +!IFNDEF USE_IDN +USE_IDN = true +!ELSEIF "$(USE_IDN)"=="yes" +USE_IDN = true +!ENDIF + +!IF "$(USE_IDN)"=="true" +IDN_CFLAGS = $(IDN_CFLAGS) /DUSE_WIN32_IDN /DWANT_IDN_PROTOTYPES +WIN_LIBS = $(WIN_LIBS) Normaliz.lib +!ENDIF + + +!IFNDEF USE_IPV6 +USE_IPV6 = true +!ELSEIF "$(USE_IPV6)"=="yes" +USE_IPV6 = true +!ENDIF + +!IF "$(USE_IPV6)"=="true" +IPV6_CFLAGS = $(IPV6_CFLAGS) /DUSE_IPV6 +!ENDIF + + +!IFNDEF USE_SSPI +USE_SSPI = true +!ELSEIF "$(USE_SSPI)"=="yes" +USE_SSPI = true +!ENDIF + +!IF "$(USE_SSPI)"=="true" +SSPI_CFLAGS = $(SSPI_CFLAGS) /DUSE_WINDOWS_SSPI +!ENDIF + + +!IFNDEF USE_WINSSL +!IF "$(USE_SSL)"=="true" +USE_WINSSL = false +!ELSE +USE_WINSSL = $(USE_SSPI) +!ENDIF +!ELSEIF "$(USE_WINSSL)"=="yes" +USE_WINSSL = true +!ENDIF + + +!IF "$(USE_WINSSL)"=="true" +!IF "$(USE_SSPI)"!="true" +!ERROR cannot build with WinSSL without SSPI +!ENDIF +SSPI_CFLAGS = $(SSPI_CFLAGS) /DUSE_SCHANNEL +WIN_LIBS = $(WIN_LIBS) Crypt32.lib +!ENDIF + + +!IF "$(GEN_PDB)"=="yes" +GEN_PDB = true +!ENDIF + + +!IFDEF EMBED_MANIFEST +MANIFESTTOOL = mt -manifest $(DIRDIST)\$(PROGRAM_NAME).manifest -outputresource:$(DIRDIST)\$(PROGRAM_NAME);1 +!ENDIF + +# Runtime library configuration +!IF "$(RTLIBCFG)"=="static" +RTLIB = /MT +RTLIB_DEBUG = /MTd +!ELSE +RTLIB = /MD +RTLIB_DEBUG = /MDd +!ENDIF + +!IF "$(MODE)"=="static" +TARGET = $(LIB_NAME_STATIC) +CURL_LIBCURL_LIBNAME=$(LIB_NAME_STATIC) +AS_DLL = false +CFGSET = true +!ELSEIF "$(MODE)"=="dll" +TARGET = $(LIB_NAME_DLL) +CURL_LIBCURL_LIBNAME=$(LIB_NAME_IMP) +AS_DLL = true +CFGSET = true +!ENDIF + +!IF "$(CFGSET)" == "FALSE" +!ERROR please choose a valid mode +!ENDIF + + + +# CURL_XX macros are for the curl.exe command + +!IF "$(DEBUG)"=="yes" +RC_FLAGS = /dDEBUGBUILD=1 /Fo $@ $(LIBCURL_SRC_DIR)\libcurl.rc +CURL_CC = $(CC_DEBUG) $(RTLIB_DEBUG) +CURL_RC_FLAGS = /i../include /dDEBUGBUILD=1 /Fo $@ $(CURL_SRC_DIR)\curl.rc +!ELSE +RC_FLAGS = /dDEBUGBUILD=0 /Fo $@ $(LIBCURL_SRC_DIR)\libcurl.rc +CURL_CC = $(CC_NODEBUG) $(RTLIB) +CURL_RC_FLAGS = /i../include /dDEBUGBUILD=0 /Fo $@ $(CURL_SRC_DIR)\curl.rc +!ENDIF + +!IF "$(AS_DLL)" == "true" + +LNK = $(LNKDLL) $(WIN_LIBS) /out:$(LIB_DIROBJ)\$(TARGET) +!IF "$(DEBUG)"=="yes" +TARGET = $(LIB_NAME_DLL_DEBUG) +LNK = $(LNK) /DEBUG /IMPLIB:$(LIB_DIROBJ)\$(LIB_NAME_IMP_DEBUG) +PDB = $(PDB_NAME_DLL_DEBUG) +CURL_LIBS = /IMPLIB:$(LIB_DIROBJ)\$(LIB_NAME_IMP_DEBUG) +!ELSE +TARGET = $(LIB_NAME_DLL) +LNK = $(LNK) /IMPLIB:$(LIB_DIROBJ)\$(LIB_NAME_IMP) +PDB = $(PDB_NAME_DLL) +CURL_LIBS = /IMPLIB:$(LIB_DIROBJ)\$(LIB_NAME_IMP) +!ENDIF +RESOURCE = $(LIB_DIROBJ)\libcurl.res + +# AS_DLL +!ELSE + +!IF "$(DEBUG)"=="yes" +TARGET = $(LIB_NAME_STATIC_DEBUG) +PDB = $(PDB_NAME_STATIC_DEBUG) +!ELSE +TARGET = $(LIB_NAME_STATIC) +PDB = $(PDB_NAME_STATIC) +!ENDIF +LNK = $(LNKLIB) $(WIN_LIBS) /out:$(LIB_DIROBJ)\$(TARGET) +CURL_CC = $(CURL_CC) $(CFLAGS_LIBCURL_STATIC) + +# AS_DLL +!ENDIF + +!IF "$(USE_SSL)"=="true" +CFLAGS = $(CFLAGS) $(SSL_CFLAGS) +LFLAGS = $(LFLAGS) $(SSL_LFLAGS) $(SSL_LIBS) +!ENDIF + +!IF "$(USE_MBEDTLS)"=="true" +CFLAGS = $(CFLAGS) $(MBEDTLS_CFLAGS) +LFLAGS = $(LFLAGS) $(MBEDTLS_LFLAGS) $(MBEDTLS_LIBS) +!ENDIF + +!IF "$(USE_CARES)"=="true" +CFLAGS = $(CFLAGS) $(CARES_CFLAGS) +LFLAGS = $(LFLAGS) $(CARES_LFLAGS) $(CARES_LIBS) +!ENDIF + +!IF "$(USE_ZLIB)"=="true" +CFLAGS = $(CFLAGS) $(ZLIB_CFLAGS) +LFLAGS = $(LFLAGS) $(ZLIB_LFLAGS) $(ZLIB_LIBS) +!ENDIF + +!IF "$(USE_SSH2)"=="true" +CFLAGS = $(CFLAGS) $(SSH2_CFLAGS) +LFLAGS = $(LFLAGS) $(SSH2_LFLAGS) $(SSH2_LIBS) +!ENDIF + +!IF "$(USE_IDN)"=="true" +CFLAGS = $(CFLAGS) $(IDN_CFLAGS) +!ENDIF + +!IF "$(USE_IPV6)"=="true" +CFLAGS = $(CFLAGS) $(IPV6_CFLAGS) +!ENDIF + +!IF "$(USE_SSPI)"=="true" +CFLAGS = $(CFLAGS) $(SSPI_CFLAGS) +!ENDIF + +!IF "$(GEN_PDB)"=="true" +CFLAGS = $(CFLAGS) $(CFLAGS_PDB) /Fd"$(LIB_DIROBJ)\$(PDB)" +LFLAGS = $(LFLAGS) $(LFLAGS_PDB) +!ENDIF + +LIB_DIROBJ = ..\builds\$(CONFIG_NAME_LIB)-obj-lib +CURL_DIROBJ = ..\builds\$(CONFIG_NAME_LIB)-obj-curl +DIRDIST = ..\builds\$(CONFIG_NAME_LIB)\ + +# +# curl.exe +# +CURL_LINK = link.exe /incremental:no /libpath:"$(DIRDIST)\lib" + +#!IF "$(CFG)" == "release-ssh2-ssl-dll-zlib" +#TARGET = $(LIB_NAME_STATIC) +#LNK = $(LNKLIB) $(WINLIBS) $(SSLLIBS) $(ZLIBLIBS) $(SSH2LIBS) $(SSL_LFLAGS) $(ZLIB_LFLAGS) $(LFLAGSSSH) /out:$(LIB_DIROBJ)\$(TARGET) +#CC = $(CCNODBG) $(RTLIB) $(SSL_CFLAGS) $(ZLIB_CFLAGS) $(CFLAGSLIB) $(SSH2_CFLAGS) +#CFGSET = TRUE +#!ENDIF + +####################### +# Only the clean target can be used if a config was not provided. +# +!IF "$(CFGSET)" == "FALSE" +clean: + @-erase /s *.dll 2> NUL + @-erase /s *.exp 2> NUL + @-erase /s *.idb 2> NUL + @-erase /s *.lib 2> NUL + @-erase /s *.obj 2> NUL + @-erase /s *.pch 2> NUL + @-erase /s *.pdb 2> NUL + @-erase /s *.res 2> NUL +!ELSE +# A mode was provided, so the library can be built. +# +!include CURL_OBJS.inc +!include LIBCURL_OBJS.inc + +!IF "$(AS_DLL)" == "true" +LIB_OBJS = $(LIBCURL_OBJS) $(RESOURCE) +!ELSE +LIB_OBJS = $(LIBCURL_OBJS) +!ENDIF + +EXE_OBJS = $(CURL_OBJS) $(CURL_DIROBJ)\curl.res + +all : $(TARGET) $(PROGRAM_NAME) + +package: $(TARGET) + @cd $(DIRDIST) + @-$(ZIP) -9 -q -r ..\$(CONFIG_NAME_LIB).zip .>nul 2<&1 + @cd $(MAKEDIR) + +$(TARGET): $(LIB_OBJS) $(LIB_DIROBJ) $(DISTDIR) + @echo Using SSL: $(USE_SSL) + @echo Using c-ares: $(USE_CARES) + @echo Using SSH2: $(USE_SSH2) + @echo Using ZLIB: $(USE_ZLIB) + @echo Using IDN: $(USE_IDN) + @echo Using IPv6: $(USE_IPV6) + @echo Using SSPI: $(USE_SSPI) + @echo Using WinSSL: $(USE_WINSSL) + @echo CFLAGS: $(CFLAGS) + @echo LFLAGS: $(LFLAGS) + @echo GenPDB: $(GEN_PDB) + @echo Debug: $(DEBUG) + @echo Machine: $(MACHINE) + $(LNK) $(LFLAGS) $(LIB_OBJS) + @echo Copying libs... + @if exist $(LIB_DIROBJ)\$(LIB_NAME_DLL) copy $(LIB_DIROBJ)\$(LIB_NAME_DLL) $(DIRDIST)\bin\ /y >nul 2<&1 + @if exist $(LIB_DIROBJ)\$(LIB_NAME_STATIC) copy $(LIB_DIROBJ)\$(LIB_NAME_STATIC) $(DIRDIST)\lib\ /y >nul 2<&1 + @if exist $(LIB_DIROBJ)\$(LIB_NAME_DLL_DEBUG) copy $(LIB_DIROBJ)\$(LIB_NAME_DLL_DEBUG) $(DIRDIST)\bin\ /y >nul 2<&1 + @if exist $(LIB_DIROBJ)\$(LIB_NAME_STATIC_DEBUG) copy $(LIB_DIROBJ)\$(LIB_NAME_STATIC_DEBUG) $(DIRDIST)\lib\ /y >nul 2<&1 + @if exist $(LIB_DIROBJ)\$(LIB_NAME_IMP) copy $(LIB_DIROBJ)\$(LIB_NAME_IMP) $(DIRDIST)\lib\ /y >nul 2<&1 + @if exist $(LIB_DIROBJ)\$(LIB_NAME_IMP_DEBUG) copy $(LIB_DIROBJ)\$(LIB_NAME_IMP_DEBUG) $(DIRDIST)\lib >nul 2<&1 + @-copy $(LIB_DIROBJ)\*.exp $(DIRDIST)\lib /y >nul 2<&1 + @-copy $(LIB_DIROBJ)\*.pdb $(DIRDIST)\lib /y >nul 2<&1 + @-copy ..\include\curl\*.h $(DIRDIST)\include\curl\ /y >nul 2<&1 + +$(LIB_OBJS): $(LIB_DIROBJ) $(DIRDIST) + +$(DIRDIST): + @if not exist "$(DIRDIST)\bin" mkdir $(DIRDIST)\bin + @if not exist "$(DIRDIST)\include" mkdir $(DIRDIST)\include + @if not exist "$(DIRDIST)\include\curl" mkdir $(DIRDIST)\include\curl + @if not exist "$(DIRDIST)\lib" mkdir $(DIRDIST)\lib + +$(LIB_DIROBJ): + @if not exist "$(LIB_DIROBJ)" mkdir $(LIB_DIROBJ) + @if not exist "$(LIB_DIROBJ)\vauth" mkdir $(LIB_DIROBJ)\vauth + @if not exist "$(LIB_DIROBJ)\vtls" mkdir $(LIB_DIROBJ)\vtls + +$(CURL_DIROBJ): + @if not exist "$(CURL_DIROBJ)" mkdir $(CURL_DIROBJ) +# we need a lib dir for the portability functions from libcurl +# we use the .c directly here + @if not exist "$(CURL_DIROBJ)" mkdir $(CURL_DIROBJ)\lib + +.SUFFIXES: .c .obj .res + +{$(LIBCURL_SRC_DIR)\}.c{$(LIB_DIROBJ)\}.obj: + $(CURL_CC) $(CFLAGS) /Fo"$@" $< + +{$(LIBCURL_SRC_DIR)\vauth\}.c{$(LIB_DIROBJ)\vauth\}.obj: + $(CURL_CC) $(CFLAGS) /Fo"$@" $< + +{$(LIBCURL_SRC_DIR)\vtls\}.c{$(LIB_DIROBJ)\vtls\}.obj: + $(CURL_CC) $(CFLAGS) /Fo"$@" $< + +$(LIB_DIROBJ)\libcurl.res: $(LIBCURL_SRC_DIR)\libcurl.rc + rc $(RC_FLAGS) + +# +# curl.exe +# + + +!IF "$(MODE)"=="static" +!IF "$(DEBUG)"=="yes" +CURL_LIBCURL_LIBNAME=$(LIB_NAME_STATIC_DEBUG) +!ELSE +CURL_LIBCURL_LIBNAME=$(LIB_NAME_STATIC) +!ENDIF +!ELSEIF "$(MODE)"=="dll" +!IF "$(DEBUG)"=="yes" +CURL_LIBCURL_LIBNAME=$(LIB_NAME_IMP_DEBUG) +!ELSE +CURL_LIBCURL_LIBNAME=$(LIB_NAME_IMP) +!ENDIF +!ENDIF + +CURL_FROM_LIBCURL=$(CURL_DIROBJ)\tool_hugehelp.obj \ + $(CURL_DIROBJ)\nonblock.obj \ + $(CURL_DIROBJ)\strcase.obj \ + $(CURL_DIROBJ)\strtoofft.obj \ + $(CURL_DIROBJ)\warnless.obj + +$(PROGRAM_NAME): $(CURL_DIROBJ) $(CURL_FROM_LIBCURL) $(EXE_OBJS) + $(CURL_LINK) $(CURL_LFLAGS) $(CURL_LIBCURL_LIBNAME) $(WIN_LIBS) $(CURL_FROM_LIBCURL) $(EXE_OBJS) + $(MANIFESTTOOL) + +{$(CURL_SRC_DIR)\}.c{$(CURL_DIROBJ)\}.obj: + $(CURL_CC) $(CURL_CFLAGS) /Fo"$@" $< + +$(CURL_DIROBJ)\tool_hugehelp.obj: $(CURL_SRC_DIR)\tool_hugehelp.c + $(CURL_CC) $(CURL_CFLAGS) /Zm200 /Fo"$@" $(CURL_SRC_DIR)\tool_hugehelp.c +$(CURL_DIROBJ)\nonblock.obj: ../lib/nonblock.c + $(CURL_CC) $(CURL_CFLAGS) /Fo"$@" ../lib/nonblock.c +$(CURL_DIROBJ)\strcase.obj: ../lib/strcase.c + $(CURL_CC) $(CURL_CFLAGS) /Fo"$@" ../lib/strcase.c +$(CURL_DIROBJ)\strtoofft.obj: ../lib/strtoofft.c + $(CURL_CC) $(CURL_CFLAGS) /Fo"$@" ../lib/strtoofft.c +$(CURL_DIROBJ)\warnless.obj: ../lib/warnless.c + $(CURL_CC) $(CURL_CFLAGS) /Fo"$@" ../lib/warnless.c +$(CURL_DIROBJ)\curl.res: $(CURL_SRC_DIR)\curl.rc + rc $(CURL_RC_FLAGS) + +!ENDIF # End of case where a config was provided. diff --git a/deps/curl-7.51.0/winbuild/gen_resp_file.bat b/deps/curl-7.51.0/winbuild/gen_resp_file.bat new file mode 100644 index 0000000000..434f36963f --- /dev/null +++ b/deps/curl-7.51.0/winbuild/gen_resp_file.bat @@ -0,0 +1,6 @@ +@echo OFF +@del %OUTFILE% +@echo %MACRO_NAME% = \> %OUTFILE% +@for %%i in (%*) do @echo %DIROBJ%/%%i \>> %OUTFILE% +@echo. >> %OUTFILE% +:END diff --git a/deps/etc2comp/AUTHORS b/deps/etc2comp/AUTHORS new file mode 100644 index 0000000000..e78a7f4d21 --- /dev/null +++ b/deps/etc2comp/AUTHORS @@ -0,0 +1,7 @@ +# This is the list of Etc2Comp authors for copyright purposes. +# +# This does not necessarily list everyone who has contributed code, since in +# some cases, their employer may be the copyright holder. To see the full list +# of contributors, see the revision history in source control. +Google Inc. +Blue Shift Inc. diff --git a/deps/etc2comp/CMakeLists.txt b/deps/etc2comp/CMakeLists.txt new file mode 100644 index 0000000000..3da27b49d3 --- /dev/null +++ b/deps/etc2comp/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright 2015 Etc2Comp Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 2.8.9) +project(EtcTest) + +set (CMAKE_CXX_STANDARD 11) +IF (APPLE) + set (CMAKE_CXX_FLAGS "-I/usr/include/i386-linux-gnu/c++/4.8 -I/usr/include/c++/4.8 -std=c++11 -g3 -Wall -O3") +ELSE () + IF (WIN32) + set (CMAKE_CXX_FLAGS "-I/usr/include/i386-linux-gnu/c++/4.8 -I/usr/include/c++/4.8 -W4 /EHsc") + ELSE() + set (CMAKE_CXX_FLAGS "-I/usr/include/i386-linux-gnu/c++/4.8 -I/usr/include/c++/4.8 -std=c++11 -pthread -g3 -Wall -O2") + ENDIF() +ENDIF () +ADD_SUBDIRECTORY(EtcLib) +ADD_SUBDIRECTORY(EtcTool) diff --git a/deps/etc2comp/CONTRIBUTING.md b/deps/etc2comp/CONTRIBUTING.md new file mode 100644 index 0000000000..273adeaf0e --- /dev/null +++ b/deps/etc2comp/CONTRIBUTING.md @@ -0,0 +1,48 @@ +# Contributing | Etc2Comp + +Thank you for contributing to the Etc2Comp community! + + - [Have a usage question?](#question) + - [Think you found a bug?](#issue) + - [Have a feature request?](#feature) + - [Want to submit a pull request?](#submit) + - [Small print](#smallprint) + +## Have a usage question? + + - Review the README.md to make sure you're building the binary correctly. + - Execute the binary with -h to show the usage help. + - Search through [old issues](https://github.com/google/etc2comp/issues) + for an answer to your question. + - If you still haven't found an answer to your question, [open a new issue](https://github.com/google/etc2comp/issues/new). +Please use the provided bug report template and include a minimal repro. + +## Think you found a bug? + +The library is experimental so that's highly likely. Follow the same +procedure above for questions. If you are up to the challenge, +[submit a Pull Request](#submit) with a fix! + +## Have a feature request? + +Great! Make sure the feature request isn't already listed in +[existing issues](https://github.com/google/etc2comp/issues), +then go ahead and [open a new issue](https://github.com/google/etc2comp/issues/new). +Remove the default template information and specify what you are requesting +technically, as well as, specifying what use cases it supports. + +## Want to submit a pull request? + +Sweet, we'd love to accept your contribution! [Open a new pull request](https://github.com/google/etc2comp/compare). + +If you want to implement a new feature, please open an issue with a +proposal first to discuss the change. + +You will need to sign our [Contributor License Agreement](https://cla.developers.google.com/about/google-individual) +before we can accept your pull request. + +## The small print +Contributions made by corporations are covered by a different +agreement than the one above, the +[Software Grant and Corporate Contributor License Agreement] +(https://cla.developers.google.com/about/google-corporate). \ No newline at end of file diff --git a/deps/etc2comp/EtcLib/CMakeLists.txt b/deps/etc2comp/EtcLib/CMakeLists.txt new file mode 100644 index 0000000000..e5701f535d --- /dev/null +++ b/deps/etc2comp/EtcLib/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright 2015 The Etc2Comp Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +project(EtcLib) +include_directories(./Etc) +include_directories(./EtcCodec) + +file(GLOB SOURCES + ${PROJECT_SOURCE_DIR}/Etc/*.h + ${PROJECT_SOURCE_DIR}/EtcCodec/*.h + ${PROJECT_SOURCE_DIR}/Etc/*.cpp + ${PROJECT_SOURCE_DIR}/EtcCodec/*.cpp) +ADD_LIBRARY(EtcLib ${SOURCES}) diff --git a/deps/etc2comp/EtcLib/Etc/Etc.cpp b/deps/etc2comp/EtcLib/Etc/Etc.cpp new file mode 100644 index 0000000000..a5ee706048 --- /dev/null +++ b/deps/etc2comp/EtcLib/Etc/Etc.cpp @@ -0,0 +1,128 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EtcConfig.h" +#include "Etc.h" +#include "EtcFilter.h" + +#include + +namespace Etc +{ + // ---------------------------------------------------------------------------------------------------- + // C-style inteface to the encoder + // + void Encode(float *a_pafSourceRGBA, + unsigned int a_uiSourceWidth, + unsigned int a_uiSourceHeight, + Image::Format a_format, + ErrorMetric a_eErrMetric, + float a_fEffort, + unsigned int a_uiJobs, + unsigned int a_uiMaxJobs, + unsigned char **a_ppaucEncodingBits, + unsigned int *a_puiEncodingBitsBytes, + unsigned int *a_puiExtendedWidth, + unsigned int *a_puiExtendedHeight, + int *a_piEncodingTime_ms, bool a_bVerboseOutput) + { + + Image image(a_pafSourceRGBA, a_uiSourceWidth, + a_uiSourceHeight, + a_eErrMetric); + image.m_bVerboseOutput = a_bVerboseOutput; + image.Encode(a_format, a_eErrMetric, a_fEffort, a_uiJobs, a_uiMaxJobs); + + *a_ppaucEncodingBits = image.GetEncodingBits(); + *a_puiEncodingBitsBytes = image.GetEncodingBitsBytes(); + *a_puiExtendedWidth = image.GetExtendedWidth(); + *a_puiExtendedHeight = image.GetExtendedHeight(); + *a_piEncodingTime_ms = image.GetEncodingTimeMs(); + } + + void EncodeMipmaps(float *a_pafSourceRGBA, + unsigned int a_uiSourceWidth, + unsigned int a_uiSourceHeight, + Image::Format a_format, + ErrorMetric a_eErrMetric, + float a_fEffort, + unsigned int a_uiJobs, + unsigned int a_uiMaxJobs, + unsigned int a_uiMaxMipmaps, + unsigned int a_uiMipFilterFlags, + RawImage* a_pMipmapImages, + int *a_piEncodingTime_ms, + bool a_bVerboseOutput) + { + auto mipWidth = a_uiSourceWidth; + auto mipHeight = a_uiSourceHeight; + int totalEncodingTime = 0; + for(unsigned int mip = 0; mip < a_uiMaxMipmaps && mipWidth >= 1 && mipHeight >= 1; mip++) + { + float* pImageData = nullptr; + float* pMipImage = nullptr; + + if(mip == 0) + { + pImageData = a_pafSourceRGBA; + } + else + { + pMipImage = new float[mipWidth*mipHeight*4]; + if(FilterTwoPass(a_pafSourceRGBA, a_uiSourceWidth, a_uiSourceHeight, pMipImage, mipWidth, mipHeight, a_uiMipFilterFlags, Etc::FilterLanczos3) ) + { + pImageData = pMipImage; + } + } + + if ( pImageData ) + { + + Image image(pImageData, mipWidth, mipHeight, a_eErrMetric); + + image.m_bVerboseOutput = a_bVerboseOutput; + image.Encode(a_format, a_eErrMetric, a_fEffort, a_uiJobs, a_uiMaxJobs); + + a_pMipmapImages[mip].paucEncodingBits = std::shared_ptr(image.GetEncodingBits(), [](unsigned char *p) { delete[] p; }); + a_pMipmapImages[mip].uiEncodingBitsBytes = image.GetEncodingBitsBytes(); + a_pMipmapImages[mip].uiExtendedWidth = image.GetExtendedWidth(); + a_pMipmapImages[mip].uiExtendedHeight = image.GetExtendedHeight(); + + totalEncodingTime += image.GetEncodingTimeMs(); + } + + if(pMipImage) + { + delete[] pMipImage; + } + + if (!pImageData) + { + break; + } + + mipWidth >>= 1; + mipHeight >>= 1; + } + + *a_piEncodingTime_ms = totalEncodingTime; + } + + + // ---------------------------------------------------------------------------------------------------- + // + +} diff --git a/deps/etc2comp/EtcLib/Etc/Etc.h b/deps/etc2comp/EtcLib/Etc/Etc.h new file mode 100644 index 0000000000..439388d649 --- /dev/null +++ b/deps/etc2comp/EtcLib/Etc/Etc.h @@ -0,0 +1,71 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcConfig.h" +#include "EtcImage.h" +#include "EtcColor.h" +#include "EtcErrorMetric.h" +#include + +#define ETCCOMP_MIN_EFFORT_LEVEL (0.0f) +#define ETCCOMP_DEFAULT_EFFORT_LEVEL (40.0f) +#define ETCCOMP_MAX_EFFORT_LEVEL (100.0f) + +namespace Etc +{ + class Block4x4EncodingBits; + + struct RawImage + { + int uiExtendedWidth; + int uiExtendedHeight; + unsigned int uiEncodingBitsBytes; + std::shared_ptr paucEncodingBits; + }; + + + + // C-style inteface to the encoder + void Encode(float *a_pafSourceRGBA, + unsigned int a_uiSourceWidth, + unsigned int a_uiSourceHeight, + Image::Format a_format, + ErrorMetric a_eErrMetric, + float a_fEffort, + unsigned int a_uiJobs, + unsigned int a_uimaxJobs, + unsigned char **a_ppaucEncodingBits, + unsigned int *a_puiEncodingBitsBytes, + unsigned int *a_puiExtendedWidth, + unsigned int *a_puiExtendedHeight, + int *a_piEncodingTime_ms, bool a_bVerboseOutput = false); + + void EncodeMipmaps(float *a_pafSourceRGBA, + unsigned int a_uiSourceWidth, + unsigned int a_uiSourceHeight, + Image::Format a_format, + ErrorMetric a_eErrMetric, + float a_fEffort, + unsigned int a_uiJobs, + unsigned int a_uiMaxJobs, + unsigned int a_uiMaxMipmaps, + unsigned int a_uiMipFilterFlags, + RawImage* a_pMipmaps, + int *a_piEncodingTime_ms, bool a_bVerboseOutput = false); + +} diff --git a/deps/etc2comp/EtcLib/Etc/EtcColor.h b/deps/etc2comp/EtcLib/Etc/EtcColor.h new file mode 100644 index 0000000000..7ceae05b65 --- /dev/null +++ b/deps/etc2comp/EtcLib/Etc/EtcColor.h @@ -0,0 +1,64 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace Etc +{ + + inline float LogToLinear(float a_fLog) + { + static const float ALPHA = 0.055f; + static const float ONE_PLUS_ALPHA = 1.0f + ALPHA; + + if (a_fLog <= 0.04045f) + { + return a_fLog / 12.92f; + } + else + { + return powf((a_fLog + ALPHA) / ONE_PLUS_ALPHA, 2.4f); + } + } + + inline float LinearToLog(float &a_fLinear) + { + static const float ALPHA = 0.055f; + static const float ONE_PLUS_ALPHA = 1.0f + ALPHA; + + if (a_fLinear <= 0.0031308f) + { + return 12.92f * a_fLinear; + } + else + { + return ONE_PLUS_ALPHA * powf(a_fLinear, (1.0f/2.4f)) - ALPHA; + } + } + + class ColorR8G8B8A8 + { + public: + + unsigned char ucR; + unsigned char ucG; + unsigned char ucB; + unsigned char ucA; + + }; +} diff --git a/deps/etc2comp/EtcLib/Etc/EtcColorFloatRGBA.h b/deps/etc2comp/EtcLib/Etc/EtcColorFloatRGBA.h new file mode 100644 index 0000000000..f2ca2c1f71 --- /dev/null +++ b/deps/etc2comp/EtcLib/Etc/EtcColorFloatRGBA.h @@ -0,0 +1,321 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcConfig.h" +#include "EtcColor.h" + +#include + +namespace Etc +{ + + class ColorFloatRGBA + { + public: + + ColorFloatRGBA(void) + { + fR = fG = fB = fA = 0.0f; + } + + ColorFloatRGBA(float a_fR, float a_fG, float a_fB, float a_fA) + { + fR = a_fR; + fG = a_fG; + fB = a_fB; + fA = a_fA; + } + + inline ColorFloatRGBA operator+(ColorFloatRGBA& a_rfrgba) + { + ColorFloatRGBA frgba; + frgba.fR = fR + a_rfrgba.fR; + frgba.fG = fG + a_rfrgba.fG; + frgba.fB = fB + a_rfrgba.fB; + frgba.fA = fA + a_rfrgba.fA; + return frgba; + } + + inline ColorFloatRGBA operator+(float a_f) + { + ColorFloatRGBA frgba; + frgba.fR = fR + a_f; + frgba.fG = fG + a_f; + frgba.fB = fB + a_f; + frgba.fA = fA; + return frgba; + } + + inline ColorFloatRGBA operator-(float a_f) + { + ColorFloatRGBA frgba; + frgba.fR = fR - a_f; + frgba.fG = fG - a_f; + frgba.fB = fB - a_f; + frgba.fA = fA; + return frgba; + } + + inline ColorFloatRGBA operator-(ColorFloatRGBA& a_rfrgba) + { + ColorFloatRGBA frgba; + frgba.fR = fR - a_rfrgba.fR; + frgba.fG = fG - a_rfrgba.fG; + frgba.fB = fB - a_rfrgba.fB; + frgba.fA = fA - a_rfrgba.fA; + return frgba; + } + + inline ColorFloatRGBA operator*(float a_f) + { + ColorFloatRGBA frgba; + frgba.fR = fR * a_f; + frgba.fG = fG * a_f; + frgba.fB = fB * a_f; + frgba.fA = fA; + + return frgba; + } + + inline ColorFloatRGBA ScaleRGB(float a_f) + { + ColorFloatRGBA frgba; + frgba.fR = a_f * fR; + frgba.fG = a_f * fG; + frgba.fB = a_f * fB; + frgba.fA = fA; + + return frgba; + } + + inline ColorFloatRGBA RoundRGB(void) + { + ColorFloatRGBA frgba; + frgba.fR = roundf(fR); + frgba.fG = roundf(fG); + frgba.fB = roundf(fB); + + return frgba; + } + + inline ColorFloatRGBA ToLinear() + { + ColorFloatRGBA frgbaLinear; + frgbaLinear.fR = LogToLinear(fR); + frgbaLinear.fG = LogToLinear(fG); + frgbaLinear.fB = LogToLinear(fB); + frgbaLinear.fA = fA; + + return frgbaLinear; + } + + inline ColorFloatRGBA ToLog(void) + { + ColorFloatRGBA frgbaLog; + frgbaLog.fR = LinearToLog(fR); + frgbaLog.fG = LinearToLog(fG); + frgbaLog.fB = LinearToLog(fB); + frgbaLog.fA = fA; + + return frgbaLog; + } + + inline static ColorFloatRGBA ConvertFromRGBA8(unsigned char a_ucR, + unsigned char a_ucG, unsigned char a_ucB, unsigned char a_ucA) + { + ColorFloatRGBA frgba; + + frgba.fR = (float)a_ucR / 255.0f; + frgba.fG = (float)a_ucG / 255.0f; + frgba.fB = (float)a_ucB / 255.0f; + frgba.fA = (float)a_ucA / 255.0f; + + return frgba; + } + + inline static ColorFloatRGBA ConvertFromRGB4(unsigned char a_ucR4, + unsigned char a_ucG4, + unsigned char a_ucB4) + { + ColorFloatRGBA frgba; + + unsigned char ucR8 = (unsigned char)((a_ucR4 << 4) + a_ucR4); + unsigned char ucG8 = (unsigned char)((a_ucG4 << 4) + a_ucG4); + unsigned char ucB8 = (unsigned char)((a_ucB4 << 4) + a_ucB4); + + frgba.fR = (float)ucR8 / 255.0f; + frgba.fG = (float)ucG8 / 255.0f; + frgba.fB = (float)ucB8 / 255.0f; + frgba.fA = 1.0f; + + return frgba; + } + + inline static ColorFloatRGBA ConvertFromRGB5(unsigned char a_ucR5, + unsigned char a_ucG5, + unsigned char a_ucB5) + { + ColorFloatRGBA frgba; + + unsigned char ucR8 = (unsigned char)((a_ucR5 << 3) + (a_ucR5 >> 2)); + unsigned char ucG8 = (unsigned char)((a_ucG5 << 3) + (a_ucG5 >> 2)); + unsigned char ucB8 = (unsigned char)((a_ucB5 << 3) + (a_ucB5 >> 2)); + + frgba.fR = (float)ucR8 / 255.0f; + frgba.fG = (float)ucG8 / 255.0f; + frgba.fB = (float)ucB8 / 255.0f; + frgba.fA = 1.0f; + + return frgba; + } + + inline static ColorFloatRGBA ConvertFromR6G7B6(unsigned char a_ucR6, + unsigned char a_ucG7, + unsigned char a_ucB6) + { + ColorFloatRGBA frgba; + + unsigned char ucR8 = (unsigned char)((a_ucR6 << 2) + (a_ucR6 >> 4)); + unsigned char ucG8 = (unsigned char)((a_ucG7 << 1) + (a_ucG7 >> 6)); + unsigned char ucB8 = (unsigned char)((a_ucB6 << 2) + (a_ucB6 >> 4)); + + frgba.fR = (float)ucR8 / 255.0f; + frgba.fG = (float)ucG8 / 255.0f; + frgba.fB = (float)ucB8 / 255.0f; + frgba.fA = 1.0f; + + return frgba; + } + + // quantize to 4 bits, expand to 8 bits + inline ColorFloatRGBA QuantizeR4G4B4(void) const + { + ColorFloatRGBA frgba = *this; + + // quantize to 4 bits + frgba = frgba.ClampRGB().ScaleRGB(15.0f).RoundRGB(); + unsigned int uiR4 = (unsigned int)frgba.fR; + unsigned int uiG4 = (unsigned int)frgba.fG; + unsigned int uiB4 = (unsigned int)frgba.fB; + + // expand to 8 bits + frgba.fR = (float) ((uiR4 << 4) + uiR4); + frgba.fG = (float) ((uiG4 << 4) + uiG4); + frgba.fB = (float) ((uiB4 << 4) + uiB4); + + frgba = frgba.ScaleRGB(1.0f/255.0f); + + return frgba; + } + + // quantize to 5 bits, expand to 8 bits + inline ColorFloatRGBA QuantizeR5G5B5(void) const + { + ColorFloatRGBA frgba = *this; + + // quantize to 5 bits + frgba = frgba.ClampRGB().ScaleRGB(31.0f).RoundRGB(); + unsigned int uiR5 = (unsigned int)frgba.fR; + unsigned int uiG5 = (unsigned int)frgba.fG; + unsigned int uiB5 = (unsigned int)frgba.fB; + + // expand to 8 bits + frgba.fR = (float)((uiR5 << 3) + (uiR5 >> 2)); + frgba.fG = (float)((uiG5 << 3) + (uiG5 >> 2)); + frgba.fB = (float)((uiB5 << 3) + (uiB5 >> 2)); + + frgba = frgba.ScaleRGB(1.0f / 255.0f); + + return frgba; + } + + // quantize to 6/7/6 bits, expand to 8 bits + inline ColorFloatRGBA QuantizeR6G7B6(void) const + { + ColorFloatRGBA frgba = *this; + + // quantize to 6/7/6 bits + ColorFloatRGBA frgba6 = frgba.ClampRGB().ScaleRGB(63.0f).RoundRGB(); + ColorFloatRGBA frgba7 = frgba.ClampRGB().ScaleRGB(127.0f).RoundRGB(); + unsigned int uiR6 = (unsigned int)frgba6.fR; + unsigned int uiG7 = (unsigned int)frgba7.fG; + unsigned int uiB6 = (unsigned int)frgba6.fB; + + // expand to 8 bits + frgba.fR = (float)((uiR6 << 2) + (uiR6 >> 4)); + frgba.fG = (float)((uiG7 << 1) + (uiG7 >> 6)); + frgba.fB = (float)((uiB6 << 2) + (uiB6 >> 4)); + + frgba = frgba.ScaleRGB(1.0f / 255.0f); + + return frgba; + } + + inline ColorFloatRGBA ClampRGB(void) + { + ColorFloatRGBA frgba = *this; + if (frgba.fR < 0.0f) { frgba.fR = 0.0f; } + if (frgba.fR > 1.0f) { frgba.fR = 1.0f; } + if (frgba.fG < 0.0f) { frgba.fG = 0.0f; } + if (frgba.fG > 1.0f) { frgba.fG = 1.0f; } + if (frgba.fB < 0.0f) { frgba.fB = 0.0f; } + if (frgba.fB > 1.0f) { frgba.fB = 1.0f; } + + return frgba; + } + + inline ColorFloatRGBA ClampRGBA(void) + { + ColorFloatRGBA frgba = *this; + if (frgba.fR < 0.0f) { frgba.fR = 0.0f; } + if (frgba.fR > 1.0f) { frgba.fR = 1.0f; } + if (frgba.fG < 0.0f) { frgba.fG = 0.0f; } + if (frgba.fG > 1.0f) { frgba.fG = 1.0f; } + if (frgba.fB < 0.0f) { frgba.fB = 0.0f; } + if (frgba.fB > 1.0f) { frgba.fB = 1.0f; } + if (frgba.fA < 0.0f) { frgba.fA = 0.0f; } + if (frgba.fA > 1.0f) { frgba.fA = 1.0f; } + + return frgba; + } + + inline int IntRed(float a_fScale) + { + return (int)roundf(fR * a_fScale); + } + + inline int IntGreen(float a_fScale) + { + return (int)roundf(fG * a_fScale); + } + + inline int IntBlue(float a_fScale) + { + return (int)roundf(fB * a_fScale); + } + + inline int IntAlpha(float a_fScale) + { + return (int)roundf(fA * a_fScale); + } + + float fR, fG, fB, fA; + }; + +} + diff --git a/deps/etc2comp/EtcLib/Etc/EtcConfig.h b/deps/etc2comp/EtcLib/Etc/EtcConfig.h new file mode 100644 index 0000000000..2e76379102 --- /dev/null +++ b/deps/etc2comp/EtcLib/Etc/EtcConfig.h @@ -0,0 +1,67 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef _WIN32 +#define ETC_WINDOWS (1) +#else +#define ETC_WINDOWS (0) +#endif + +#if __APPLE__ +#define ETC_OSX (1) +#else +#define ETC_OSX (0) +#endif + +#if __unix__ +#define ETC_UNIX (1) +#else +#define ETC_UNIX (0) +#endif + + +// short names for common types +#include +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef float f32; +typedef double f64; + +// Keep asserts enabled in release builds during development +#undef NDEBUG + +// 0=disable. stb_image can be used if you need to compress +//other image formats like jpg +#define USE_STB_IMAGE_LOAD 0 + +#if ETC_WINDOWS +#include +#define _CRT_SECURE_NO_WARNINGS (1) +#include +#endif + +#include + diff --git a/deps/etc2comp/EtcLib/Etc/EtcFilter.cpp b/deps/etc2comp/EtcLib/Etc/EtcFilter.cpp new file mode 100644 index 0000000000..bc899a533e --- /dev/null +++ b/deps/etc2comp/EtcLib/Etc/EtcFilter.cpp @@ -0,0 +1,401 @@ +#include +#include +#include "EtcFilter.h" + + +namespace Etc +{ + +static const double PiConst = 3.14159265358979323846; + +inline double sinc(double x) +{ + if ( x == 0.0 ) + { + return 1.0; + } + + return sin(PiConst * x) / (PiConst * x); +} + +//inline float sincf( float x ) +//{ +// x *= F_PI; +// if (x < 0.01f && x > -0.01f) +// { +// return 1.0f + x*x*(-1.0f/6.0f + x*x*1.0f/120.0f); +// } +// +// return sinf(x)/x; +//} +// +//double bessel0(double x) +//{ +// const double EPSILON_RATIO = 1E-16; +// double xh, sum, pow, ds; +// int k; +// +// xh = 0.5 * x; +// sum = 1.0; +// pow = 1.0; +// k = 0; +// ds = 1.0; +// while (ds > sum * EPSILON_RATIO) +// { +// ++k; +// pow = pow * (xh / k); +// ds = pow * pow; +// sum = sum + ds; +// } +// +// return sum; +//} + +//**-------------------------------------------------------------------------- +//** Name: kaiser(double alpha, double half_width, double x) +//** Returns: +//** Description: Alpha controls shape of filter. We are using 4. +//**-------------------------------------------------------------------------- +//inline double kaiser(double alpha, double half_width, double x) +//{ +// double ratio = (x / half_width); +// return bessel0(alpha * sqrt(1 - ratio * ratio)) / bessel0(alpha); +//} +// +//float Filter_Lanczos4Sinc(float x) +//{ +// if (x <= -4.0f || x >= 4.0f) // half-width of 4 +// { +// return 0.0; +// } +// +// return sinc(0.875f * x) * sinc(0.25f * x); +//} +// +//double Filter_Kaiser4( double t ) +//{ +// return kaiser( 4.0, 3.0, t); +//} +// +//double Filter_KaiserOptimal( double t ) +//{ +// return kaiser( 8.93, 3.0f, t); +//} + +double FilterLanczos3( double t ) +{ + if ( t <= -3.0 || t >= 3.0 ) + { + return 0.0; + } + + return sinc( t ) * sinc( t / 3.0 ); +} + +double FilterBox( double t ) +{ + return ( t > -0.5 && t < 0.5) ? 1.0 : 0.0; +} + +double FilterLinear( double t ) +{ + if (t < 0.0) t = -t; + + return (t < 1.0) ? (1.0 - t) : 0.0; +} + + +//**-------------------------------------------------------------------------- +//** Name: CalcContributions( int srcSize, +//** int destSize, +//** double filterSize, +//** bool wrap, +//** double (*FilterProc)(double), +//** FilterWeights contrib[] ) +//** Returns: void +//** Description: +//**-------------------------------------------------------------------------- +void CalcContributions( int srcSize, int destSize, double filterSize, bool wrap, double (*FilterProc)(double), FilterWeights contrib[] ) +{ + double scale; + double filterScale; + double center; + double totalWeight; + double weight; + int iRight; + int iLeft; + int iDest; + + scale = (double)destSize / srcSize; + if ( scale < 1.0 ) + { + filterSize = filterSize / scale; + filterScale = scale; + } + else + { + filterScale = 1.0; + } + + if ( filterSize > (double)MaxFilterSize ) + { + filterSize = (double)MaxFilterSize; + } + + for ( iDest = 0; iDest < destSize; ++iDest ) + { + center = (double)iDest / scale; + + iLeft = (int)ceil(center - filterSize); + iRight = (int)floor(center + filterSize); + + if ( !wrap ) + { + if ( iLeft < 0 ) + { + iLeft = 0; + } + + if ( iRight >= srcSize ) + { + iRight = srcSize - 1; + } + } + + int numWeights = iRight - iLeft + 1; + + contrib[iDest].first = iLeft; + contrib[iDest].numWeights = numWeights; + + totalWeight = 0; + double t = ((double)iLeft - center) * filterScale; + for (int i = 0; i < numWeights; i++) + { + weight = (*FilterProc)(t) * filterScale; + totalWeight += weight; + contrib[iDest].weight[i] = weight; + t += filterScale; + } + + //**-------------------------------------------------------- + //** Normalize weights by dividing by the sum of the weights + //**-------------------------------------------------------- + if ( totalWeight > 0.0 ) + { + for ( int i = 0; i < numWeights; i++) + { + contrib[iDest].weight[i] /= totalWeight; + } + } + } +} + +//**------------------------------------------------------------------------- +//** Name: Filter_TwoPass( RGBCOLOR *pSrcImage, +//** int srcWidth, int srcHeight, +//** RGBCOLOR *pDestImage, +//** int destWidth, int destHeight, +//** double (*FilterProc)(double) ) +//** Returns: 0 on failure and 1 on success +//** Description: Filters a 2d image with a two pass filter by averaging the +//** weighted contributions of the pixels within the filter region. The +//** contributions are determined by a weighting function parameter. +//**------------------------------------------------------------------------- +int FilterTwoPass( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight, + RGBCOLOR *pDestImage, int destWidth, int destHeight, unsigned int wrapFlags, double (*FilterProc)(double) ) +{ + FilterWeights *contrib; + RGBCOLOR *pPixel; + RGBCOLOR *pSrcPixel; + RGBCOLOR *pTempImage; + int iRow; + int iCol; + int iSrcCol; + int iSrcRow; + int iWeight; + double dRed; + double dGreen; + double dBlue; + double dAlpha; + double filterSize = 3.0; + + int maxDim = (srcWidth>srcHeight)?srcWidth:srcHeight; + contrib = (FilterWeights*)malloc(maxDim * sizeof(FilterWeights)); + + //**------------------------------------------------------------------------ + //** Need to create a temporary image to stuff the horizontally scaled image + //**------------------------------------------------------------------------ + pTempImage = (RGBCOLOR *)malloc( destWidth * srcHeight * sizeof(RGBCOLOR) ); + if ( pTempImage == NULL ) + { + return 0; + } + + //**------------------------------------------------------- + //** Horizontally filter the image into the temporary image + //**------------------------------------------------------- + bool bWrapHorizontal = !!(wrapFlags&FILTER_WRAP_X); + CalcContributions( srcWidth, destWidth, filterSize, bWrapHorizontal, FilterProc, contrib ); + for ( iRow = 0; iRow < srcHeight; iRow++ ) + { + for ( iCol = 0; iCol < destWidth; iCol++ ) + { + dRed = 0; + dGreen = 0; + dBlue = 0; + dAlpha = 0; + + for ( iWeight = 0; iWeight < contrib[iCol].numWeights; iWeight++ ) + { + iSrcCol = iWeight + contrib[iCol].first; + if (bWrapHorizontal) + { + iSrcCol = (iSrcCol < 0) ? (srcWidth + iSrcCol) : (iSrcCol >= srcWidth) ? (iSrcCol - srcWidth) : iSrcCol; + } + pSrcPixel = pSrcImage + (iRow * srcWidth) + iSrcCol; + dRed += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[0]; + dGreen += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[1]; + dBlue += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[2]; + dAlpha += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[3]; + } + + pPixel = pTempImage + (iRow * destWidth) + iCol; + pPixel->rgba[0] = static_cast(std::max(0.0, std::min(255.0, dRed))); + pPixel->rgba[1] = static_cast(std::max(0.0, std::min(255.0, dGreen))); + pPixel->rgba[2] = static_cast(std::max(0.0, std::min(255.0, dBlue))); + pPixel->rgba[3] = static_cast(std::max(0.0, std::min(255.0, dAlpha))); + } + } + + //**------------------------------------------------------- + //** Vertically filter the image into the destination image + //**------------------------------------------------------- + bool bWrapVertical = !!(wrapFlags&FILTER_WRAP_Y); + CalcContributions(srcHeight, destHeight, filterSize, bWrapVertical, FilterProc, contrib); + for ( iCol = 0; iCol < destWidth; iCol++ ) + { + for ( iRow = 0; iRow < destHeight; iRow++ ) + { + dRed = 0; + dGreen = 0; + dBlue = 0; + dAlpha = 0; + + for ( iWeight = 0; iWeight < contrib[iRow].numWeights; iWeight++ ) + { + iSrcRow = iWeight + contrib[iRow].first; + if (bWrapVertical) + { + iSrcRow = (iSrcRow < 0) ? (srcHeight + iSrcRow) : (iSrcRow >= srcHeight) ? (iSrcRow - srcHeight) : iSrcRow; + } + pSrcPixel = pTempImage + (iSrcRow * destWidth) + iCol; + dRed += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[0]; + dGreen += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[1]; + dBlue += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[2]; + dAlpha += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[3]; + } + + pPixel = pDestImage + (iRow * destWidth) + iCol; + pPixel->rgba[0] = (unsigned char)(std::max( 0.0, std::min( 255.0, dRed))); + pPixel->rgba[1] = (unsigned char)(std::max( 0.0, std::min( 255.0, dGreen))); + pPixel->rgba[2] = (unsigned char)(std::max( 0.0, std::min( 255.0, dBlue))); + pPixel->rgba[3] = (unsigned char)(std::max( 0.0, std::min( 255.0, dAlpha))); + } + } + + free( pTempImage ); + free( contrib ); + + return 1; +} + +//**------------------------------------------------------------------------- +//** Name: FilterResample(RGBCOLOR *pSrcImage, int srcWidth, int srcHeight, +//** RGBCOLOR *pDstImage, int dstWidth, int dstHeight) +//** Returns: 1 +//** Description: This function runs a 2d box filter over the srouce image +//** to produce the destination image. +//**------------------------------------------------------------------------- +void FilterResample( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight, + RGBCOLOR *pDstImage, int dstWidth, int dstHeight ) +{ + int iRow; + int iCol; + int iSampleRow; + int iSampleCol; + int iFirstSampleRow; + int iFirstSampleCol; + int iLastSampleRow; + int iLastSampleCol; + int red; + int green; + int blue; + int alpha; + int samples; + float xScale; + float yScale; + + RGBCOLOR *pSrcPixel; + RGBCOLOR *pDstPixel; + + xScale = (float)srcWidth / dstWidth; + yScale = (float)srcHeight / dstHeight; + + for ( iRow = 0; iRow < dstHeight; iRow++ ) + { + for ( iCol = 0; iCol < dstWidth; iCol++ ) + { + iFirstSampleRow = (int)(iRow * yScale); + iLastSampleRow = (int)ceil(iFirstSampleRow + yScale - 1); + if ( iLastSampleRow >= srcHeight ) + { + iLastSampleRow = srcHeight - 1; + } + + iFirstSampleCol = (int)(iCol * xScale); + iLastSampleCol = (int)ceil(iFirstSampleCol + xScale - 1); + if ( iLastSampleCol >= srcWidth ) + { + iLastSampleCol = srcWidth - 1; + } + + samples = 0; + red = 0; + green = 0; + blue = 0; + alpha = 0; + for ( iSampleRow = iFirstSampleRow; iSampleRow <= iLastSampleRow; iSampleRow++ ) + { + for ( iSampleCol = iFirstSampleCol; iSampleCol <= iLastSampleCol; iSampleCol++ ) + { + pSrcPixel = pSrcImage + iSampleRow * srcWidth + iSampleCol; + red += pSrcPixel->rgba[0]; + green += pSrcPixel->rgba[1]; + blue += pSrcPixel->rgba[2]; + alpha += pSrcPixel->rgba[3]; + + samples++; + } + } + + pDstPixel = pDstImage + iRow * dstWidth + iCol; + if ( samples > 0 ) + { + pDstPixel->rgba[0] = static_cast(red / samples); + pDstPixel->rgba[1] = static_cast(green / samples); + pDstPixel->rgba[2] = static_cast(blue / samples); + pDstPixel->rgba[3] = static_cast(alpha / samples); + } + else + { + pDstPixel->rgba[0] = static_cast(red); + pDstPixel->rgba[1] = static_cast(green); + pDstPixel->rgba[2] = static_cast(blue); + pDstPixel->rgba[3] = static_cast(alpha); + } + } + } +} + + +} \ No newline at end of file diff --git a/deps/etc2comp/EtcLib/Etc/EtcFilter.h b/deps/etc2comp/EtcLib/Etc/EtcFilter.h new file mode 100644 index 0000000000..fcf125c6df --- /dev/null +++ b/deps/etc2comp/EtcLib/Etc/EtcFilter.h @@ -0,0 +1,244 @@ +#pragma once +#include +#include + +namespace Etc +{ + +enum FilterEnums +{ + MaxFilterSize = 32 +}; + +enum WrapFlags +{ + FILTER_WRAP_NONE = 0, + FILTER_WRAP_X = 0x1, + FILTER_WRAP_Y = 0x2 +}; + +typedef struct tagFilterWeights +{ + int first; + int numWeights; + double weight[MaxFilterSize * 2 + 1]; +} FilterWeights; + +typedef struct tagRGBCOLOR +{ + union + { + uint32_t ulColor; + uint8_t rgba[4]; + }; +} RGBCOLOR; + + +double FilterBox( double t ); +double FilterLinear( double t ); +double FilterLanczos3( double t ); + +int FilterTwoPass( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight, + RGBCOLOR *pDestImage, int destWidth, int destHeight, unsigned int wrapFlags, double (*FilterProc)(double) ); +void FilterResample( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight, + RGBCOLOR *pDstImage, int dstWidth, int dstHeight ); + + +void CalcContributions(int srcSize, int destSize, double filterSize, bool wrap, double(*FilterProc)(double), FilterWeights contrib[]); + +template +void FilterResample(T *pSrcImage, int srcWidth, int srcHeight, T *pDstImage, int dstWidth, int dstHeight) +{ + float xScale; + float yScale; + + T *pSrcPixel; + T *pDstPixel; + + xScale = (float)srcWidth / dstWidth; + yScale = (float)srcHeight / dstHeight; + + for (int iRow = 0; iRow < dstHeight; iRow++) + { + for (int iCol = 0; iCol < dstWidth; iCol++) + { + int samples; + int iFirstSampleRow; + int iFirstSampleCol; + int iLastSampleRow; + int iLastSampleCol; + float red; + float green; + float blue; + float alpha; + + iFirstSampleRow = (int)(iRow * yScale); + iLastSampleRow = (int)ceil(iFirstSampleRow + yScale - 1); + if (iLastSampleRow >= srcHeight) + { + iLastSampleRow = srcHeight - 1; + } + + iFirstSampleCol = (int)(iCol * xScale); + iLastSampleCol = (int)ceil(iFirstSampleCol + xScale - 1); + if (iLastSampleCol >= srcWidth) + { + iLastSampleCol = srcWidth - 1; + } + + samples = 0; + red = 0.f; + green = 0.f; + blue = 0.f; + alpha = 0.f; + for (int iSampleRow = iFirstSampleRow; iSampleRow <= iLastSampleRow; iSampleRow++) + { + for (int iSampleCol = iFirstSampleCol; iSampleCol <= iLastSampleCol; iSampleCol++) + { + pSrcPixel = pSrcImage + (iSampleRow * srcWidth + iSampleCol) * 4; + red += static_cast(pSrcPixel[0]); + green += static_cast(pSrcPixel[1]); + blue += static_cast(pSrcPixel[2]); + alpha += static_cast(pSrcPixel[3]); + + samples++; + } + } + + pDstPixel = pDstImage + (iRow * dstWidth + iCol) * 4; + if (samples > 0) + { + pDstPixel[0] = static_cast(red / samples); + pDstPixel[1] = static_cast(green / samples); + pDstPixel[2] = static_cast(blue / samples); + pDstPixel[3] = static_cast(alpha / samples); + } + else + { + pDstPixel[0] = static_cast(red); + pDstPixel[1] = static_cast(green); + pDstPixel[2] = static_cast(blue); + pDstPixel[3] = static_cast(alpha); + } + } + } + +} + +//**------------------------------------------------------------------------- +//** Name: Filter_TwoPass( RGBCOLOR *pSrcImage, +//** int srcWidth, int srcHeight, +//** RGBCOLOR *pDestImage, +//** int destWidth, int destHeight, +//** double (*FilterProc)(double) ) +//** Returns: 0 on failure and 1 on success +//** Description: Filters a 2d image with a two pass filter by averaging the +//** weighted contributions of the pixels within the filter region. The +//** contributions are determined by a weighting function parameter. +//**------------------------------------------------------------------------- +template +int FilterTwoPass(T *pSrcImage, int srcWidth, int srcHeight, + T *pDestImage, int destWidth, int destHeight, unsigned int wrapFlags, double(*FilterProc)(double)) +{ + const int numComponents = 4; + FilterWeights *contrib; + T *pPixel; + T *pTempImage; + double dRed; + double dGreen; + double dBlue; + double dAlpha; + double filterSize = 3.0; + + int maxDim = (srcWidth>srcHeight) ? srcWidth : srcHeight; + contrib = new FilterWeights[maxDim]; + + //**------------------------------------------------------------------------ + //** Need to create a temporary image to stuff the horizontally scaled image + //**------------------------------------------------------------------------ + pTempImage = new T[destWidth * srcHeight * numComponents]; + if (pTempImage == NULL) + { + return 0; + } + + //**------------------------------------------------------- + //** Horizontally filter the image into the temporary image + //**------------------------------------------------------- + bool bWrapHorizontal = !!(wrapFlags&FILTER_WRAP_X); + CalcContributions(srcWidth, destWidth, filterSize, bWrapHorizontal, FilterProc, contrib); + for (int iRow = 0; iRow < srcHeight; iRow++) + { + for (int iCol = 0; iCol < destWidth; iCol++) + { + dRed = 0; + dGreen = 0; + dBlue = 0; + dAlpha = 0; + + for (int iWeight = 0; iWeight < contrib[iCol].numWeights; iWeight++) + { + int iSrcCol = iWeight + contrib[iCol].first; + if(bWrapHorizontal) + { + iSrcCol = (iSrcCol < 0)?(srcWidth+iSrcCol):(iSrcCol >= srcWidth)?(iSrcCol-srcWidth):iSrcCol; + } + T* pSrcPixel = pSrcImage + ((iRow * srcWidth) + iSrcCol)*numComponents; + dRed += contrib[iCol].weight[iWeight] * pSrcPixel[0]; + dGreen += contrib[iCol].weight[iWeight] * pSrcPixel[1]; + dBlue += contrib[iCol].weight[iWeight] * pSrcPixel[2]; + dAlpha += contrib[iCol].weight[iWeight] * pSrcPixel[3]; + } + + pPixel = pTempImage + ((iRow * destWidth) + iCol)*numComponents; + pPixel[0] = static_cast(std::max(0.0, std::min(255.0, dRed))); + pPixel[1] = static_cast(std::max(0.0, std::min(255.0, dGreen))); + pPixel[2] = static_cast(std::max(0.0, std::min(255.0, dBlue))); + pPixel[3] = static_cast(std::max(0.0, std::min(255.0, dAlpha))); + } + } + + //**------------------------------------------------------- + //** Vertically filter the image into the destination image + //**------------------------------------------------------- + bool bWrapVertical = !!(wrapFlags&FILTER_WRAP_Y); + CalcContributions(srcHeight, destHeight, filterSize, bWrapVertical, FilterProc, contrib); + for (int iCol = 0; iCol < destWidth; iCol++) + { + for (int iRow = 0; iRow < destHeight; iRow++) + { + dRed = 0; + dGreen = 0; + dBlue = 0; + dAlpha = 0; + + for (int iWeight = 0; iWeight < contrib[iRow].numWeights; iWeight++) + { + int iSrcRow = iWeight + contrib[iRow].first; + if (bWrapVertical) + { + iSrcRow = (iSrcRow < 0) ? (srcHeight + iSrcRow) : (iSrcRow >= srcHeight) ? (iSrcRow - srcHeight) : iSrcRow; + } + T* pSrcPixel = pTempImage + ((iSrcRow * destWidth) + iCol)*numComponents; + dRed += contrib[iRow].weight[iWeight] * pSrcPixel[0]; + dGreen += contrib[iRow].weight[iWeight] * pSrcPixel[1]; + dBlue += contrib[iRow].weight[iWeight] * pSrcPixel[2]; + dAlpha += contrib[iRow].weight[iWeight] * pSrcPixel[3]; + } + + pPixel = pDestImage + ((iRow * destWidth) + iCol)*numComponents; + pPixel[0] = static_cast(std::max(0.0, std::min(255.0, dRed))); + pPixel[1] = static_cast(std::max(0.0, std::min(255.0, dGreen))); + pPixel[2] = static_cast(std::max(0.0, std::min(255.0, dBlue))); + pPixel[3] = static_cast(std::max(0.0, std::min(255.0, dAlpha))); + } + } + + delete[] pTempImage; + delete[] contrib; + + return 1; +} + + +} \ No newline at end of file diff --git a/deps/etc2comp/EtcLib/Etc/EtcImage.cpp b/deps/etc2comp/EtcLib/Etc/EtcImage.cpp new file mode 100644 index 0000000000..1e8a930874 --- /dev/null +++ b/deps/etc2comp/EtcLib/Etc/EtcImage.cpp @@ -0,0 +1,685 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +EtcImage.cpp + +Image is an array of 4x4 blocks that represent the encoding of the source image + +*/ + +#include "EtcConfig.h" + +#include + +#include "EtcImage.h" + +#include "Etc.h" +#include "EtcBlock4x4.h" +#include "EtcBlock4x4EncodingBits.h" +#include "EtcSortedBlockList.h" + +#if ETC_WINDOWS +#include +#endif +#include +#include +#include +#include +#include +#include + +// fix conflict with Block4x4::AlphaMix +#ifdef OPAQUE +#undef OPAQUE +#endif +#ifdef TRANSPARENT +#undef TRANSPARENT +#endif + +namespace Etc +{ + + // ---------------------------------------------------------------------------------------------------- + // + Image::Image(void) + { + m_encodingStatus = EncodingStatus::SUCCESS; + m_warningsToCapture = EncodingStatus::SUCCESS; + m_pafrgbaSource = nullptr; + + m_pablock = nullptr; + + m_encodingbitsformat = Block4x4EncodingBits::Format::UNKNOWN; + m_uiEncodingBitsBytes = 0; + m_paucEncodingBits = nullptr; + + m_format = Format::UNKNOWN; + m_iNumOpaquePixels = 0; + m_iNumTranslucentPixels = 0; + m_iNumTransparentPixels = 0; + } + + // ---------------------------------------------------------------------------------------------------- + // constructor using source image + // used to set state before Encode() is called + // + Image::Image(float *a_pafSourceRGBA, unsigned int a_uiSourceWidth, + unsigned int a_uiSourceHeight, + ErrorMetric a_errormetric) + { + m_encodingStatus = EncodingStatus::SUCCESS; + m_warningsToCapture = EncodingStatus::SUCCESS; + m_pafrgbaSource = (ColorFloatRGBA *) a_pafSourceRGBA; + m_uiSourceWidth = a_uiSourceWidth; + m_uiSourceHeight = a_uiSourceHeight; + + m_uiExtendedWidth = CalcExtendedDimension((unsigned short)m_uiSourceWidth); + m_uiExtendedHeight = CalcExtendedDimension((unsigned short)m_uiSourceHeight); + + m_uiBlockColumns = m_uiExtendedWidth >> 2; + m_uiBlockRows = m_uiExtendedHeight >> 2; + + m_pablock = new Block4x4[GetNumberOfBlocks()]; + assert(m_pablock); + + m_format = Format::UNKNOWN; + + m_encodingbitsformat = Block4x4EncodingBits::Format::UNKNOWN; + m_uiEncodingBitsBytes = 0; + m_paucEncodingBits = nullptr; + + m_errormetric = a_errormetric; + m_fEffort = 0.0f; + + m_iEncodeTime_ms = -1; + + m_iNumOpaquePixels = 0; + m_iNumTranslucentPixels = 0; + m_iNumTransparentPixels = 0; + m_bVerboseOutput = false; + + } + + // ---------------------------------------------------------------------------------------------------- + // constructor using encoding bits + // recreates encoding state using a previously encoded image + // + Image::Image(Format a_format, + unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight, + unsigned char *a_paucEncidingBits, unsigned int a_uiEncodingBitsBytes, + Image *a_pimageSource, ErrorMetric a_errormetric) + { + m_encodingStatus = EncodingStatus::SUCCESS; + m_pafrgbaSource = nullptr; + m_uiSourceWidth = a_uiSourceWidth; + m_uiSourceHeight = a_uiSourceHeight; + + m_uiExtendedWidth = CalcExtendedDimension((unsigned short)m_uiSourceWidth); + m_uiExtendedHeight = CalcExtendedDimension((unsigned short)m_uiSourceHeight); + + m_uiBlockColumns = m_uiExtendedWidth >> 2; + m_uiBlockRows = m_uiExtendedHeight >> 2; + + unsigned int uiBlocks = GetNumberOfBlocks(); + + m_pablock = new Block4x4[uiBlocks]; + assert(m_pablock); + + m_format = a_format; + + m_iNumOpaquePixels = 0; + m_iNumTranslucentPixels = 0; + m_iNumTransparentPixels = 0; + + m_encodingbitsformat = DetermineEncodingBitsFormat(m_format); + if (m_encodingbitsformat == Block4x4EncodingBits::Format::UNKNOWN) + { + AddToEncodingStatus(ERROR_UNKNOWN_FORMAT); + return; + } + m_uiEncodingBitsBytes = a_uiEncodingBitsBytes; + m_paucEncodingBits = a_paucEncidingBits; + + m_errormetric = a_errormetric; + m_fEffort = 0.0f; + m_bVerboseOutput = false; + m_iEncodeTime_ms = -1; + + unsigned char *paucEncodingBits = m_paucEncodingBits; + unsigned int uiEncodingBitsBytesPerBlock = Block4x4EncodingBits::GetBytesPerBlock(m_encodingbitsformat); + + unsigned int uiH = 0; + unsigned int uiV = 0; + for (unsigned int uiBlock = 0; uiBlock < uiBlocks; uiBlock++) + { + m_pablock[uiBlock].InitFromEtcEncodingBits(a_format, uiH, uiV, paucEncodingBits, + a_pimageSource, a_errormetric); + paucEncodingBits += uiEncodingBitsBytesPerBlock; + uiH += 4; + if (uiH >= m_uiSourceWidth) + { + uiH = 0; + uiV += 4; + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // + Image::~Image(void) + { + if (m_pablock != nullptr) + { + delete[] m_pablock; + m_pablock = nullptr; + } + + if (m_paucEncodingBits != nullptr) + { + delete[] m_paucEncodingBits; + m_paucEncodingBits = nullptr; + } + } + + // ---------------------------------------------------------------------------------------------------- + // encode an image + // create a set of encoding bits that conforms to a_format + // find best fit using a_errormetric + // explore a range of possible encodings based on a_fEffort (range = [0:100]) + // speed up process using a_uiJobs as the number of process threads (a_uiJobs must not excede a_uiMaxJobs) + // + Image::EncodingStatus Image::Encode(Format a_format, ErrorMetric a_errormetric, float a_fEffort, unsigned int a_uiJobs, unsigned int a_uiMaxJobs) + { + + auto start = std::chrono::steady_clock::now(); + + m_encodingStatus = EncodingStatus::SUCCESS; + + m_format = a_format; + m_errormetric = a_errormetric; + m_fEffort = a_fEffort; + + if (m_errormetric < 0 || m_errormetric > ERROR_METRICS) + { + AddToEncodingStatus(ERROR_UNKNOWN_ERROR_METRIC); + return m_encodingStatus; + } + + if (m_fEffort < ETCCOMP_MIN_EFFORT_LEVEL) + { + AddToEncodingStatus(WARNING_EFFORT_OUT_OF_RANGE); + m_fEffort = ETCCOMP_MIN_EFFORT_LEVEL; + } + else if (m_fEffort > ETCCOMP_MAX_EFFORT_LEVEL) + { + AddToEncodingStatus(WARNING_EFFORT_OUT_OF_RANGE); + m_fEffort = ETCCOMP_MAX_EFFORT_LEVEL; + } + if (a_uiJobs < 1) + { + a_uiJobs = 1; + AddToEncodingStatus(WARNING_JOBS_OUT_OF_RANGE); + } + else if (a_uiJobs > a_uiMaxJobs) + { + a_uiJobs = a_uiMaxJobs; + AddToEncodingStatus(WARNING_JOBS_OUT_OF_RANGE); + } + + m_encodingbitsformat = DetermineEncodingBitsFormat(m_format); + + if (m_encodingbitsformat == Block4x4EncodingBits::Format::UNKNOWN) + { + AddToEncodingStatus(ERROR_UNKNOWN_FORMAT); + return m_encodingStatus; + } + + assert(m_paucEncodingBits == nullptr); + m_uiEncodingBitsBytes = GetNumberOfBlocks() * Block4x4EncodingBits::GetBytesPerBlock(m_encodingbitsformat); + m_paucEncodingBits = new unsigned char[m_uiEncodingBitsBytes]; + + InitBlocksAndBlockSorter(); + + + std::future *handle = new std::future[a_uiMaxJobs]; + + unsigned int uiNumThreadsNeeded = 0; + unsigned int uiUnfinishedBlocks = GetNumberOfBlocks(); + + uiNumThreadsNeeded = (uiUnfinishedBlocks < a_uiJobs) ? uiUnfinishedBlocks : a_uiJobs; + + for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++) + { + handle[i] = async(std::launch::async, &Image::RunFirstPass, this, i, uiNumThreadsNeeded); + } + + RunFirstPass(uiNumThreadsNeeded - 1, uiNumThreadsNeeded); + + for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++) + { + handle[i].get(); + } + + // perform effort-based encoding + if (m_fEffort > ETCCOMP_MIN_EFFORT_LEVEL) + { + unsigned int uiFinishedBlocks = 0; + unsigned int uiTotalEffortBlocks = static_cast(roundf(0.01f * m_fEffort * GetNumberOfBlocks())); + + if (m_bVerboseOutput) + { + printf("effortblocks = %d\n", uiTotalEffortBlocks); + } + unsigned int uiPass = 0; + while (1) + { + if (m_bVerboseOutput) + { + uiPass++; + printf("pass %u\n", uiPass); + } + m_psortedblocklist->Sort(); + uiUnfinishedBlocks = m_psortedblocklist->GetNumberOfSortedBlocks(); + uiFinishedBlocks = GetNumberOfBlocks() - uiUnfinishedBlocks; + if (m_bVerboseOutput) + { + printf(" %u unfinished blocks\n", uiUnfinishedBlocks); + // m_psortedblocklist->Print(); + } + + + + //stop enocding when we did enough to satify the effort percentage + if (uiFinishedBlocks >= uiTotalEffortBlocks) + { + if (m_bVerboseOutput) + { + printf("Finished %d Blocks out of %d\n", uiFinishedBlocks, uiTotalEffortBlocks); + } + break; + } + + unsigned int uiIteratedBlocks = 0; + unsigned int blocksToIterateThisPass = (uiTotalEffortBlocks - uiFinishedBlocks); + uiNumThreadsNeeded = (uiUnfinishedBlocks < a_uiJobs) ? uiUnfinishedBlocks : a_uiJobs; + + if (uiNumThreadsNeeded <= 1) + { + //since we already how many blocks each thread will process + //cap the thread limit to do the proper amount of work, and not more + uiIteratedBlocks = IterateThroughWorstBlocks(blocksToIterateThisPass, 0, 1); + } + else + { + //we have a lot of work to do, so lets multi thread it + std::future *handleToBlockEncoders = new std::future[uiNumThreadsNeeded-1]; + + for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++) + { + handleToBlockEncoders[i] = async(std::launch::async, &Image::IterateThroughWorstBlocks, this, blocksToIterateThisPass, i, uiNumThreadsNeeded); + } + uiIteratedBlocks = IterateThroughWorstBlocks(blocksToIterateThisPass, uiNumThreadsNeeded - 1, uiNumThreadsNeeded); + + for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++) + { + uiIteratedBlocks += handleToBlockEncoders[i].get(); + } + + delete[] handleToBlockEncoders; + } + + if (m_bVerboseOutput) + { + printf(" %u iterated blocks\n", uiIteratedBlocks); + } + } + } + + // generate Etc2-compatible bit-format 4x4 blocks + for (int i = 0; i < (int)a_uiJobs - 1; i++) + { + handle[i] = async(std::launch::async, &Image::SetEncodingBits, this, i, a_uiJobs); + } + SetEncodingBits(a_uiJobs - 1, a_uiJobs); + + for (int i = 0; i < (int)a_uiJobs - 1; i++) + { + handle[i].get(); + } + + auto end = std::chrono::steady_clock::now(); + std::chrono::milliseconds elapsed = std::chrono::duration_cast(end - start); + m_iEncodeTime_ms = (int)elapsed.count(); + + delete[] handle; + delete m_psortedblocklist; + return m_encodingStatus; + } + + // ---------------------------------------------------------------------------------------------------- + // iterate the encoding thru the blocks with the worst error + // stop when a_uiMaxBlocks blocks have been iterated + // split the blocks between the process threads using a_uiMultithreadingOffset and a_uiMultithreadingStride + // + unsigned int Image::IterateThroughWorstBlocks(unsigned int a_uiMaxBlocks, + unsigned int a_uiMultithreadingOffset, + unsigned int a_uiMultithreadingStride) + { + assert(a_uiMultithreadingStride > 0); + unsigned int uiIteratedBlocks = a_uiMultithreadingOffset; + + SortedBlockList::Link *plink = m_psortedblocklist->GetLinkToFirstBlock(); + for (plink = plink->Advance(a_uiMultithreadingOffset); + plink != nullptr; + plink = plink->Advance(a_uiMultithreadingStride) ) + { + if (uiIteratedBlocks >= a_uiMaxBlocks) + { + break; + } + + plink->GetBlock()->PerformEncodingIteration(m_fEffort); + + uiIteratedBlocks += a_uiMultithreadingStride; + } + + return uiIteratedBlocks; + } + + // ---------------------------------------------------------------------------------------------------- + // determine which warnings to check for during Encode() based on encoding format + // + void Image::FindEncodingWarningTypesForCurFormat() + { + TrackEncodingWarning(WARNING_ALL_TRANSPARENT_PIXELS); + TrackEncodingWarning(WARNING_SOME_RGBA_NOT_0_TO_1); + switch (m_format) + { + case Image::Format::ETC1: + case Image::Format::RGB8: + case Image::Format::SRGB8: + TrackEncodingWarning(WARNING_SOME_NON_OPAQUE_PIXELS); + TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS); + break; + + case Image::Format::RGB8A1: + case Image::Format::SRGB8A1: + TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS); + TrackEncodingWarning(WARNING_ALL_OPAQUE_PIXELS); + break; + case Image::Format::RGBA8: + case Image::Format::SRGBA8: + TrackEncodingWarning(WARNING_ALL_OPAQUE_PIXELS); + break; + + case Image::Format::R11: + case Image::Format::SIGNED_R11: + TrackEncodingWarning(WARNING_SOME_NON_OPAQUE_PIXELS); + TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS); + TrackEncodingWarning(WARNING_SOME_GREEN_VALUES_ARE_NOT_ZERO); + TrackEncodingWarning(WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO); + break; + + case Image::Format::RG11: + case Image::Format::SIGNED_RG11: + TrackEncodingWarning(WARNING_SOME_NON_OPAQUE_PIXELS); + TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS); + TrackEncodingWarning(WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO); + break; + case Image::Format::FORMATS: + case Image::Format::UNKNOWN: + default: + assert(0); + break; + } + } + + // ---------------------------------------------------------------------------------------------------- + // examine source pixels to check for warnings + // + void Image::FindAndSetEncodingWarnings() + { + int numPixels = (m_uiBlockRows * 4) * (m_uiBlockColumns * 4); + if (m_iNumOpaquePixels == numPixels) + { + AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_ALL_OPAQUE_PIXELS); + } + if (m_iNumOpaquePixels < numPixels) + { + AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_NON_OPAQUE_PIXELS); + } + if (m_iNumTranslucentPixels > 0) + { + AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_TRANSLUCENT_PIXELS); + } + if (m_iNumTransparentPixels == numPixels) + { + AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_ALL_TRANSPARENT_PIXELS); + } + if (m_numColorValues.fB > 0.0f) + { + AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO); + } + if (m_numColorValues.fG > 0.0f) + { + AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_GREEN_VALUES_ARE_NOT_ZERO); + } + + if (m_numOutOfRangeValues.fR > 0.0f || m_numOutOfRangeValues.fG > 0.0f) + { + AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_RGBA_NOT_0_TO_1); + } + if (m_numOutOfRangeValues.fB > 0.0f || m_numOutOfRangeValues.fA > 0.0f) + { + AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_RGBA_NOT_0_TO_1); + } + } + + // ---------------------------------------------------------------------------------------------------- + // return a string name for a given image format + // + const char * Image::EncodingFormatToString(Image::Format a_format) + { + switch (a_format) + { + case Image::Format::ETC1: + return "ETC1"; + case Image::Format::RGB8: + return "RGB8"; + case Image::Format::SRGB8: + return "SRGB8"; + + case Image::Format::RGB8A1: + return "RGB8A1"; + case Image::Format::SRGB8A1: + return "SRGB8A1"; + case Image::Format::RGBA8: + return "RGBA8"; + case Image::Format::SRGBA8: + return "SRGBA8"; + + case Image::Format::R11: + return "R11"; + case Image::Format::SIGNED_R11: + return "SIGNED_R11"; + + case Image::Format::RG11: + return "RG11"; + case Image::Format::SIGNED_RG11: + return "SIGNED_RG11"; + case Image::Format::FORMATS: + case Image::Format::UNKNOWN: + default: + return "UNKNOWN"; + } + } + + // ---------------------------------------------------------------------------------------------------- + // return a string name for the image's format + // + const char * Image::EncodingFormatToString(void) + { + return EncodingFormatToString(m_format); + } + + // ---------------------------------------------------------------------------------------------------- + // init image blocks prior to encoding + // init block sorter for subsequent sortings + // check for encoding warnings + // + void Image::InitBlocksAndBlockSorter(void) + { + + FindEncodingWarningTypesForCurFormat(); + + // init each block + Block4x4 *pblock = m_pablock; + unsigned char *paucEncodingBits = m_paucEncodingBits; + for (unsigned int uiBlockRow = 0; uiBlockRow < m_uiBlockRows; uiBlockRow++) + { + unsigned int uiBlockV = uiBlockRow * 4; + + for (unsigned int uiBlockColumn = 0; uiBlockColumn < m_uiBlockColumns; uiBlockColumn++) + { + unsigned int uiBlockH = uiBlockColumn * 4; + + pblock->InitFromSource(this, uiBlockH, uiBlockV, paucEncodingBits, m_errormetric); + + paucEncodingBits += Block4x4EncodingBits::GetBytesPerBlock(m_encodingbitsformat); + + pblock++; + } + } + + FindAndSetEncodingWarnings(); + + // init block sorter + { + m_psortedblocklist = new SortedBlockList(GetNumberOfBlocks(), 100); + + for (unsigned int uiBlock = 0; uiBlock < GetNumberOfBlocks(); uiBlock++) + { + pblock = &m_pablock[uiBlock]; + m_psortedblocklist->AddBlock(pblock); + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // run the first pass of the encoder + // the encoder generally finds a reasonable, fast encoding + // this is run on all blocks regardless of effort to ensure that all blocks have a valid encoding + // + void Image::RunFirstPass(unsigned int a_uiMultithreadingOffset, unsigned int a_uiMultithreadingStride) + { + assert(a_uiMultithreadingStride > 0); + + for (unsigned int uiBlock = a_uiMultithreadingOffset; + uiBlock < GetNumberOfBlocks(); + uiBlock += a_uiMultithreadingStride) + { + Block4x4 *pblock = &m_pablock[uiBlock]; + pblock->PerformEncodingIteration(m_fEffort); + } + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits (for the output file) based on the best encoding for each block + // + void Image::SetEncodingBits(unsigned int a_uiMultithreadingOffset, + unsigned int a_uiMultithreadingStride) + { + assert(a_uiMultithreadingStride > 0); + + for (unsigned int uiBlock = a_uiMultithreadingOffset; + uiBlock < GetNumberOfBlocks(); + uiBlock += a_uiMultithreadingStride) + { + Block4x4 *pblock = &m_pablock[uiBlock]; + pblock->SetEncodingBitsFromEncoding(); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // return the image error + // image error is the sum of all block errors + // + float Image::GetError(void) + { + float fError = 0.0f; + + for (unsigned int uiBlock = 0; uiBlock < GetNumberOfBlocks(); uiBlock++) + { + Block4x4 *pblock = &m_pablock[uiBlock]; + fError += pblock->GetError(); + } + + return fError; + } + + // ---------------------------------------------------------------------------------------------------- + // determine the encoding bits format based on the encoding format + // the encoding bits format is a family of bit encodings that are shared across various encoding formats + // + Block4x4EncodingBits::Format Image::DetermineEncodingBitsFormat(Format a_format) + { + Block4x4EncodingBits::Format encodingbitsformat; + + // determine encoding bits format from image format + switch (a_format) + { + case Format::ETC1: + case Format::RGB8: + case Format::SRGB8: + encodingbitsformat = Block4x4EncodingBits::Format::RGB8; + break; + + case Format::RGBA8: + case Format::SRGBA8: + encodingbitsformat = Block4x4EncodingBits::Format::RGBA8; + break; + + case Format::R11: + case Format::SIGNED_R11: + encodingbitsformat = Block4x4EncodingBits::Format::R11; + break; + + case Format::RG11: + case Format::SIGNED_RG11: + encodingbitsformat = Block4x4EncodingBits::Format::RG11; + break; + + case Format::RGB8A1: + case Format::SRGB8A1: + encodingbitsformat = Block4x4EncodingBits::Format::RGB8A1; + break; + + default: + encodingbitsformat = Block4x4EncodingBits::Format::UNKNOWN; + break; + } + + return encodingbitsformat; + } + + // ---------------------------------------------------------------------------------------------------- + // + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/Etc/EtcImage.h b/deps/etc2comp/EtcLib/Etc/EtcImage.h new file mode 100644 index 0000000000..bd807ac32e --- /dev/null +++ b/deps/etc2comp/EtcLib/Etc/EtcImage.h @@ -0,0 +1,249 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +//#include "Etc.h" +#include "EtcColorFloatRGBA.h" +#include "EtcBlock4x4EncodingBits.h" +#include "EtcErrorMetric.h" + + +namespace Etc +{ + class Block4x4; + class EncoderSpec; + class SortedBlockList; + + class Image + { + public: + + //the differnt warning and errors that can come up during encoding + enum EncodingStatus + { + SUCCESS = 0, + // + WARNING_THRESHOLD = 1 << 0, + // + WARNING_EFFORT_OUT_OF_RANGE = 1 << 1, + WARNING_JOBS_OUT_OF_RANGE = 1 << 2, + WARNING_SOME_NON_OPAQUE_PIXELS = 1 << 3,//just for opaque formats, etc1, rgb8, r11, rg11 + WARNING_ALL_OPAQUE_PIXELS = 1 << 4, + WARNING_ALL_TRANSPARENT_PIXELS = 1 << 5, + WARNING_SOME_TRANSLUCENT_PIXELS = 1 << 6,//just for rgb8A1 + WARNING_SOME_RGBA_NOT_0_TO_1 = 1 << 7, + WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO = 1 << 8, + WARNING_SOME_GREEN_VALUES_ARE_NOT_ZERO = 1 << 9, + // + ERROR_THRESHOLD = 1 << 16, + // + ERROR_UNKNOWN_FORMAT = 1 << 17, + ERROR_UNKNOWN_ERROR_METRIC = 1 << 18, + ERROR_ZERO_WIDTH_OR_HEIGHT = 1 << 19, + // + }; + + enum class Format + { + UNKNOWN, + // + ETC1, + // + // ETC2 formats + RGB8, + SRGB8, + RGBA8, + SRGBA8, + R11, + SIGNED_R11, + RG11, + SIGNED_RG11, + RGB8A1, + SRGB8A1, + // + FORMATS, + // + DEFAULT = SRGB8 + }; + + // constructor using source image + Image(float *a_pafSourceRGBA, unsigned int a_uiSourceWidth, + unsigned int a_uiSourceHeight, + ErrorMetric a_errormetric); + + // constructor using encoding bits + Image(Format a_format, + unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight, + unsigned char *a_paucEncidingBits, unsigned int a_uiEncodingBitsBytes, + Image *a_pimageSource, + ErrorMetric a_errormetric); + + ~Image(void); + + EncodingStatus Encode(Format a_format, ErrorMetric a_errormetric, float a_fEffort, + unsigned int a_uiJobs, unsigned int a_uiMaxJobs); + + inline void AddToEncodingStatus(EncodingStatus a_encStatus) + { + m_encodingStatus = (EncodingStatus)((unsigned int)m_encodingStatus | (unsigned int)a_encStatus); + } + + inline unsigned int GetSourceWidth(void) + { + return m_uiSourceWidth; + } + + inline unsigned int GetSourceHeight(void) + { + return m_uiSourceHeight; + } + + inline unsigned int GetExtendedWidth(void) + { + return m_uiExtendedWidth; + } + + inline unsigned int GetExtendedHeight(void) + { + return m_uiExtendedHeight; + } + + inline unsigned int GetNumberOfBlocks() + { + return m_uiBlockColumns * m_uiBlockRows; + } + + inline Block4x4 * GetBlocks() + { + return m_pablock; + } + + inline unsigned char * GetEncodingBits(void) + { + return m_paucEncodingBits; + } + + inline unsigned int GetEncodingBitsBytes(void) + { + return m_uiEncodingBitsBytes; + } + + inline int GetEncodingTimeMs(void) + { + return m_iEncodeTime_ms; + } + + float GetError(void); + + inline ColorFloatRGBA * GetSourcePixel(unsigned int a_uiH, unsigned int a_uiV) + { + if (a_uiH >= m_uiSourceWidth || a_uiV >= m_uiSourceHeight) + { + return nullptr; + } + + return &m_pafrgbaSource[a_uiV*m_uiSourceWidth + a_uiH]; + } + + inline Format GetFormat(void) + { + return m_format; + } + + static Block4x4EncodingBits::Format DetermineEncodingBitsFormat(Format a_format); + + inline static unsigned short CalcExtendedDimension(unsigned short a_ushOriginalDimension) + { + return (unsigned short)((a_ushOriginalDimension + 3) & ~3); + } + + inline ErrorMetric GetErrorMetric(void) + { + return m_errormetric; + } + + static const char * EncodingFormatToString(Image::Format a_format); + const char * EncodingFormatToString(void); + //used to get basic information about the image data + int m_iNumOpaquePixels; + int m_iNumTranslucentPixels; + int m_iNumTransparentPixels; + + ColorFloatRGBA m_numColorValues; + ColorFloatRGBA m_numOutOfRangeValues; + + bool m_bVerboseOutput; + private: + //add a warning or error to check for while encoding + inline void TrackEncodingWarning(EncodingStatus a_encStatus) + { + m_warningsToCapture = (EncodingStatus)((unsigned int)m_warningsToCapture | (unsigned int)a_encStatus); + } + + //report the warning if it is something we care about for this encoding + inline void AddToEncodingStatusIfSignfigant(EncodingStatus a_encStatus) + { + if ((EncodingStatus)((unsigned int)m_warningsToCapture & (unsigned int)a_encStatus) == a_encStatus) + { + AddToEncodingStatus(a_encStatus); + } + } + + Image(void); + void FindEncodingWarningTypesForCurFormat(); + void FindAndSetEncodingWarnings(); + + void InitBlocksAndBlockSorter(void); + + void RunFirstPass(unsigned int a_uiMultithreadingOffset, + unsigned int a_uiMultithreadingStride); + + void SetEncodingBits(unsigned int a_uiMultithreadingOffset, + unsigned int a_uiMultithreadingStride); + + unsigned int IterateThroughWorstBlocks(unsigned int a_uiMaxBlocks, + unsigned int a_uiMultithreadingOffset, + unsigned int a_uiMultithreadingStride); + + // inputs + ColorFloatRGBA *m_pafrgbaSource; + unsigned int m_uiSourceWidth; + unsigned int m_uiSourceHeight; + unsigned int m_uiExtendedWidth; + unsigned int m_uiExtendedHeight; + unsigned int m_uiBlockColumns; + unsigned int m_uiBlockRows; + // intermediate data + Block4x4 *m_pablock; + // encoding + Format m_format; + Block4x4EncodingBits::Format m_encodingbitsformat; + unsigned int m_uiEncodingBitsBytes; // for entire image + unsigned char *m_paucEncodingBits; + ErrorMetric m_errormetric; + float m_fEffort; + // stats + int m_iEncodeTime_ms; + + SortedBlockList *m_psortedblocklist; + //this will hold any warning or errors that happen during encoding + EncodingStatus m_encodingStatus; + //these will be the warnings we are tracking + EncodingStatus m_warningsToCapture; + }; + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/Etc/EtcMath.cpp b/deps/etc2comp/EtcLib/Etc/EtcMath.cpp new file mode 100644 index 0000000000..096d5f7ab9 --- /dev/null +++ b/deps/etc2comp/EtcLib/Etc/EtcMath.cpp @@ -0,0 +1,64 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EtcConfig.h" +#include "EtcMath.h" + +namespace Etc +{ + + // ---------------------------------------------------------------------------------------------------- + // calculate the line that best fits the set of XY points contained in a_afX[] and a_afY[] + // use a_fSlope and a_fOffset to define that line + // + bool Regression(float a_afX[], float a_afY[], unsigned int a_Points, + float *a_fSlope, float *a_fOffset) + { + float fPoints = (float)a_Points; + + float fSumX = 0.0f; + float fSumY = 0.0f; + float fSumXY = 0.0f; + float fSumX2 = 0.0f; + + for (unsigned int uiPoint = 0; uiPoint < a_Points; uiPoint++) + { + fSumX += a_afX[uiPoint]; + fSumY += a_afY[uiPoint]; + fSumXY += a_afX[uiPoint] * a_afY[uiPoint]; + fSumX2 += a_afX[uiPoint] * a_afX[uiPoint]; + } + + float fDivisor = fPoints*fSumX2 - fSumX*fSumX; + + // if vertical line + if (fDivisor == 0.0f) + { + *a_fSlope = 0.0f; + *a_fOffset = 0.0f; + return true; + } + + *a_fSlope = (fPoints*fSumXY - fSumX*fSumY) / fDivisor; + *a_fOffset = (fSumY - (*a_fSlope)*fSumX) / fPoints; + + return false; + } + + // ---------------------------------------------------------------------------------------------------- + // + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/Etc/EtcMath.h b/deps/etc2comp/EtcLib/Etc/EtcMath.h new file mode 100644 index 0000000000..c58c9a91bc --- /dev/null +++ b/deps/etc2comp/EtcLib/Etc/EtcMath.h @@ -0,0 +1,40 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace Etc +{ + + // ---------------------------------------------------------------------------------------------------- + // return true if vertical line + bool Regression(float a_afX[], float a_afY[], unsigned int a_Points, + float *a_fSlope, float *a_fOffset); + + inline float ConvertMSEToPSNR(float a_fMSE) + { + if (a_fMSE == 0.0f) + { + return INFINITY; + } + + return 10.0f * log10f(1.0f / a_fMSE); + } + + +} diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4.cpp b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4.cpp new file mode 100644 index 0000000000..3082fe60db --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4.cpp @@ -0,0 +1,425 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +EtcBlock4x4.cpp + +Implements the state associated with each 4x4 block of pixels in an image + +Source images that are not a multiple of 4x4 are extended to fill the Block4x4 using pixels with an +alpha of NAN + +*/ + +#include "EtcConfig.h" +#include "EtcBlock4x4.h" + +#include "EtcBlock4x4EncodingBits.h" +#include "EtcColor.h" +#include "EtcImage.h" +#include "EtcColorFloatRGBA.h" +#include "EtcBlock4x4Encoding_RGB8.h" +#include "EtcBlock4x4Encoding_RGBA8.h" +#include "EtcBlock4x4Encoding_RGB8A1.h" +#include "EtcBlock4x4Encoding_R11.h" +#include "EtcBlock4x4Encoding_RG11.h" + +#include +#include +#include + +namespace Etc +{ + // ETC pixels are scanned vertically. + // this mapping is for when someone wants to scan the ETC pixels horizontally + const unsigned int Block4x4::s_auiPixelOrderHScan[PIXELS] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 }; + + // ---------------------------------------------------------------------------------------------------- + // + Block4x4::Block4x4(void) + { + m_pimageSource = nullptr; + m_uiSourceH = 0; + m_uiSourceV = 0; + + m_sourcealphamix = SourceAlphaMix::UNKNOWN; + m_boolBorderPixels = false; + m_boolPunchThroughPixels = false; + + m_pencoding = nullptr; + + m_errormetric = ErrorMetric::NUMERIC; + + } + Block4x4::~Block4x4() + { + m_pimageSource = nullptr; + if (m_pencoding) + { + delete m_pencoding; + m_pencoding = nullptr; + } + } + // ---------------------------------------------------------------------------------------------------- + // initialization prior to encoding from a source image + // [a_uiSourceH,a_uiSourceV] is the location of the block in a_pimageSource + // a_paucEncodingBits is the place to store the final encoding + // a_errormetric is used for finding the best encoding + // + void Block4x4::InitFromSource(Image *a_pimageSource, + unsigned int a_uiSourceH, unsigned int a_uiSourceV, + unsigned char *a_paucEncodingBits, + ErrorMetric a_errormetric) + { + + Block4x4(); + + m_pimageSource = a_pimageSource; + m_uiSourceH = a_uiSourceH; + m_uiSourceV = a_uiSourceV; + m_errormetric = a_errormetric; + + SetSourcePixels(); + + // set block encoder function + switch (m_pimageSource->GetFormat()) + { + case Image::Format::ETC1: + m_pencoding = new Block4x4Encoding_ETC1; + break; + + case Image::Format::RGB8: + case Image::Format::SRGB8: + m_pencoding = new Block4x4Encoding_RGB8; + break; + + case Image::Format::RGBA8: + case Image::Format::SRGBA8: + if (a_errormetric == RGBX) + { + m_pencoding = new Block4x4Encoding_RGBA8; + } + else + { + switch (m_sourcealphamix) + { + case SourceAlphaMix::OPAQUE: + m_pencoding = new Block4x4Encoding_RGBA8_Opaque; + break; + + case SourceAlphaMix::TRANSPARENT: + m_pencoding = new Block4x4Encoding_RGBA8_Transparent; + break; + + case SourceAlphaMix::TRANSLUCENT: + m_pencoding = new Block4x4Encoding_RGBA8; + break; + + default: + assert(0); + break; + } + break; + } + break; + + case Image::Format::RGB8A1: + case Image::Format::SRGB8A1: + switch (m_sourcealphamix) + { + case SourceAlphaMix::OPAQUE: + m_pencoding = new Block4x4Encoding_RGB8A1_Opaque; + break; + + case SourceAlphaMix::TRANSPARENT: + m_pencoding = new Block4x4Encoding_RGB8A1_Transparent; + break; + + case SourceAlphaMix::TRANSLUCENT: + if (m_boolPunchThroughPixels) + { + m_pencoding = new Block4x4Encoding_RGB8A1; + } + else + { + m_pencoding = new Block4x4Encoding_RGB8A1_Opaque; + } + break; + + default: + assert(0); + break; + } + break; + + case Image::Format::R11: + case Image::Format::SIGNED_R11: + m_pencoding = new Block4x4Encoding_R11; + break; + case Image::Format::RG11: + case Image::Format::SIGNED_RG11: + m_pencoding = new Block4x4Encoding_RG11; + break; + default: + assert(0); + break; + } + + m_pencoding->InitFromSource(this, m_afrgbaSource, + a_paucEncodingBits, a_errormetric); + + } + + // ---------------------------------------------------------------------------------------------------- + // initialization of encoding state from a prior encoding using encoding bits + // [a_uiSourceH,a_uiSourceV] is the location of the block in a_pimageSource + // a_paucEncodingBits is the place to read the prior encoding + // a_imageformat is used to determine how to interpret a_paucEncodingBits + // a_errormetric was used for the prior encoding + // + void Block4x4::InitFromEtcEncodingBits(Image::Format a_imageformat, + unsigned int a_uiSourceH, unsigned int a_uiSourceV, + unsigned char *a_paucEncodingBits, + Image *a_pimageSource, + ErrorMetric a_errormetric) + { + Block4x4(); + + m_pimageSource = a_pimageSource; + m_uiSourceH = a_uiSourceH; + m_uiSourceV = a_uiSourceV; + m_errormetric = a_errormetric; + + SetSourcePixels(); + + // set block encoder function + switch (a_imageformat) + { + case Image::Format::ETC1: + m_pencoding = new Block4x4Encoding_ETC1; + break; + + case Image::Format::RGB8: + case Image::Format::SRGB8: + m_pencoding = new Block4x4Encoding_RGB8; + break; + + case Image::Format::RGBA8: + case Image::Format::SRGBA8: + m_pencoding = new Block4x4Encoding_RGBA8; + break; + + case Image::Format::RGB8A1: + case Image::Format::SRGB8A1: + m_pencoding = new Block4x4Encoding_RGB8A1; + break; + + case Image::Format::R11: + case Image::Format::SIGNED_R11: + m_pencoding = new Block4x4Encoding_R11; + break; + case Image::Format::RG11: + case Image::Format::SIGNED_RG11: + m_pencoding = new Block4x4Encoding_RG11; + break; + default: + assert(0); + break; + } + + m_pencoding->InitFromEncodingBits(this, a_paucEncodingBits, m_afrgbaSource, + m_pimageSource->GetErrorMetric()); + + } + + // ---------------------------------------------------------------------------------------------------- + // set source pixels from m_pimageSource + // set m_alphamix + // + void Block4x4::SetSourcePixels(void) + { + + Image::Format imageformat = m_pimageSource->GetFormat(); + + // alpha census + unsigned int uiTransparentSourcePixels = 0; + unsigned int uiOpaqueSourcePixels = 0; + + // copy source to consecutive memory locations + // convert from image horizontal scan to block vertical scan + unsigned int uiPixel = 0; + for (unsigned int uiBlockPixelH = 0; uiBlockPixelH < Block4x4::COLUMNS; uiBlockPixelH++) + { + unsigned int uiSourcePixelH = m_uiSourceH + uiBlockPixelH; + + for (unsigned int uiBlockPixelV = 0; uiBlockPixelV < Block4x4::ROWS; uiBlockPixelV++) + { + unsigned int uiSourcePixelV = m_uiSourceV + uiBlockPixelV; + + ColorFloatRGBA *pfrgbaSource = m_pimageSource->GetSourcePixel(uiSourcePixelH, uiSourcePixelV); + + // if pixel extends beyond source image because of block padding + if (pfrgbaSource == nullptr) + { + m_afrgbaSource[uiPixel] = ColorFloatRGBA(0.0f, 0.0f, 0.0f, NAN); // denotes border pixel + m_boolBorderPixels = true; + uiTransparentSourcePixels++; + } + else + { + //get teh current pixel data, and store some of the attributes + //before capping values to fit the encoder type + + m_afrgbaSource[uiPixel] = (*pfrgbaSource).ClampRGBA(); + + if (m_afrgbaSource[uiPixel].fA == 1.0f || m_errormetric == RGBX) + { + m_pimageSource->m_iNumOpaquePixels++; + } + else if (m_afrgbaSource[uiPixel].fA == 0.0f) + { + m_pimageSource->m_iNumTransparentPixels++; + } + else if(m_afrgbaSource[uiPixel].fA > 0.0f && m_afrgbaSource[uiPixel].fA < 1.0f) + { + m_pimageSource->m_iNumTranslucentPixels++; + } + else + { + m_pimageSource->m_numOutOfRangeValues.fA++; + } + + if (m_afrgbaSource[uiPixel].fR != 0.0f) + { + m_pimageSource->m_numColorValues.fR++; + //make sure we are getting a float between 0-1 + if (m_afrgbaSource[uiPixel].fR - 1.0f > 0.0f) + { + m_pimageSource->m_numOutOfRangeValues.fR++; + } + } + + if (m_afrgbaSource[uiPixel].fG != 0.0f) + { + m_pimageSource->m_numColorValues.fG++; + if (m_afrgbaSource[uiPixel].fG - 1.0f > 0.0f) + { + m_pimageSource->m_numOutOfRangeValues.fG++; + } + } + if (m_afrgbaSource[uiPixel].fB != 0.0f) + { + m_pimageSource->m_numColorValues.fB++; + if (m_afrgbaSource[uiPixel].fB - 1.0f > 0.0f) + { + m_pimageSource->m_numOutOfRangeValues.fB++; + } + } + // for formats with no alpha, set source alpha to 1 + if (imageformat == Image::Format::ETC1 || + imageformat == Image::Format::RGB8 || + imageformat == Image::Format::SRGB8) + { + m_afrgbaSource[uiPixel].fA = 1.0f; + } + + if (imageformat == Image::Format::R11 || + imageformat == Image::Format::SIGNED_R11) + { + m_afrgbaSource[uiPixel].fA = 1.0f; + m_afrgbaSource[uiPixel].fG = 0.0f; + m_afrgbaSource[uiPixel].fB = 0.0f; + } + + if (imageformat == Image::Format::RG11 || + imageformat == Image::Format::SIGNED_RG11) + { + m_afrgbaSource[uiPixel].fA = 1.0f; + m_afrgbaSource[uiPixel].fB = 0.0f; + } + + + // for RGB8A1, set source alpha to 0.0 or 1.0 + // set punch through flag + if (imageformat == Image::Format::RGB8A1 || + imageformat == Image::Format::SRGB8A1) + { + if (m_afrgbaSource[uiPixel].fA >= 0.5f) + { + m_afrgbaSource[uiPixel].fA = 1.0f; + } + else + { + m_afrgbaSource[uiPixel].fA = 0.0f; + m_boolPunchThroughPixels = true; + } + } + + if (m_afrgbaSource[uiPixel].fA == 1.0f || m_errormetric == RGBX) + { + uiOpaqueSourcePixels++; + } + else if (m_afrgbaSource[uiPixel].fA == 0.0f) + { + uiTransparentSourcePixels++; + } + + } + + uiPixel += 1; + } + } + + if (uiOpaqueSourcePixels == PIXELS) + { + m_sourcealphamix = SourceAlphaMix::OPAQUE; + } + else if (uiTransparentSourcePixels == PIXELS) + { + m_sourcealphamix = SourceAlphaMix::TRANSPARENT; + } + else + { + m_sourcealphamix = SourceAlphaMix::TRANSLUCENT; + } + + } + + // ---------------------------------------------------------------------------------------------------- + // return a name for the encoding mode + // + const char * Block4x4::GetEncodingModeName(void) + { + + switch (m_pencoding->GetMode()) + { + case Block4x4Encoding::MODE_ETC1: + return "ETC1"; + case Block4x4Encoding::MODE_T: + return "T"; + case Block4x4Encoding::MODE_H: + return "H"; + case Block4x4Encoding::MODE_PLANAR: + return "PLANAR"; + default: + return "???"; + } + } + + // ---------------------------------------------------------------------------------------------------- + // + +} diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4.h b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4.h new file mode 100644 index 0000000000..0fd30c598d --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4.h @@ -0,0 +1,172 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcColor.h" +#include "EtcColorFloatRGBA.h" +#include "EtcErrorMetric.h" +#include "EtcImage.h" +#include "EtcBlock4x4Encoding.h" + +namespace Etc +{ + class Block4x4EncodingBits; + + class Block4x4 + { + public: + + static const unsigned int ROWS = 4; + static const unsigned int COLUMNS = 4; + static const unsigned int PIXELS = ROWS * COLUMNS; + + // the alpha mix for a 4x4 block of pixels + enum class SourceAlphaMix + { + UNKNOWN, + // + OPAQUE, // all 1.0 + TRANSPARENT, // all 0.0 or NAN + TRANSLUCENT // not all opaque or transparent + }; + + typedef void (Block4x4::*EncoderFunctionPtr)(void); + + Block4x4(void); + ~Block4x4(); + void InitFromSource(Image *a_pimageSource, + unsigned int a_uiSourceH, + unsigned int a_uiSourceV, + unsigned char *a_paucEncodingBits, + ErrorMetric a_errormetric); + + void InitFromEtcEncodingBits(Image::Format a_imageformat, + unsigned int a_uiSourceH, + unsigned int a_uiSourceV, + unsigned char *a_paucEncodingBits, + Image *a_pimageSource, + ErrorMetric a_errormetric); + + // return true if final iteration was performed + inline void PerformEncodingIteration(float a_fEffort) + { + m_pencoding->PerformIteration(a_fEffort); + } + + inline void SetEncodingBitsFromEncoding(void) + { + m_pencoding->SetEncodingBits(); + } + + inline unsigned int GetSourceH(void) + { + return m_uiSourceH; + } + + inline unsigned int GetSourceV(void) + { + return m_uiSourceV; + } + + inline float GetError(void) + { + return m_pencoding->GetError(); + } + + static const unsigned int s_auiPixelOrderHScan[PIXELS]; + + inline ColorFloatRGBA * GetDecodedColors(void) + { + return m_pencoding->GetDecodedColors(); + } + + inline float * GetDecodedAlphas(void) + { + return m_pencoding->GetDecodedAlphas(); + } + + inline Block4x4Encoding::Mode GetEncodingMode(void) + { + return m_pencoding->GetMode(); + } + + inline bool GetFlip(void) + { + return m_pencoding->GetFlip(); + } + + inline bool IsDifferential(void) + { + return m_pencoding->IsDifferential(); + } + + inline ColorFloatRGBA * GetSource() + { + return m_afrgbaSource; + } + + inline ErrorMetric GetErrorMetric() + { + return m_errormetric; + } + + const char * GetEncodingModeName(void); + + inline Block4x4Encoding * GetEncoding(void) + { + return m_pencoding; + } + + inline SourceAlphaMix GetSourceAlphaMix(void) + { + return m_sourcealphamix; + } + + inline Image * GetImageSource(void) + { + return m_pimageSource; + } + + inline bool HasBorderPixels(void) + { + return m_boolBorderPixels; + } + + inline bool HasPunchThroughPixels(void) + { + return m_boolPunchThroughPixels; + } + + private: + + void SetSourcePixels(void); + + Image *m_pimageSource; + unsigned int m_uiSourceH; + unsigned int m_uiSourceV; + ErrorMetric m_errormetric; + ColorFloatRGBA m_afrgbaSource[PIXELS]; // vertical scan + + SourceAlphaMix m_sourcealphamix; + bool m_boolBorderPixels; // marked as rgba(NAN, NAN, NAN, NAN) + bool m_boolPunchThroughPixels; // RGB8A1 or SRGB8A1 with any pixels with alpha < 0.5 + + Block4x4Encoding *m_pencoding; + + }; + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding.cpp b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding.cpp new file mode 100644 index 0000000000..7a9e68c4cf --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding.cpp @@ -0,0 +1,261 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +EtcBlock4x4Encoding.cpp + +Block4x4Encoding is the abstract base class for the different encoders. Each encoder targets a +particular file format (e.g. ETC1, RGB8, RGBA8, R11) + +*/ + +#include "EtcConfig.h" +#include "EtcBlock4x4Encoding.h" + +#include "EtcBlock4x4EncodingBits.h" +#include "EtcBlock4x4.h" + +#include +#include +#include + +namespace Etc +{ + // ---------------------------------------------------------------------------------------------------- + // + const float Block4x4Encoding::LUMA_WEIGHT = 3.0f; + const float Block4x4Encoding::CHROMA_BLUE_WEIGHT = 0.5f; + + // ---------------------------------------------------------------------------------------------------- + // + Block4x4Encoding::Block4x4Encoding(void) + { + + m_pblockParent = nullptr; + + m_pafrgbaSource = nullptr; + + m_boolBorderPixels = false; + + m_fError = -1.0f; + + m_mode = MODE_UNKNOWN; + + m_uiEncodingIterations = 0; + m_boolDone = false; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(-1.0f, -1.0f, -1.0f, -1.0f); + m_afDecodedAlphas[uiPixel] = -1.0f; + } + + } + + // ---------------------------------------------------------------------------------------------------- + // initialize the generic encoding for a 4x4 block + // a_pblockParent points to the block associated with this encoding + // a_errormetric is used to choose the best encoding + // init the decoded pixels to -1 to mark them as undefined + // init the error to -1 to mark it as undefined + // + void Block4x4Encoding::Init(Block4x4 *a_pblockParent, + ColorFloatRGBA *a_pafrgbaSource, + ErrorMetric a_errormetric) + { + + m_pblockParent = a_pblockParent; + + m_pafrgbaSource = a_pafrgbaSource; + + m_boolBorderPixels = m_pblockParent->HasBorderPixels(); + + m_fError = -1.0f; + + m_uiEncodingIterations = 0; + + m_errormetric = a_errormetric; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(-1.0f, -1.0f, -1.0f, -1.0f); + m_afDecodedAlphas[uiPixel] = -1.0f; + } + + } + + // ---------------------------------------------------------------------------------------------------- + // calculate the error for the block by summing the pixel errors + // + void Block4x4Encoding::CalcBlockError(void) + { + m_fError = 0.0f; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_fError += CalcPixelError(m_afrgbaDecodedColors[uiPixel], m_afDecodedAlphas[uiPixel], + m_pafrgbaSource[uiPixel]); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // calculate the error between the source pixel and the decoded pixel + // the error amount is base on the error metric + // + float Block4x4Encoding::CalcPixelError(ColorFloatRGBA a_frgbaDecodedColor, float a_fDecodedAlpha, + ColorFloatRGBA a_frgbaSourcePixel) + { + + // if a border pixel + if (isnan(a_frgbaSourcePixel.fA)) + { + return 0.0f; + } + + if (m_errormetric == ErrorMetric::RGBA) + { + assert(a_fDecodedAlpha >= 0.0f); + + float fDRed = (a_fDecodedAlpha * a_frgbaDecodedColor.fR) - + (a_frgbaSourcePixel.fA * a_frgbaSourcePixel.fR); + float fDGreen = (a_fDecodedAlpha * a_frgbaDecodedColor.fG) - + (a_frgbaSourcePixel.fA * a_frgbaSourcePixel.fG); + float fDBlue = (a_fDecodedAlpha * a_frgbaDecodedColor.fB) - + (a_frgbaSourcePixel.fA * a_frgbaSourcePixel.fB); + + float fDAlpha = a_fDecodedAlpha - a_frgbaSourcePixel.fA; + + return fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue + fDAlpha*fDAlpha; + } + else if (m_errormetric == ErrorMetric::RGBX) + { + assert(a_fDecodedAlpha >= 0.0f); + + float fDRed = a_frgbaDecodedColor.fR - a_frgbaSourcePixel.fR; + float fDGreen = a_frgbaDecodedColor.fG - a_frgbaSourcePixel.fG; + float fDBlue = a_frgbaDecodedColor.fB - a_frgbaSourcePixel.fB; + float fDAlpha = a_fDecodedAlpha - a_frgbaSourcePixel.fA; + + return fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue + fDAlpha*fDAlpha; + } + else if (m_errormetric == ErrorMetric::REC709) + { + assert(a_fDecodedAlpha >= 0.0f); + + float fLuma1 = a_frgbaSourcePixel.fR*0.2126f + a_frgbaSourcePixel.fG*0.7152f + a_frgbaSourcePixel.fB*0.0722f; + float fChromaR1 = 0.5f * ((a_frgbaSourcePixel.fR - fLuma1) * (1.0f / (1.0f - 0.2126f))); + float fChromaB1 = 0.5f * ((a_frgbaSourcePixel.fB - fLuma1) * (1.0f / (1.0f - 0.0722f))); + + float fLuma2 = a_frgbaDecodedColor.fR*0.2126f + + a_frgbaDecodedColor.fG*0.7152f + + a_frgbaDecodedColor.fB*0.0722f; + float fChromaR2 = 0.5f * ((a_frgbaDecodedColor.fR - fLuma2) * (1.0f / (1.0f - 0.2126f))); + float fChromaB2 = 0.5f * ((a_frgbaDecodedColor.fB - fLuma2) * (1.0f / (1.0f - 0.0722f))); + + float fDeltaL = a_frgbaSourcePixel.fA * fLuma1 - a_fDecodedAlpha * fLuma2; + float fDeltaCr = a_frgbaSourcePixel.fA * fChromaR1 - a_fDecodedAlpha * fChromaR2; + float fDeltaCb = a_frgbaSourcePixel.fA * fChromaB1 - a_fDecodedAlpha * fChromaB2; + + float fDAlpha = a_fDecodedAlpha - a_frgbaSourcePixel.fA; + + // Favor Luma accuracy over Chroma, and Red over Blue + return LUMA_WEIGHT*fDeltaL*fDeltaL + + fDeltaCr*fDeltaCr + + CHROMA_BLUE_WEIGHT*fDeltaCb*fDeltaCb + + fDAlpha*fDAlpha; + #if 0 + float fDRed = a_frgbaDecodedPixel.fR - a_frgbaSourcePixel.fR; + float fDGreen = a_frgbaDecodedPixel.fG - a_frgbaSourcePixel.fG; + float fDBlue = a_frgbaDecodedPixel.fB - a_frgbaSourcePixel.fB; + return 2.0f * 3.0f * fDeltaL * fDeltaL + fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue; +#endif + } + else if (m_errormetric == ErrorMetric::NORMALXYZ) + { + float fDecodedX = 2.0f * a_frgbaDecodedColor.fR - 1.0f; + float fDecodedY = 2.0f * a_frgbaDecodedColor.fG - 1.0f; + float fDecodedZ = 2.0f * a_frgbaDecodedColor.fB - 1.0f; + + float fDecodedLength = sqrtf(fDecodedX*fDecodedX + fDecodedY*fDecodedY + fDecodedZ*fDecodedZ); + + if (fDecodedLength < 0.5f) + { + return 1.0f; + } + else if (fDecodedLength == 0.0f) + { + fDecodedX = 1.0f; + fDecodedY = 0.0f; + fDecodedZ = 0.0f; + } + else + { + fDecodedX /= fDecodedLength; + fDecodedY /= fDecodedLength; + fDecodedZ /= fDecodedLength; + } + + float fSourceX = 2.0f * a_frgbaSourcePixel.fR - 1.0f; + float fSourceY = 2.0f * a_frgbaSourcePixel.fG - 1.0f; + float fSourceZ = 2.0f * a_frgbaSourcePixel.fB - 1.0f; + + float fSourceLength = sqrtf(fSourceX*fSourceX + fSourceY*fSourceY + fSourceZ*fSourceZ); + + if (fSourceLength == 0.0f) + { + fSourceX = 1.0f; + fSourceY = 0.0f; + fSourceZ = 0.0f; + } + else + { + fSourceX /= fSourceLength; + fSourceY /= fSourceLength; + fSourceZ /= fSourceLength; + } + + float fDotProduct = fSourceX*fDecodedX + fSourceY*fDecodedY + fSourceZ*fDecodedZ; + float fNormalizedDotProduct = 1.0f - 0.5f * (fDotProduct + 1.0f); + float fDotProductError = fNormalizedDotProduct * fNormalizedDotProduct; + + float fLength2 = fDecodedX*fDecodedX + fDecodedY*fDecodedY + fDecodedZ*fDecodedZ; + float fLength2Error = fabsf(1.0f - fLength2); + + float fDeltaW = a_frgbaDecodedColor.fA - a_frgbaSourcePixel.fA; + float fErrorW = fDeltaW * fDeltaW; + + return fDotProductError + fLength2Error + fErrorW; + } + else // ErrorMetric::NUMERIC + { + assert(a_fDecodedAlpha >= 0.0f); + + float fDX = a_frgbaDecodedColor.fR - a_frgbaSourcePixel.fR; + float fDY = a_frgbaDecodedColor.fG - a_frgbaSourcePixel.fG; + float fDZ = a_frgbaDecodedColor.fB - a_frgbaSourcePixel.fB; + float fDW = a_frgbaDecodedColor.fA - a_frgbaSourcePixel.fA; + + return fDX*fDX + fDY*fDY + fDZ*fDZ + fDW*fDW; + } + + } + + // ---------------------------------------------------------------------------------------------------- + // + +} // namespace Etc + diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding.h b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding.h new file mode 100644 index 0000000000..c14c3b8616 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding.h @@ -0,0 +1,148 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcColorFloatRGBA.h" + +#include "EtcErrorMetric.h" + +#include +#include + +namespace Etc +{ + class Block4x4; + + // abstract base class for specific encodings + class Block4x4Encoding + { + public: + + static const unsigned int ROWS = 4; + static const unsigned int COLUMNS = 4; + static const unsigned int PIXELS = ROWS * COLUMNS; + static const float LUMA_WEIGHT; + static const float CHROMA_BLUE_WEIGHT; + + typedef enum + { + MODE_UNKNOWN, + // + MODE_ETC1, + MODE_T, + MODE_H, + MODE_PLANAR, + MODE_R11, + MODE_RG11, + // + MODES + } Mode; + + Block4x4Encoding(void); + //virtual ~Block4x4Encoding(void) =0; + virtual ~Block4x4Encoding(void) {} + virtual void InitFromSource(Block4x4 *a_pblockParent, + ColorFloatRGBA *a_pafrgbaSource, + + unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric) = 0; + + virtual void InitFromEncodingBits(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + + ErrorMetric a_errormetric) = 0; + + // perform an iteration of the encoding + // the first iteration must generate a complete, valid (if poor) encoding + virtual void PerformIteration(float a_fEffort) = 0; + + void CalcBlockError(void); + + inline float GetError(void) + { + assert(m_fError >= 0.0f); + + return m_fError; + } + + inline ColorFloatRGBA * GetDecodedColors(void) + { + return m_afrgbaDecodedColors; + } + + inline float * GetDecodedAlphas(void) + { + return m_afDecodedAlphas; + } + + virtual void SetEncodingBits(void) = 0; + + virtual bool GetFlip(void) = 0; + + virtual bool IsDifferential(void) = 0; + + virtual bool HasSeverelyBentDifferentialColors(void) const = 0; + + inline Mode GetMode(void) + { + return m_mode; + } + + inline bool IsDone(void) + { + return m_boolDone; + } + + inline void SetDoneIfPerfect() + { + if (GetError() == 0.0f) + { + m_boolDone = true; + } + } + + float CalcPixelError(ColorFloatRGBA a_frgbaDecodedColor, float a_fDecodedAlpha, + ColorFloatRGBA a_frgbaSourcePixel); + + protected: + + void Init(Block4x4 *a_pblockParent, + ColorFloatRGBA *a_pafrgbaSource, + + ErrorMetric a_errormetric); + + Block4x4 *m_pblockParent; + ColorFloatRGBA *m_pafrgbaSource; + + bool m_boolBorderPixels; // if block has any border pixels + + ColorFloatRGBA m_afrgbaDecodedColors[PIXELS]; // decoded RGB components, ignore Alpha + float m_afDecodedAlphas[PIXELS]; // decoded alpha component + float m_fError; // error for RGBA relative to m_pafrgbaSource + + // intermediate encoding + Mode m_mode; + + unsigned int m_uiEncodingIterations; + bool m_boolDone; // all iterations have been done + ErrorMetric m_errormetric; + + private: + + }; + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4EncodingBits.h b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4EncodingBits.h new file mode 100644 index 0000000000..4065700379 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4EncodingBits.h @@ -0,0 +1,315 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace Etc +{ + + // ################################################################################ + // Block4x4EncodingBits + // Base class for Block4x4EncodingBits_XXXX + // ################################################################################ + + class Block4x4EncodingBits + { + public: + + enum class Format + { + UNKNOWN, + // + RGB8, + RGBA8, + R11, + RG11, + RGB8A1, + // + FORMATS + }; + + static unsigned int GetBytesPerBlock(Format a_format) + { + switch (a_format) + { + case Format::RGB8: + case Format::R11: + case Format::RGB8A1: + return 8; + break; + + case Format::RGBA8: + case Format::RG11: + return 16; + break; + + default: + return 0; + break; + } + + } + + }; + + // ################################################################################ + // Block4x4EncodingBits_RGB8 + // Encoding bits for the RGB portion of ETC1, RGB8, RGB8A1 and RGBA8 + // ################################################################################ + + class Block4x4EncodingBits_RGB8 + { + public: + + static const unsigned int BYTES_PER_BLOCK = 8; + + inline Block4x4EncodingBits_RGB8(void) + { + assert(sizeof(Block4x4EncodingBits_RGB8) == BYTES_PER_BLOCK); + + for (unsigned int uiByte = 0; uiByte < BYTES_PER_BLOCK; uiByte++) + { + auc[uiByte] = 0; + } + + } + + typedef struct + { + unsigned red2 : 4; + unsigned red1 : 4; + // + unsigned green2 : 4; + unsigned green1 : 4; + // + unsigned blue2 : 4; + unsigned blue1 : 4; + // + unsigned flip : 1; + unsigned diff : 1; + unsigned cw2 : 3; + unsigned cw1 : 3; + // + unsigned int selectors; + } Individual; + + typedef struct + { + signed dred2 : 3; + unsigned red1 : 5; + // + signed dgreen2 : 3; + unsigned green1 : 5; + // + signed dblue2 : 3; + unsigned blue1 : 5; + // + unsigned flip : 1; + unsigned diff : 1; + unsigned cw2 : 3; + unsigned cw1 : 3; + // + unsigned int selectors; + } Differential; + + typedef struct + { + unsigned red1b : 2; + unsigned detect2 : 1; + unsigned red1a : 2; + unsigned detect1 : 3; + // + unsigned blue1 : 4; + unsigned green1 : 4; + // + unsigned green2 : 4; + unsigned red2 : 4; + // + unsigned db : 1; + unsigned diff : 1; + unsigned da : 2; + unsigned blue2 : 4; + // + unsigned int selectors; + } T; + + typedef struct + { + unsigned green1a : 3; + unsigned red1 : 4; + unsigned detect1 : 1; + // + unsigned blue1b : 2; + unsigned detect3 : 1; + unsigned blue1a : 1; + unsigned green1b : 1; + unsigned detect2 : 3; + // + unsigned green2a : 3; + unsigned red2 : 4; + unsigned blue1c : 1; + // + unsigned db : 1; + unsigned diff : 1; + unsigned da : 1; + unsigned blue2 : 4; + unsigned green2b : 1; + // + unsigned int selectors; + } H; + + typedef struct + { + unsigned originGreen1 : 1; + unsigned originRed : 6; + unsigned detect1 : 1; + // + unsigned originBlue1 : 1; + unsigned originGreen2 : 6; + unsigned detect2 : 1; + // + unsigned originBlue3 : 2; + unsigned detect4 : 1; + unsigned originBlue2 : 2; + unsigned detect3 : 3; + // + unsigned horizRed2 : 1; + unsigned diff : 1; + unsigned horizRed1 : 5; + unsigned originBlue4 : 1; + // + unsigned horizBlue1: 1; + unsigned horizGreen : 7; + // + unsigned vertRed1 : 3; + unsigned horizBlue2 : 5; + // + unsigned vertGreen1 : 5; + unsigned vertRed2 : 3; + // + unsigned vertBlue : 6; + unsigned vertGreen2 : 2; + } Planar; + + union + { + unsigned char auc[BYTES_PER_BLOCK]; + unsigned long int ul; + Individual individual; + Differential differential; + T t; + H h; + Planar planar; + }; + + }; + + // ################################################################################ + // Block4x4EncodingBits_A8 + // Encoding bits for the A portion of RGBA8 + // ################################################################################ + + class Block4x4EncodingBits_A8 + { + public: + + static const unsigned int BYTES_PER_BLOCK = 8; + static const unsigned int SELECTOR_BYTES = 6; + + typedef struct + { + unsigned base : 8; + unsigned table : 4; + unsigned multiplier : 4; + unsigned selectors0 : 8; + unsigned selectors1 : 8; + unsigned selectors2 : 8; + unsigned selectors3 : 8; + unsigned selectors4 : 8; + unsigned selectors5 : 8; + } Data; + + Data data; + + }; + + // ################################################################################ + // Block4x4EncodingBits_R11 + // Encoding bits for the R portion of R11 + // ################################################################################ + + class Block4x4EncodingBits_R11 + { + public: + + static const unsigned int BYTES_PER_BLOCK = 8; + static const unsigned int SELECTOR_BYTES = 6; + + typedef struct + { + unsigned base : 8; + unsigned table : 4; + unsigned multiplier : 4; + unsigned selectors0 : 8; + unsigned selectors1 : 8; + unsigned selectors2 : 8; + unsigned selectors3 : 8; + unsigned selectors4 : 8; + unsigned selectors5 : 8; + } Data; + + Data data; + + }; + + class Block4x4EncodingBits_RG11 + { + public: + + static const unsigned int BYTES_PER_BLOCK = 16; + static const unsigned int SELECTOR_BYTES = 12; + + typedef struct + { + //Red portion + unsigned baseR : 8; + unsigned tableIndexR : 4; + unsigned multiplierR : 4; + unsigned selectorsR0 : 8; + unsigned selectorsR1 : 8; + unsigned selectorsR2 : 8; + unsigned selectorsR3 : 8; + unsigned selectorsR4 : 8; + unsigned selectorsR5 : 8; + //Green portion + unsigned baseG : 8; + unsigned tableIndexG : 4; + unsigned multiplierG : 4; + unsigned selectorsG0 : 8; + unsigned selectorsG1 : 8; + unsigned selectorsG2 : 8; + unsigned selectorsG3 : 8; + unsigned selectorsG4 : 8; + unsigned selectorsG5 : 8; + } Data; + + Data data; + + }; + +} diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_ETC1.cpp b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_ETC1.cpp new file mode 100644 index 0000000000..a27f74c0d5 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_ETC1.cpp @@ -0,0 +1,1281 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +EtcBlock4x4Encoding_ETC1.cpp + +Block4x4Encoding_ETC1 is the encoder to use when targetting file format ETC1. This encoder is also +used for the ETC1 subset of file format RGB8, RGBA8 and RGB8A1 + +*/ + +#include "EtcConfig.h" +#include "EtcBlock4x4Encoding_ETC1.h" + +#include "EtcBlock4x4.h" +#include "EtcBlock4x4EncodingBits.h" +#include "EtcDifferentialTrys.h" + +#include +#include +#include +#include +#include + +namespace Etc +{ + + // pixel processing order if the flip bit = 0 (horizontal split) + const unsigned int Block4x4Encoding_ETC1::s_auiPixelOrderFlip0[PIXELS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + + // pixel processing order if the flip bit = 1 (vertical split) + const unsigned int Block4x4Encoding_ETC1::s_auiPixelOrderFlip1[PIXELS] = { 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15 }; + + // pixel processing order for horizontal scan (ETC normally does a vertical scan) + const unsigned int Block4x4Encoding_ETC1::s_auiPixelOrderHScan[PIXELS] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 }; + + // pixel indices for different block halves + const unsigned int Block4x4Encoding_ETC1::s_auiLeftPixelMapping[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + const unsigned int Block4x4Encoding_ETC1::s_auiRightPixelMapping[8] = { 8, 9, 10, 11, 12, 13, 14, 15 }; + const unsigned int Block4x4Encoding_ETC1::s_auiTopPixelMapping[8] = { 0, 1, 4, 5, 8, 9, 12, 13 }; + const unsigned int Block4x4Encoding_ETC1::s_auiBottomPixelMapping[8] = { 2, 3, 6, 7, 10, 11, 14, 15 }; + + // CW ranges that the ETC1 decoders use + // CW is basically a contrast for the different selector bits, since these values are offsets to the base color + // the first axis in the array is indexed by the CW in the encoding bits + // the second axis in the array is indexed by the selector bits + float Block4x4Encoding_ETC1::s_aafCwTable[CW_RANGES][SELECTORS] = + { + { 2.0f / 255.0f, 8.0f / 255.0f, -2.0f / 255.0f, -8.0f / 255.0f }, + { 5.0f / 255.0f, 17.0f / 255.0f, -5.0f / 255.0f, -17.0f / 255.0f }, + { 9.0f / 255.0f, 29.0f / 255.0f, -9.0f / 255.0f, -29.0f / 255.0f }, + { 13.0f / 255.0f, 42.0f / 255.0f, -13.0f / 255.0f, -42.0f / 255.0f }, + { 18.0f / 255.0f, 60.0f / 255.0f, -18.0f / 255.0f, -60.0f / 255.0f }, + { 24.0f / 255.0f, 80.0f / 255.0f, -24.0f / 255.0f, -80.0f / 255.0f }, + { 33.0f / 255.0f, 106.0f / 255.0f, -33.0f / 255.0f, -106.0f / 255.0f }, + { 47.0f / 255.0f, 183.0f / 255.0f, -47.0f / 255.0f, -183.0f / 255.0f } + }; + + // ---------------------------------------------------------------------------------------------------- + // + Block4x4Encoding_ETC1::Block4x4Encoding_ETC1(void) + { + m_mode = MODE_ETC1; + m_boolDiff = false; + m_boolFlip = false; + m_frgbaColor1 = ColorFloatRGBA(); + m_frgbaColor2 = ColorFloatRGBA(); + m_uiCW1 = 0; + m_uiCW2 = 0; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = 0; + m_afDecodedAlphas[uiPixel] = 1.0f; + } + + m_boolMostLikelyFlip = false; + + m_fError = -1.0f; + + m_fError1 = -1.0f; + m_fError2 = -1.0f; + m_boolSeverelyBentDifferentialColors = false; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_afDecodedAlphas[uiPixel] = 1.0f; + } + + } + + Block4x4Encoding_ETC1::~Block4x4Encoding_ETC1(void) {} + + // ---------------------------------------------------------------------------------------------------- + // initialization prior to encoding + // a_pblockParent points to the block associated with this encoding + // a_errormetric is used to choose the best encoding + // a_pafrgbaSource points to a 4x4 block subset of the source image + // a_paucEncodingBits points to the final encoding bits + // + void Block4x4Encoding_ETC1::InitFromSource(Block4x4 *a_pblockParent, + ColorFloatRGBA *a_pafrgbaSource, + unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric) + { + + Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric); + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_afDecodedAlphas[uiPixel] = 1.0f; + } + + m_fError = -1.0f; + + m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)(a_paucEncodingBits); + + } + + // ---------------------------------------------------------------------------------------------------- + // initialization from the encoding bits of a previous encoding + // a_pblockParent points to the block associated with this encoding + // a_errormetric is used to choose the best encoding + // a_pafrgbaSource points to a 4x4 block subset of the source image + // a_paucEncodingBits points to the final encoding bits of a previous encoding + // + void Block4x4Encoding_ETC1::InitFromEncodingBits(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + ErrorMetric a_errormetric) + { + + Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric); + m_fError = -1.0f; + + m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits; + + m_mode = MODE_ETC1; + m_boolDiff = m_pencodingbitsRGB8->individual.diff; + m_boolFlip = m_pencodingbitsRGB8->individual.flip; + if (m_boolDiff) + { + int iR2 = (int)(m_pencodingbitsRGB8->differential.red1 + m_pencodingbitsRGB8->differential.dred2); + if (iR2 < 0) + { + iR2 = 0; + } + else if (iR2 > 31) + { + iR2 = 31; + } + + int iG2 = (int)(m_pencodingbitsRGB8->differential.green1 + m_pencodingbitsRGB8->differential.dgreen2); + if (iG2 < 0) + { + iG2 = 0; + } + else if (iG2 > 31) + { + iG2 = 31; + } + + int iB2 = (int)(m_pencodingbitsRGB8->differential.blue1 + m_pencodingbitsRGB8->differential.dblue2); + if (iB2 < 0) + { + iB2 = 0; + } + else if (iB2 > 31) + { + iB2 = 31; + } + + m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5(m_pencodingbitsRGB8->differential.red1, m_pencodingbitsRGB8->differential.green1, m_pencodingbitsRGB8->differential.blue1); + m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iR2, (unsigned char)iG2, (unsigned char)iB2); + + } + else + { + m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(m_pencodingbitsRGB8->individual.red1, m_pencodingbitsRGB8->individual.green1, m_pencodingbitsRGB8->individual.blue1); + m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(m_pencodingbitsRGB8->individual.red2, m_pencodingbitsRGB8->individual.green2, m_pencodingbitsRGB8->individual.blue2); + } + + m_uiCW1 = m_pencodingbitsRGB8->individual.cw1; + m_uiCW2 = m_pencodingbitsRGB8->individual.cw2; + + InitFromEncodingBits_Selectors(); + + Decode(); + + CalcBlockError(); + } + + // ---------------------------------------------------------------------------------------------------- + // init the selectors from a prior encoding + // + void Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors(void) + { + + unsigned char *paucSelectors = (unsigned char *)&m_pencodingbitsRGB8->individual.selectors; + + for (unsigned int iPixel = 0; iPixel < PIXELS; iPixel++) + { + unsigned int uiByteMSB = (unsigned int)(1 - (iPixel / 8)); + unsigned int uiByteLSB = (unsigned int)(3 - (iPixel / 8)); + unsigned int uiShift = (unsigned int)(iPixel & 7); + + unsigned int uiSelectorMSB = (unsigned int)((paucSelectors[uiByteMSB] >> uiShift) & 1); + unsigned int uiSelectorLSB = (unsigned int)((paucSelectors[uiByteLSB] >> uiShift) & 1); + + m_auiSelectors[iPixel] = (uiSelectorMSB << 1) + uiSelectorLSB; + } + + } + + // ---------------------------------------------------------------------------------------------------- + // perform a single encoding iteration + // replace the encoding if a better encoding was found + // subsequent iterations generally take longer for each iteration + // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort + // + void Block4x4Encoding_ETC1::PerformIteration(float a_fEffort) + { + assert(!m_boolDone); + + switch (m_uiEncodingIterations) + { + case 0: + PerformFirstIteration(); + break; + + case 1: + TryDifferential(m_boolMostLikelyFlip, 1, 0, 0); + break; + + case 2: + TryIndividual(m_boolMostLikelyFlip, 1); + if (a_fEffort <= 49.5f) + { + m_boolDone = true; + } + break; + + case 3: + TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0); + if (a_fEffort <= 59.5f) + { + m_boolDone = true; + } + break; + + case 4: + TryIndividual(!m_boolMostLikelyFlip, 1); + if (a_fEffort <= 69.5f) + { + m_boolDone = true; + } + break; + + case 5: + TryDegenerates1(); + if (a_fEffort <= 79.5f) + { + m_boolDone = true; + } + break; + + case 6: + TryDegenerates2(); + if (a_fEffort <= 89.5f) + { + m_boolDone = true; + } + break; + + case 7: + TryDegenerates3(); + if (a_fEffort <= 99.5f) + { + m_boolDone = true; + } + break; + + case 8: + TryDegenerates4(); + m_boolDone = true; + break; + + default: + assert(0); + break; + } + + m_uiEncodingIterations++; + SetDoneIfPerfect(); + } + + // ---------------------------------------------------------------------------------------------------- + // find best initial encoding to ensure block has a valid encoding + // + void Block4x4Encoding_ETC1::PerformFirstIteration(void) + { + CalculateMostLikelyFlip(); + + m_fError = FLT_MAX; + + TryDifferential(m_boolMostLikelyFlip, 0, 0, 0); + SetDoneIfPerfect(); + if (m_boolDone) + { + return; + } + + TryIndividual(m_boolMostLikelyFlip, 0); + SetDoneIfPerfect(); + if (m_boolDone) + { + return; + } + TryDifferential(!m_boolMostLikelyFlip, 0, 0, 0); + SetDoneIfPerfect(); + if (m_boolDone) + { + return; + } + TryIndividual(!m_boolMostLikelyFlip, 0); + + } + + // ---------------------------------------------------------------------------------------------------- + // algorithm: + // create a source average color for the Left, Right, Top and Bottom halves using the 8 pixels in each half + // note: the "gray line" is the line of equal delta RGB that goes thru the average color + // for each half: + // see how close each of the 8 pixels are to the "gray line" that goes thru the source average color + // create an error value that is the sum of the distances from the gray line + // h_error is the sum of Left and Right errors + // v_error is the sum of Top and Bottom errors + // + void Block4x4Encoding_ETC1::CalculateMostLikelyFlip(void) + { + static const bool DEBUG_PRINT = false; + + CalculateSourceAverages(); + + float fLeftGrayErrorSum = 0.0f; + float fRightGrayErrorSum = 0.0f; + float fTopGrayErrorSum = 0.0f; + float fBottomGrayErrorSum = 0.0f; + + for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) + { + ColorFloatRGBA *pfrgbaLeft = &m_pafrgbaSource[uiPixel]; + ColorFloatRGBA *pfrgbaRight = &m_pafrgbaSource[uiPixel + 8]; + ColorFloatRGBA *pfrgbaTop = &m_pafrgbaSource[s_auiTopPixelMapping[uiPixel]]; + ColorFloatRGBA *pfrgbaBottom = &m_pafrgbaSource[s_auiBottomPixelMapping[uiPixel]]; + + float fLeftGrayError = CalcGrayDistance2(*pfrgbaLeft, m_frgbaSourceAverageLeft); + float fRightGrayError = CalcGrayDistance2(*pfrgbaRight, m_frgbaSourceAverageRight); + float fTopGrayError = CalcGrayDistance2(*pfrgbaTop, m_frgbaSourceAverageTop); + float fBottomGrayError = CalcGrayDistance2(*pfrgbaBottom, m_frgbaSourceAverageBottom); + + fLeftGrayErrorSum += fLeftGrayError; + fRightGrayErrorSum += fRightGrayError; + fTopGrayErrorSum += fTopGrayError; + fBottomGrayErrorSum += fBottomGrayError; + } + + if (DEBUG_PRINT) + { + printf("\n%.2f %.2f\n", fLeftGrayErrorSum + fRightGrayErrorSum, fTopGrayErrorSum + fBottomGrayErrorSum); + } + + m_boolMostLikelyFlip = (fTopGrayErrorSum + fBottomGrayErrorSum) < (fLeftGrayErrorSum + fRightGrayErrorSum); + + } + + // ---------------------------------------------------------------------------------------------------- + // calculate source pixel averages for each 2x2 quadrant in a 4x4 block + // these are used to determine the averages for each of the 4 different halves (left, right, top, bottom) + // ignore pixels that have alpha == NAN (these are border pixels outside of the source image) + // weight the averages based on a pixel's alpha + // + void Block4x4Encoding_ETC1::CalculateSourceAverages(void) + { + static const bool DEBUG_PRINT = false; + + bool boolRGBX = m_pblockParent->GetImageSource()->GetErrorMetric() == ErrorMetric::RGBX; + + if (m_pblockParent->GetSourceAlphaMix() == Block4x4::SourceAlphaMix::OPAQUE || boolRGBX) + { + ColorFloatRGBA frgbaSumUL = m_pafrgbaSource[0] + m_pafrgbaSource[1] + m_pafrgbaSource[4] + m_pafrgbaSource[5]; + ColorFloatRGBA frgbaSumLL = m_pafrgbaSource[2] + m_pafrgbaSource[3] + m_pafrgbaSource[6] + m_pafrgbaSource[7]; + ColorFloatRGBA frgbaSumUR = m_pafrgbaSource[8] + m_pafrgbaSource[9] + m_pafrgbaSource[12] + m_pafrgbaSource[13]; + ColorFloatRGBA frgbaSumLR = m_pafrgbaSource[10] + m_pafrgbaSource[11] + m_pafrgbaSource[14] + m_pafrgbaSource[15]; + + m_frgbaSourceAverageLeft = (frgbaSumUL + frgbaSumLL) * 0.125f; + m_frgbaSourceAverageRight = (frgbaSumUR + frgbaSumLR) * 0.125f; + m_frgbaSourceAverageTop = (frgbaSumUL + frgbaSumUR) * 0.125f; + m_frgbaSourceAverageBottom = (frgbaSumLL + frgbaSumLR) * 0.125f; + } + else + { + float afSourceAlpha[PIXELS]; + + // treat alpha NAN as 0.0f + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + afSourceAlpha[uiPixel] = isnan(m_pafrgbaSource[uiPixel].fA) ? + 0.0f : + m_pafrgbaSource[uiPixel].fA; + } + + ColorFloatRGBA afrgbaAlphaWeightedSource[PIXELS]; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + afrgbaAlphaWeightedSource[uiPixel] = m_pafrgbaSource[uiPixel] * afSourceAlpha[uiPixel]; + } + + ColorFloatRGBA frgbaSumUL = afrgbaAlphaWeightedSource[0] + + afrgbaAlphaWeightedSource[1] + + afrgbaAlphaWeightedSource[4] + + afrgbaAlphaWeightedSource[5]; + + ColorFloatRGBA frgbaSumLL = afrgbaAlphaWeightedSource[2] + + afrgbaAlphaWeightedSource[3] + + afrgbaAlphaWeightedSource[6] + + afrgbaAlphaWeightedSource[7]; + + ColorFloatRGBA frgbaSumUR = afrgbaAlphaWeightedSource[8] + + afrgbaAlphaWeightedSource[9] + + afrgbaAlphaWeightedSource[12] + + afrgbaAlphaWeightedSource[13]; + + ColorFloatRGBA frgbaSumLR = afrgbaAlphaWeightedSource[10] + + afrgbaAlphaWeightedSource[11] + + afrgbaAlphaWeightedSource[14] + + afrgbaAlphaWeightedSource[15]; + + float fWeightSumUL = afSourceAlpha[0] + + afSourceAlpha[1] + + afSourceAlpha[4] + + afSourceAlpha[5]; + + float fWeightSumLL = afSourceAlpha[2] + + afSourceAlpha[3] + + afSourceAlpha[6] + + afSourceAlpha[7]; + + float fWeightSumUR = afSourceAlpha[8] + + afSourceAlpha[9] + + afSourceAlpha[12] + + afSourceAlpha[13]; + + float fWeightSumLR = afSourceAlpha[10] + + afSourceAlpha[11] + + afSourceAlpha[14] + + afSourceAlpha[15]; + + ColorFloatRGBA frgbaSumLeft = frgbaSumUL + frgbaSumLL; + ColorFloatRGBA frgbaSumRight = frgbaSumUR + frgbaSumLR; + ColorFloatRGBA frgbaSumTop = frgbaSumUL + frgbaSumUR; + ColorFloatRGBA frgbaSumBottom = frgbaSumLL + frgbaSumLR; + + float fWeightSumLeft = fWeightSumUL + fWeightSumLL; + float fWeightSumRight = fWeightSumUR + fWeightSumLR; + float fWeightSumTop = fWeightSumUL + fWeightSumUR; + float fWeightSumBottom = fWeightSumLL + fWeightSumLR; + + // check to see if there is at least 1 pixel with non-zero alpha + // completely transparent block should not make it to this code + assert((fWeightSumLeft + fWeightSumRight) > 0.0f); + assert((fWeightSumTop + fWeightSumBottom) > 0.0f); + + if (fWeightSumLeft > 0.0f) + { + m_frgbaSourceAverageLeft = frgbaSumLeft * (1.0f/fWeightSumLeft); + } + if (fWeightSumRight > 0.0f) + { + m_frgbaSourceAverageRight = frgbaSumRight * (1.0f/fWeightSumRight); + } + if (fWeightSumTop > 0.0f) + { + m_frgbaSourceAverageTop = frgbaSumTop * (1.0f/fWeightSumTop); + } + if (fWeightSumBottom > 0.0f) + { + m_frgbaSourceAverageBottom = frgbaSumBottom * (1.0f/fWeightSumBottom); + } + + if (fWeightSumLeft == 0.0f) + { + assert(fWeightSumRight > 0.0f); + m_frgbaSourceAverageLeft = m_frgbaSourceAverageRight; + } + if (fWeightSumRight == 0.0f) + { + assert(fWeightSumLeft > 0.0f); + m_frgbaSourceAverageRight = m_frgbaSourceAverageLeft; + } + if (fWeightSumTop == 0.0f) + { + assert(fWeightSumBottom > 0.0f); + m_frgbaSourceAverageTop = m_frgbaSourceAverageBottom; + } + if (fWeightSumBottom == 0.0f) + { + assert(fWeightSumTop > 0.0f); + m_frgbaSourceAverageBottom = m_frgbaSourceAverageTop; + } + } + + + + if (DEBUG_PRINT) + { + printf("\ntarget: [%.2f,%.2f,%.2f] [%.2f,%.2f,%.2f] [%.2f,%.2f,%.2f] [%.2f,%.2f,%.2f]\n", + m_frgbaSourceAverageLeft.fR, m_frgbaSourceAverageLeft.fG, m_frgbaSourceAverageLeft.fB, + m_frgbaSourceAverageRight.fR, m_frgbaSourceAverageRight.fG, m_frgbaSourceAverageRight.fB, + m_frgbaSourceAverageTop.fR, m_frgbaSourceAverageTop.fG, m_frgbaSourceAverageTop.fB, + m_frgbaSourceAverageBottom.fR, m_frgbaSourceAverageBottom.fG, m_frgbaSourceAverageBottom.fB); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // try an ETC1 differential mode encoding + // use a_boolFlip to set the encoding F bit + // use a_uiRadius to alter basecolor components in the range[-a_uiRadius:a_uiRadius] + // use a_iGrayOffset1 and a_iGrayOffset2 to offset the basecolor to search for degenerate encodings + // replace the encoding if the encoding error is less than previous encoding + // + void Block4x4Encoding_ETC1::TryDifferential(bool a_boolFlip, unsigned int a_uiRadius, + int a_iGrayOffset1, int a_iGrayOffset2) + { + + ColorFloatRGBA frgbaColor1; + ColorFloatRGBA frgbaColor2; + + const unsigned int *pauiPixelMapping1; + const unsigned int *pauiPixelMapping2; + + if (a_boolFlip) + { + frgbaColor1 = m_frgbaSourceAverageTop; + frgbaColor2 = m_frgbaSourceAverageBottom; + + pauiPixelMapping1 = s_auiTopPixelMapping; + pauiPixelMapping2 = s_auiBottomPixelMapping; + } + else + { + frgbaColor1 = m_frgbaSourceAverageLeft; + frgbaColor2 = m_frgbaSourceAverageRight; + + pauiPixelMapping1 = s_auiLeftPixelMapping; + pauiPixelMapping2 = s_auiRightPixelMapping; + } + + DifferentialTrys trys(frgbaColor1, frgbaColor2, pauiPixelMapping1, pauiPixelMapping2, + a_uiRadius, a_iGrayOffset1, a_iGrayOffset2); + + Block4x4Encoding_ETC1 encodingTry = *this; + encodingTry.m_boolFlip = a_boolFlip; + + encodingTry.TryDifferentialHalf(&trys.m_half1); + encodingTry.TryDifferentialHalf(&trys.m_half2); + + // find best halves that are within differential range + DifferentialTrys::Try *ptryBest1 = nullptr; + DifferentialTrys::Try *ptryBest2 = nullptr; + encodingTry.m_fError = FLT_MAX; + + // see if the best of each half are in differential range + int iDRed = trys.m_half2.m_ptryBest->m_iRed - trys.m_half1.m_ptryBest->m_iRed; + int iDGreen = trys.m_half2.m_ptryBest->m_iGreen - trys.m_half1.m_ptryBest->m_iGreen; + int iDBlue = trys.m_half2.m_ptryBest->m_iBlue - trys.m_half1.m_ptryBest->m_iBlue; + if (iDRed >= -4 && iDRed <= 3 && iDGreen >= -4 && iDGreen <= 3 && iDBlue >= -4 && iDBlue <= 3) + { + ptryBest1 = trys.m_half1.m_ptryBest; + ptryBest2 = trys.m_half2.m_ptryBest; + encodingTry.m_fError = trys.m_half1.m_ptryBest->m_fError + trys.m_half2.m_ptryBest->m_fError; + } + else + { + // else, find the next best halves that are in differential range + for (DifferentialTrys::Try *ptry1 = &trys.m_half1.m_atry[0]; + ptry1 < &trys.m_half1.m_atry[trys.m_half1.m_uiTrys]; + ptry1++) + { + for (DifferentialTrys::Try *ptry2 = &trys.m_half2.m_atry[0]; + ptry2 < &trys.m_half2.m_atry[trys.m_half2.m_uiTrys]; + ptry2++) + { + iDRed = ptry2->m_iRed - ptry1->m_iRed; + bool boolValidRedDelta = iDRed <= 3 && iDRed >= -4; + iDGreen = ptry2->m_iGreen - ptry1->m_iGreen; + bool boolValidGreenDelta = iDGreen <= 3 && iDGreen >= -4; + iDBlue = ptry2->m_iBlue - ptry1->m_iBlue; + bool boolValidBlueDelta = iDBlue <= 3 && iDBlue >= -4; + + if (boolValidRedDelta && boolValidGreenDelta && boolValidBlueDelta) + { + float fError = ptry1->m_fError + ptry2->m_fError; + + if (fError < encodingTry.m_fError) + { + encodingTry.m_fError = fError; + + ptryBest1 = ptry1; + ptryBest2 = ptry2; + } + } + + } + } + assert(encodingTry.m_fError < FLT_MAX); + assert(ptryBest1 != nullptr); + assert(ptryBest2 != nullptr); + } + + if (encodingTry.m_fError < m_fError) + { + m_mode = MODE_ETC1; + m_boolDiff = true; + m_boolFlip = encodingTry.m_boolFlip; + m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest1->m_iRed, (unsigned char)ptryBest1->m_iGreen, (unsigned char)ptryBest1->m_iBlue); + m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest2->m_iRed, (unsigned char)ptryBest2->m_iGreen, (unsigned char)ptryBest2->m_iBlue); + m_uiCW1 = ptryBest1->m_uiCW; + m_uiCW2 = ptryBest2->m_uiCW; + + for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS / 2; uiPixelOrder++) + { + unsigned int uiPixel1 = pauiPixelMapping1[uiPixelOrder]; + unsigned int uiPixel2 = pauiPixelMapping2[uiPixelOrder]; + + unsigned int uiSelector1 = ptryBest1->m_auiSelectors[uiPixelOrder]; + unsigned int uiSelector2 = ptryBest2->m_auiSelectors[uiPixelOrder]; + + m_auiSelectors[uiPixel1] = uiSelector1; + m_auiSelectors[uiPixel2] = ptryBest2->m_auiSelectors[uiPixelOrder]; + + float fDeltaRGB1 = s_aafCwTable[m_uiCW1][uiSelector1]; + float fDeltaRGB2 = s_aafCwTable[m_uiCW2][uiSelector2]; + + m_afrgbaDecodedColors[uiPixel1] = (m_frgbaColor1 + fDeltaRGB1).ClampRGB(); + m_afrgbaDecodedColors[uiPixel2] = (m_frgbaColor2 + fDeltaRGB2).ClampRGB(); + } + + m_fError1 = ptryBest1->m_fError; + m_fError2 = ptryBest2->m_fError; + m_boolSeverelyBentDifferentialColors = trys.m_boolSeverelyBentColors; + m_fError = m_fError1 + m_fError2; + + // sanity check + { + int iRed1 = m_frgbaColor1.IntRed(31.0f); + int iGreen1 = m_frgbaColor1.IntGreen(31.0f); + int iBlue1 = m_frgbaColor1.IntBlue(31.0f); + + int iRed2 = m_frgbaColor2.IntRed(31.0f); + int iGreen2 = m_frgbaColor2.IntGreen(31.0f); + int iBlue2 = m_frgbaColor2.IntBlue(31.0f); + + iDRed = iRed2 - iRed1; + iDGreen = iGreen2 - iGreen1; + iDBlue = iBlue2 - iBlue1; + + assert(iDRed >= -4 && iDRed < 4); + assert(iDGreen >= -4 && iDGreen < 4); + assert(iDBlue >= -4 && iDBlue < 4); + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // try an ETC1 differential mode encoding for a half of a 4x4 block + // vary the basecolor components using a radius + // + void Block4x4Encoding_ETC1::TryDifferentialHalf(DifferentialTrys::Half *a_phalf) + { + + a_phalf->m_ptryBest = nullptr; + float fBestTryError = FLT_MAX; + + a_phalf->m_uiTrys = 0; + for (int iRed = a_phalf->m_iRed - (int)a_phalf->m_uiRadius; + iRed <= a_phalf->m_iRed + (int)a_phalf->m_uiRadius; + iRed++) + { + assert(iRed >= 0 && iRed <= 31); + + for (int iGreen = a_phalf->m_iGreen - (int)a_phalf->m_uiRadius; + iGreen <= a_phalf->m_iGreen + (int)a_phalf->m_uiRadius; + iGreen++) + { + assert(iGreen >= 0 && iGreen <= 31); + + for (int iBlue = a_phalf->m_iBlue - (int)a_phalf->m_uiRadius; + iBlue <= a_phalf->m_iBlue + (int)a_phalf->m_uiRadius; + iBlue++) + { + assert(iBlue >= 0 && iBlue <= 31); + + DifferentialTrys::Try *ptry = &a_phalf->m_atry[a_phalf->m_uiTrys]; + assert(ptry < &a_phalf->m_atry[DifferentialTrys::Half::MAX_TRYS]); + + ptry->m_iRed = iRed; + ptry->m_iGreen = iGreen; + ptry->m_iBlue = iBlue; + ptry->m_fError = FLT_MAX; + ColorFloatRGBA frgbaColor = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iRed, (unsigned char)iGreen, (unsigned char)iBlue); + + // try each CW + for (unsigned int uiCW = 0; uiCW < CW_RANGES; uiCW++) + { + unsigned int auiPixelSelectors[PIXELS / 2]; + ColorFloatRGBA afrgbaDecodedPixels[PIXELS / 2]; + float afPixelErrors[PIXELS / 2] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, + FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; + + // pre-compute decoded pixels for each selector + ColorFloatRGBA afrgbaSelectors[SELECTORS]; + assert(SELECTORS == 4); + afrgbaSelectors[0] = (frgbaColor + s_aafCwTable[uiCW][0]).ClampRGB(); + afrgbaSelectors[1] = (frgbaColor + s_aafCwTable[uiCW][1]).ClampRGB(); + afrgbaSelectors[2] = (frgbaColor + s_aafCwTable[uiCW][2]).ClampRGB(); + afrgbaSelectors[3] = (frgbaColor + s_aafCwTable[uiCW][3]).ClampRGB(); + + for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) + { + ColorFloatRGBA *pfrgbaSourcePixel = &m_pafrgbaSource[a_phalf->m_pauiPixelMapping[uiPixel]]; + ColorFloatRGBA frgbaDecodedPixel; + + for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++) + { + frgbaDecodedPixel = afrgbaSelectors[uiSelector]; + + float fPixelError; + + fPixelError = CalcPixelError(frgbaDecodedPixel, m_afDecodedAlphas[a_phalf->m_pauiPixelMapping[uiPixel]], + *pfrgbaSourcePixel); + + if (fPixelError < afPixelErrors[uiPixel]) + { + auiPixelSelectors[uiPixel] = uiSelector; + afrgbaDecodedPixels[uiPixel] = frgbaDecodedPixel; + afPixelErrors[uiPixel] = fPixelError; + } + + } + } + + // add up all pixel errors + float fCWError = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) + { + fCWError += afPixelErrors[uiPixel]; + } + + // if best CW so far + if (fCWError < ptry->m_fError) + { + ptry->m_uiCW = uiCW; + for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) + { + ptry->m_auiSelectors[uiPixel] = auiPixelSelectors[uiPixel]; + } + ptry->m_fError = fCWError; + } + + } + + if (ptry->m_fError < fBestTryError) + { + a_phalf->m_ptryBest = ptry; + fBestTryError = ptry->m_fError; + } + + assert(ptry->m_fError < FLT_MAX); + + a_phalf->m_uiTrys++; + } + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // try an ETC1 individual mode encoding + // use a_boolFlip to set the encoding F bit + // use a_uiRadius to alter basecolor components in the range[-a_uiRadius:a_uiRadius] + // replace the encoding if the encoding error is less than previous encoding + // + void Block4x4Encoding_ETC1::TryIndividual(bool a_boolFlip, unsigned int a_uiRadius) + { + + ColorFloatRGBA frgbaColor1; + ColorFloatRGBA frgbaColor2; + + const unsigned int *pauiPixelMapping1; + const unsigned int *pauiPixelMapping2; + + if (a_boolFlip) + { + frgbaColor1 = m_frgbaSourceAverageTop; + frgbaColor2 = m_frgbaSourceAverageBottom; + + pauiPixelMapping1 = s_auiTopPixelMapping; + pauiPixelMapping2 = s_auiBottomPixelMapping; + } + else + { + frgbaColor1 = m_frgbaSourceAverageLeft; + frgbaColor2 = m_frgbaSourceAverageRight; + + pauiPixelMapping1 = s_auiLeftPixelMapping; + pauiPixelMapping2 = s_auiRightPixelMapping; + } + + IndividualTrys trys(frgbaColor1, frgbaColor2, pauiPixelMapping1, pauiPixelMapping2, a_uiRadius); + + Block4x4Encoding_ETC1 encodingTry = *this; + encodingTry.m_boolFlip = a_boolFlip; + + encodingTry.TryIndividualHalf(&trys.m_half1); + encodingTry.TryIndividualHalf(&trys.m_half2); + + // use the best of each half + IndividualTrys::Try *ptryBest1 = trys.m_half1.m_ptryBest; + IndividualTrys::Try *ptryBest2 = trys.m_half2.m_ptryBest; + encodingTry.m_fError = trys.m_half1.m_ptryBest->m_fError + trys.m_half2.m_ptryBest->m_fError; + + if (encodingTry.m_fError < m_fError) + { + m_mode = MODE_ETC1; + m_boolDiff = false; + m_boolFlip = encodingTry.m_boolFlip; + m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)ptryBest1->m_iRed, (unsigned char)ptryBest1->m_iGreen, (unsigned char)ptryBest1->m_iBlue); + m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)ptryBest2->m_iRed, (unsigned char)ptryBest2->m_iGreen, (unsigned char)ptryBest2->m_iBlue); + m_uiCW1 = ptryBest1->m_uiCW; + m_uiCW2 = ptryBest2->m_uiCW; + + for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS / 2; uiPixelOrder++) + { + unsigned int uiPixel1 = pauiPixelMapping1[uiPixelOrder]; + unsigned int uiPixel2 = pauiPixelMapping2[uiPixelOrder]; + + unsigned int uiSelector1 = ptryBest1->m_auiSelectors[uiPixelOrder]; + unsigned int uiSelector2 = ptryBest2->m_auiSelectors[uiPixelOrder]; + + m_auiSelectors[uiPixel1] = uiSelector1; + m_auiSelectors[uiPixel2] = ptryBest2->m_auiSelectors[uiPixelOrder]; + + float fDeltaRGB1 = s_aafCwTable[m_uiCW1][uiSelector1]; + float fDeltaRGB2 = s_aafCwTable[m_uiCW2][uiSelector2]; + + m_afrgbaDecodedColors[uiPixel1] = (m_frgbaColor1 + fDeltaRGB1).ClampRGB(); + m_afrgbaDecodedColors[uiPixel2] = (m_frgbaColor2 + fDeltaRGB2).ClampRGB(); + } + + m_fError1 = ptryBest1->m_fError; + m_fError2 = ptryBest2->m_fError; + m_fError = m_fError1 + m_fError2; + } + + } + + // ---------------------------------------------------------------------------------------------------- + // try an ETC1 differential mode encoding for a half of a 4x4 block + // vary the basecolor components using a radius + // + void Block4x4Encoding_ETC1::TryIndividualHalf(IndividualTrys::Half *a_phalf) + { + + a_phalf->m_ptryBest = nullptr; + float fBestTryError = FLT_MAX; + + a_phalf->m_uiTrys = 0; + for (int iRed = a_phalf->m_iRed - (int)a_phalf->m_uiRadius; + iRed <= a_phalf->m_iRed + (int)a_phalf->m_uiRadius; + iRed++) + { + assert(iRed >= 0 && iRed <= 15); + + for (int iGreen = a_phalf->m_iGreen - (int)a_phalf->m_uiRadius; + iGreen <= a_phalf->m_iGreen + (int)a_phalf->m_uiRadius; + iGreen++) + { + assert(iGreen >= 0 && iGreen <= 15); + + for (int iBlue = a_phalf->m_iBlue - (int)a_phalf->m_uiRadius; + iBlue <= a_phalf->m_iBlue + (int)a_phalf->m_uiRadius; + iBlue++) + { + assert(iBlue >= 0 && iBlue <= 15); + + IndividualTrys::Try *ptry = &a_phalf->m_atry[a_phalf->m_uiTrys]; + assert(ptry < &a_phalf->m_atry[IndividualTrys::Half::MAX_TRYS]); + + ptry->m_iRed = iRed; + ptry->m_iGreen = iGreen; + ptry->m_iBlue = iBlue; + ptry->m_fError = FLT_MAX; + ColorFloatRGBA frgbaColor = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed, (unsigned char)iGreen, (unsigned char)iBlue); + + // try each CW + for (unsigned int uiCW = 0; uiCW < CW_RANGES; uiCW++) + { + unsigned int auiPixelSelectors[PIXELS / 2]; + ColorFloatRGBA afrgbaDecodedPixels[PIXELS / 2]; + float afPixelErrors[PIXELS / 2] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, + FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; + + // pre-compute decoded pixels for each selector + ColorFloatRGBA afrgbaSelectors[SELECTORS]; + assert(SELECTORS == 4); + afrgbaSelectors[0] = (frgbaColor + s_aafCwTable[uiCW][0]).ClampRGB(); + afrgbaSelectors[1] = (frgbaColor + s_aafCwTable[uiCW][1]).ClampRGB(); + afrgbaSelectors[2] = (frgbaColor + s_aafCwTable[uiCW][2]).ClampRGB(); + afrgbaSelectors[3] = (frgbaColor + s_aafCwTable[uiCW][3]).ClampRGB(); + + for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) + { + ColorFloatRGBA *pfrgbaSourcePixel = &m_pafrgbaSource[a_phalf->m_pauiPixelMapping[uiPixel]]; + ColorFloatRGBA frgbaDecodedPixel; + + for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++) + { + frgbaDecodedPixel = afrgbaSelectors[uiSelector]; + + float fPixelError; + + fPixelError = CalcPixelError(frgbaDecodedPixel, m_afDecodedAlphas[a_phalf->m_pauiPixelMapping[uiPixel]], + *pfrgbaSourcePixel); + + if (fPixelError < afPixelErrors[uiPixel]) + { + auiPixelSelectors[uiPixel] = uiSelector; + afrgbaDecodedPixels[uiPixel] = frgbaDecodedPixel; + afPixelErrors[uiPixel] = fPixelError; + } + + } + } + + // add up all pixel errors + float fCWError = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) + { + fCWError += afPixelErrors[uiPixel]; + } + + // if best CW so far + if (fCWError < ptry->m_fError) + { + ptry->m_uiCW = uiCW; + for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) + { + ptry->m_auiSelectors[uiPixel] = auiPixelSelectors[uiPixel]; + } + ptry->m_fError = fCWError; + } + + } + + if (ptry->m_fError < fBestTryError) + { + a_phalf->m_ptryBest = ptry; + fBestTryError = ptry->m_fError; + } + + assert(ptry->m_fError < FLT_MAX); + + a_phalf->m_uiTrys++; + } + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // try version 1 of the degenerate search + // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings + // each subsequent version of the degenerate search uses more basecolor movement and is less likely to + // be successfull + // + void Block4x4Encoding_ETC1::TryDegenerates1(void) + { + + TryDifferential(m_boolMostLikelyFlip, 1, -2, 0); + TryDifferential(m_boolMostLikelyFlip, 1, 2, 0); + TryDifferential(m_boolMostLikelyFlip, 1, 0, 2); + TryDifferential(m_boolMostLikelyFlip, 1, 0, -2); + + } + + // ---------------------------------------------------------------------------------------------------- + // try version 2 of the degenerate search + // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings + // each subsequent version of the degenerate search uses more basecolor movement and is less likely to + // be successfull + // + void Block4x4Encoding_ETC1::TryDegenerates2(void) + { + + TryDifferential(!m_boolMostLikelyFlip, 1, -2, 0); + TryDifferential(!m_boolMostLikelyFlip, 1, 2, 0); + TryDifferential(!m_boolMostLikelyFlip, 1, 0, 2); + TryDifferential(!m_boolMostLikelyFlip, 1, 0, -2); + + } + + // ---------------------------------------------------------------------------------------------------- + // try version 3 of the degenerate search + // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings + // each subsequent version of the degenerate search uses more basecolor movement and is less likely to + // be successfull + // + void Block4x4Encoding_ETC1::TryDegenerates3(void) + { + + TryDifferential(m_boolMostLikelyFlip, 1, -2, -2); + TryDifferential(m_boolMostLikelyFlip, 1, -2, 2); + TryDifferential(m_boolMostLikelyFlip, 1, 2, -2); + TryDifferential(m_boolMostLikelyFlip, 1, 2, 2); + + } + + // ---------------------------------------------------------------------------------------------------- + // try version 4 of the degenerate search + // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings + // each subsequent version of the degenerate search uses more basecolor movement and is less likely to + // be successfull + // + void Block4x4Encoding_ETC1::TryDegenerates4(void) + { + + TryDifferential(m_boolMostLikelyFlip, 1, -4, 0); + TryDifferential(m_boolMostLikelyFlip, 1, 4, 0); + TryDifferential(m_boolMostLikelyFlip, 1, 0, 4); + TryDifferential(m_boolMostLikelyFlip, 1, 0, -4); + + } + + // ---------------------------------------------------------------------------------------------------- + // find the best selector for each pixel based on a particular basecolor and CW that have been previously set + // calculate the selectors for each half of the block separately + // set the block error as the sum of each half's error + // + void Block4x4Encoding_ETC1::CalculateSelectors() + { + if (m_boolFlip) + { + CalculateHalfOfTheSelectors(0, s_auiTopPixelMapping); + CalculateHalfOfTheSelectors(1, s_auiBottomPixelMapping); + } + else + { + CalculateHalfOfTheSelectors(0, s_auiLeftPixelMapping); + CalculateHalfOfTheSelectors(1, s_auiRightPixelMapping); + } + + m_fError = m_fError1 + m_fError2; + } + + // ---------------------------------------------------------------------------------------------------- + // choose best selectors for half of the block + // calculate the error for half of the block + // + void Block4x4Encoding_ETC1::CalculateHalfOfTheSelectors(unsigned int a_uiHalf, + const unsigned int *pauiPixelMapping) + { + static const bool DEBUG_PRINT = false; + + ColorFloatRGBA *pfrgbaColor = a_uiHalf ? &m_frgbaColor2 : &m_frgbaColor1; + unsigned int *puiCW = a_uiHalf ? &m_uiCW2 : &m_uiCW1; + + float *pfHalfError = a_uiHalf ? &m_fError2 : &m_fError1; + *pfHalfError = FLT_MAX; + + // try each CW + for (unsigned int uiCW = 0; uiCW < CW_RANGES; uiCW++) + { + if (DEBUG_PRINT) + { + printf("\ncw=%u\n", uiCW); + } + + unsigned int auiPixelSelectors[PIXELS / 2]; + ColorFloatRGBA afrgbaDecodedPixels[PIXELS / 2]; + float afPixelErrors[PIXELS / 2] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; + + for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) + { + if (DEBUG_PRINT) + { + printf("\tsource [%.2f,%.2f,%.2f]\n", m_pafrgbaSource[pauiPixelMapping[uiPixel]].fR, + m_pafrgbaSource[pauiPixelMapping[uiPixel]].fG, m_pafrgbaSource[pauiPixelMapping[uiPixel]].fB); + } + + ColorFloatRGBA *pfrgbaSourcePixel = &m_pafrgbaSource[pauiPixelMapping[uiPixel]]; + ColorFloatRGBA frgbaDecodedPixel; + + for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++) + { + float fDeltaRGB = s_aafCwTable[uiCW][uiSelector]; + + frgbaDecodedPixel = (*pfrgbaColor + fDeltaRGB).ClampRGB(); + + float fPixelError; + + fPixelError = CalcPixelError(frgbaDecodedPixel, m_afDecodedAlphas[pauiPixelMapping[uiPixel]], + *pfrgbaSourcePixel); + + if (DEBUG_PRINT) + { + printf("\tpixel %u, index %u [%.2f,%.2f,%.2f], error %.2f", uiPixel, uiSelector, + frgbaDecodedPixel.fR, + frgbaDecodedPixel.fG, + frgbaDecodedPixel.fB, + fPixelError); + } + + if (fPixelError < afPixelErrors[uiPixel]) + { + if (DEBUG_PRINT) + { + printf(" *"); + } + + auiPixelSelectors[uiPixel] = uiSelector; + afrgbaDecodedPixels[uiPixel] = frgbaDecodedPixel; + afPixelErrors[uiPixel] = fPixelError; + } + + if (DEBUG_PRINT) + { + printf("\n"); + } + } + } + + // add up all pixel errors + float fCWError = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) + { + fCWError += afPixelErrors[uiPixel]; + } + if (DEBUG_PRINT) + { + printf("\terror %.2f\n", fCWError); + } + + // if best CW so far + if (fCWError < *pfHalfError) + { + *pfHalfError = fCWError; + *puiCW = uiCW; + for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) + { + m_auiSelectors[pauiPixelMapping[uiPixel]] = auiPixelSelectors[uiPixel]; + m_afrgbaDecodedColors[pauiPixelMapping[uiPixel]] = afrgbaDecodedPixels[uiPixel]; + } + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state + // + void Block4x4Encoding_ETC1::SetEncodingBits(void) + { + assert(m_mode == MODE_ETC1); + + if (m_boolDiff) + { + int iRed1 = m_frgbaColor1.IntRed(31.0f); + int iGreen1 = m_frgbaColor1.IntGreen(31.0f); + int iBlue1 = m_frgbaColor1.IntBlue(31.0f); + + int iRed2 = m_frgbaColor2.IntRed(31.0f); + int iGreen2 = m_frgbaColor2.IntGreen(31.0f); + int iBlue2 = m_frgbaColor2.IntBlue(31.0f); + + int iDRed2 = iRed2 - iRed1; + int iDGreen2 = iGreen2 - iGreen1; + int iDBlue2 = iBlue2 - iBlue1; + + assert(iDRed2 >= -4 && iDRed2 < 4); + assert(iDGreen2 >= -4 && iDGreen2 < 4); + assert(iDBlue2 >= -4 && iDBlue2 < 4); + + m_pencodingbitsRGB8->differential.red1 = (unsigned int)iRed1; + m_pencodingbitsRGB8->differential.green1 = (unsigned int)iGreen1; + m_pencodingbitsRGB8->differential.blue1 = (unsigned int)iBlue1; + + m_pencodingbitsRGB8->differential.dred2 = iDRed2; + m_pencodingbitsRGB8->differential.dgreen2 = iDGreen2; + m_pencodingbitsRGB8->differential.dblue2 = iDBlue2; + } + else + { + m_pencodingbitsRGB8->individual.red1 = (unsigned int)m_frgbaColor1.IntRed(15.0f); + m_pencodingbitsRGB8->individual.green1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f); + m_pencodingbitsRGB8->individual.blue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f); + + m_pencodingbitsRGB8->individual.red2 = (unsigned int)m_frgbaColor2.IntRed(15.0f); + m_pencodingbitsRGB8->individual.green2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f); + m_pencodingbitsRGB8->individual.blue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f); + } + + m_pencodingbitsRGB8->individual.cw1 = m_uiCW1; + m_pencodingbitsRGB8->individual.cw2 = m_uiCW2; + + SetEncodingBits_Selectors(); + + m_pencodingbitsRGB8->individual.diff = (unsigned int)m_boolDiff; + m_pencodingbitsRGB8->individual.flip = (unsigned int)m_boolFlip; + + } + + // ---------------------------------------------------------------------------------------------------- + // set the selectors in the encoding bits + // + void Block4x4Encoding_ETC1::SetEncodingBits_Selectors(void) + { + + m_pencodingbitsRGB8->individual.selectors = 0; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + unsigned int uiSelector = m_auiSelectors[uiPixel]; + + // set index msb + m_pencodingbitsRGB8->individual.selectors |= (uiSelector >> 1) << (uiPixel ^ 8); + + // set index lsb + m_pencodingbitsRGB8->individual.selectors |= (uiSelector & 1) << ((16 + uiPixel) ^ 8); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // set the decoded colors and decoded alpha based on the encoding state + // + void Block4x4Encoding_ETC1::Decode(void) + { + + const unsigned int *pauiPixelOrder = m_boolFlip ? s_auiPixelOrderFlip1 : s_auiPixelOrderFlip0; + + for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS; uiPixelOrder++) + { + ColorFloatRGBA *pfrgbaCenter = uiPixelOrder < 8 ? &m_frgbaColor1 : &m_frgbaColor2; + unsigned int uiCW = uiPixelOrder < 8 ? m_uiCW1 : m_uiCW2; + + unsigned int uiPixel = pauiPixelOrder[uiPixelOrder]; + + float fDelta = s_aafCwTable[uiCW][m_auiSelectors[uiPixel]]; + m_afrgbaDecodedColors[uiPixel] = (*pfrgbaCenter + fDelta).ClampRGB(); + m_afDecodedAlphas[uiPixel] = 1.0f; + } + + } + + // ---------------------------------------------------------------------------------------------------- + // + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_ETC1.h b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_ETC1.h new file mode 100644 index 0000000000..c0dc84d5d5 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_ETC1.h @@ -0,0 +1,186 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcBlock4x4Encoding.h" +#include "EtcBlock4x4EncodingBits.h" +#include "EtcDifferentialTrys.h" +#include "EtcIndividualTrys.h" + +namespace Etc +{ + + // base class for Block4x4Encoding_RGB8 + class Block4x4Encoding_ETC1 : public Block4x4Encoding + { + public: + + Block4x4Encoding_ETC1(void); + virtual ~Block4x4Encoding_ETC1(void); + + virtual void InitFromSource(Block4x4 *a_pblockParent, + ColorFloatRGBA *a_pafrgbaSource, + + unsigned char *a_paucEncodingBits, + ErrorMetric a_errormetric); + + virtual void InitFromEncodingBits(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + + ErrorMetric a_errormetric); + + virtual void PerformIteration(float a_fEffort); + + inline virtual bool GetFlip(void) + { + return m_boolFlip; + } + + inline virtual bool IsDifferential(void) + { + return m_boolDiff; + } + + virtual void SetEncodingBits(void); + + void Decode(void); + + inline ColorFloatRGBA GetColor1(void) const + { + return m_frgbaColor1; + } + + inline ColorFloatRGBA GetColor2(void) const + { + return m_frgbaColor2; + } + + inline const unsigned int * GetSelectors(void) const + { + return m_auiSelectors; + } + + inline unsigned int GetCW1(void) const + { + return m_uiCW1; + } + + inline unsigned int GetCW2(void) const + { + return m_uiCW2; + } + + inline bool HasSeverelyBentDifferentialColors(void) const + { + return m_boolSeverelyBentDifferentialColors; + } + + protected: + + static const unsigned int s_auiPixelOrderFlip0[PIXELS]; + static const unsigned int s_auiPixelOrderFlip1[PIXELS]; + static const unsigned int s_auiPixelOrderHScan[PIXELS]; + + static const unsigned int s_auiLeftPixelMapping[8]; + static const unsigned int s_auiRightPixelMapping[8]; + static const unsigned int s_auiTopPixelMapping[8]; + static const unsigned int s_auiBottomPixelMapping[8]; + + static const unsigned int SELECTOR_BITS = 2; + static const unsigned int SELECTORS = 1 << SELECTOR_BITS; + + static const unsigned int CW_BITS = 3; + static const unsigned int CW_RANGES = 1 << CW_BITS; + + static float s_aafCwTable[CW_RANGES][SELECTORS]; + static unsigned char s_aucDifferentialCwRange[256]; + + static const int MAX_DIFFERENTIAL = 3; + static const int MIN_DIFFERENTIAL = -4; + + void InitFromEncodingBits_Selectors(void); + + void PerformFirstIteration(void); + void CalculateMostLikelyFlip(void); + + void TryDifferential(bool a_boolFlip, unsigned int a_uiRadius, + int a_iGrayOffset1, int a_iGrayOffset2); + void TryDifferentialHalf(DifferentialTrys::Half *a_phalf); + + void TryIndividual(bool a_boolFlip, unsigned int a_uiRadius); + void TryIndividualHalf(IndividualTrys::Half *a_phalf); + + void TryDegenerates1(void); + void TryDegenerates2(void); + void TryDegenerates3(void); + void TryDegenerates4(void); + + void CalculateSelectors(); + void CalculateHalfOfTheSelectors(unsigned int a_uiHalf, + const unsigned int *pauiPixelMapping); + + // calculate the distance2 of r_frgbaPixel from r_frgbaTarget's gray line + inline float CalcGrayDistance2(ColorFloatRGBA &r_frgbaPixel, + ColorFloatRGBA &r_frgbaTarget) + { + float fDeltaGray = ((r_frgbaPixel.fR - r_frgbaTarget.fR) + + (r_frgbaPixel.fG - r_frgbaTarget.fG) + + (r_frgbaPixel.fB - r_frgbaTarget.fB)) / 3.0f; + + ColorFloatRGBA frgbaPointOnGrayLine = (r_frgbaTarget + fDeltaGray).ClampRGB(); + + float fDR = r_frgbaPixel.fR - frgbaPointOnGrayLine.fR; + float fDG = r_frgbaPixel.fG - frgbaPointOnGrayLine.fG; + float fDB = r_frgbaPixel.fB - frgbaPointOnGrayLine.fB; + + return (fDR*fDR) + (fDG*fDG) + (fDB*fDB); + } + + void SetEncodingBits_Selectors(void); + + // intermediate encoding + bool m_boolDiff; + bool m_boolFlip; + ColorFloatRGBA m_frgbaColor1; + ColorFloatRGBA m_frgbaColor2; + unsigned int m_uiCW1; + unsigned int m_uiCW2; + unsigned int m_auiSelectors[PIXELS]; + + // state shared between iterations + ColorFloatRGBA m_frgbaSourceAverageLeft; + ColorFloatRGBA m_frgbaSourceAverageRight; + ColorFloatRGBA m_frgbaSourceAverageTop; + ColorFloatRGBA m_frgbaSourceAverageBottom; + bool m_boolMostLikelyFlip; + + // stats + float m_fError1; // error for Etc1 half 1 + float m_fError2; // error for Etc1 half 2 + bool m_boolSeverelyBentDifferentialColors; // only valid if m_boolDiff; + + // final encoding + Block4x4EncodingBits_RGB8 *m_pencodingbitsRGB8; // or RGB8 portion of Block4x4EncodingBits_RGB8A8 + + private: + + void CalculateSourceAverages(void); + + }; + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_R11.cpp b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_R11.cpp new file mode 100644 index 0000000000..4c012fbbf1 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_R11.cpp @@ -0,0 +1,429 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +EtcBlock4x4Encoding_R11.cpp + +Block4x4Encoding_R11 is the encoder to use when targetting file format R11 and SR11 (signed R11). + +*/ + +#include "EtcConfig.h" +#include "EtcBlock4x4Encoding_R11.h" + +#include "EtcBlock4x4EncodingBits.h" +#include "EtcBlock4x4.h" + +#include +#include +#include +#include +#include + +namespace Etc +{ + + // modifier values to use for R11, SR11, RG11 and SRG11 + float Block4x4Encoding_R11::s_aafModifierTable[MODIFIER_TABLE_ENTRYS][SELECTORS] + { + { -3.0f / 255.0f, -6.0f / 255.0f, -9.0f / 255.0f, -15.0f / 255.0f, 2.0f / 255.0f, 5.0f / 255.0f, 8.0f / 255.0f, 14.0f / 255.0f }, + { -3.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, -13.0f / 255.0f, 2.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f, 12.0f / 255.0f }, + { -2.0f / 255.0f, -5.0f / 255.0f, -8.0f / 255.0f, -13.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 12.0f / 255.0f }, + { -2.0f / 255.0f, -4.0f / 255.0f, -6.0f / 255.0f, -13.0f / 255.0f, 1.0f / 255.0f, 3.0f / 255.0f, 5.0f / 255.0f, 12.0f / 255.0f }, + + { -3.0f / 255.0f, -6.0f / 255.0f, -8.0f / 255.0f, -12.0f / 255.0f, 2.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 11.0f / 255.0f }, + { -3.0f / 255.0f, -7.0f / 255.0f, -9.0f / 255.0f, -11.0f / 255.0f, 2.0f / 255.0f, 6.0f / 255.0f, 8.0f / 255.0f, 10.0f / 255.0f }, + { -4.0f / 255.0f, -7.0f / 255.0f, -8.0f / 255.0f, -11.0f / 255.0f, 3.0f / 255.0f, 6.0f / 255.0f, 7.0f / 255.0f, 10.0f / 255.0f }, + { -3.0f / 255.0f, -5.0f / 255.0f, -8.0f / 255.0f, -11.0f / 255.0f, 2.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 10.0f / 255.0f }, + + { -2.0f / 255.0f, -6.0f / 255.0f, -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 9.0f / 255.0f }, + { -2.0f / 255.0f, -5.0f / 255.0f, -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 9.0f / 255.0f }, + { -2.0f / 255.0f, -4.0f / 255.0f, -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 3.0f / 255.0f, 7.0f / 255.0f, 9.0f / 255.0f }, + { -2.0f / 255.0f, -5.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f }, + + { -3.0f / 255.0f, -4.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, 2.0f / 255.0f, 3.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f }, + { -1.0f / 255.0f, -2.0f / 255.0f, -3.0f / 255.0f, -10.0f / 255.0f, 0.0f / 255.0f, 1.0f / 255.0f, 2.0f / 255.0f, 9.0f / 255.0f }, + { -4.0f / 255.0f, -6.0f / 255.0f, -8.0f / 255.0f, -9.0f / 255.0f, 3.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 8.0f / 255.0f }, + { -3.0f / 255.0f, -5.0f / 255.0f, -7.0f / 255.0f, -9.0f / 255.0f, 2.0f / 255.0f, 4.0f / 255.0f, 6.0f / 255.0f, 8.0f / 255.0f } + }; + + // ---------------------------------------------------------------------------------------------------- + // + Block4x4Encoding_R11::Block4x4Encoding_R11(void) + { + + m_pencodingbitsR11 = nullptr; + + } + + Block4x4Encoding_R11::~Block4x4Encoding_R11(void) {} + // ---------------------------------------------------------------------------------------------------- + // initialization prior to encoding + // a_pblockParent points to the block associated with this encoding + // a_errormetric is used to choose the best encoding + // a_pafrgbaSource points to a 4x4 block subset of the source image + // a_paucEncodingBits points to the final encoding bits + // + void Block4x4Encoding_R11::InitFromSource(Block4x4 *a_pblockParent, + ColorFloatRGBA *a_pafrgbaSource, + unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric) + { + Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric); + + m_pencodingbitsR11 = (Block4x4EncodingBits_R11 *)a_paucEncodingBits; + } + + // ---------------------------------------------------------------------------------------------------- + // initialization from the encoding bits of a previous encoding + // a_pblockParent points to the block associated with this encoding + // a_errormetric is used to choose the best encoding + // a_pafrgbaSource points to a 4x4 block subset of the source image + // a_paucEncodingBits points to the final encoding bits of a previous encoding + // + void Block4x4Encoding_R11::InitFromEncodingBits(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + ErrorMetric a_errormetric) + { + m_pencodingbitsR11 = (Block4x4EncodingBits_R11 *)a_paucEncodingBits; + + // init RGB portion + Block4x4Encoding_RGB8::InitFromEncodingBits(a_pblockParent, + (unsigned char *)m_pencodingbitsR11, + a_pafrgbaSource, + a_errormetric); + + // init R11 portion + { + m_mode = MODE_R11; + if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_R11 || a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11) + { + m_fRedBase = (float)(signed char)m_pencodingbitsR11->data.base; + } + else + { + m_fRedBase = (float)(unsigned char)m_pencodingbitsR11->data.base; + } + m_fRedMultiplier = (float)m_pencodingbitsR11->data.multiplier; + m_uiRedModifierTableIndex = m_pencodingbitsR11->data.table; + + unsigned long long int ulliSelectorBits = 0; + ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors0 << 40; + ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors1 << 32; + ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors2 << 24; + ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors3 << 16; + ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors4 << 8; + ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors5; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + unsigned int uiShift = 45 - (3 * uiPixel); + m_auiRedSelectors[uiPixel] = (ulliSelectorBits >> uiShift) & (SELECTORS - 1); + } + + // decode the red channel + // calc red error + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + float fDecodedPixelData = 0.0f; + if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::R11 || a_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11) + { + fDecodedPixelData = DecodePixelRed(m_fRedBase, m_fRedMultiplier, + m_uiRedModifierTableIndex, + m_auiRedSelectors[uiPixel]); + } + else if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_R11 || a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11) + { + fDecodedPixelData = DecodePixelRed(m_fRedBase + 128, m_fRedMultiplier, + m_uiRedModifierTableIndex, + m_auiRedSelectors[uiPixel]); + } + else + { + assert(0); + } + m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(fDecodedPixelData, 0.0f, 0.0f, 1.0f); + } + CalcBlockError(); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // perform a single encoding iteration + // replace the encoding if a better encoding was found + // subsequent iterations generally take longer for each iteration + // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort + // + void Block4x4Encoding_R11::PerformIteration(float a_fEffort) + { + assert(!m_boolDone); + m_mode = MODE_R11; + + switch (m_uiEncodingIterations) + { + case 0: + m_fError = FLT_MAX; + m_fRedBlockError = FLT_MAX; // artificially high value + CalculateR11(8, 0.0f, 0.0f); + m_fError = m_fRedBlockError; + break; + + case 1: + CalculateR11(8, 2.0f, 1.0f); + m_fError = m_fRedBlockError; + if (a_fEffort <= 24.5f) + { + m_boolDone = true; + } + break; + + case 2: + CalculateR11(8, 12.0f, 1.0f); + m_fError = m_fRedBlockError; + if (a_fEffort <= 49.5f) + { + m_boolDone = true; + } + break; + + case 3: + CalculateR11(7, 6.0f, 1.0f); + m_fError = m_fRedBlockError; + break; + + case 4: + CalculateR11(6, 3.0f, 1.0f); + m_fError = m_fRedBlockError; + break; + + case 5: + CalculateR11(5, 1.0f, 0.0f); + m_fError = m_fRedBlockError; + m_boolDone = true; + break; + + default: + assert(0); + break; + } + + m_uiEncodingIterations++; + SetDoneIfPerfect(); + } + + // ---------------------------------------------------------------------------------------------------- + // find the best combination of base color, multiplier and selectors + // + // a_uiSelectorsUsed limits the number of selector combinations to try + // a_fBaseRadius limits the range of base colors to try + // a_fMultiplierRadius limits the range of multipliers to try + // + void Block4x4Encoding_R11::CalculateR11(unsigned int a_uiSelectorsUsed, + float a_fBaseRadius, float a_fMultiplierRadius) + { + // maps from virtual (monotonic) selector to ETC selector + static const unsigned int auiVirtualSelectorMap[8] = {3, 2, 1, 0, 4, 5, 6, 7}; + + // find min/max red + float fMinRed = 1.0f; + float fMaxRed = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + // ignore border pixels + float fAlpha = m_pafrgbaSource[uiPixel].fA; + if (isnan(fAlpha)) + { + continue; + } + + float fRed = m_pafrgbaSource[uiPixel].fR; + + if (fRed < fMinRed) + { + fMinRed = fRed; + } + if (fRed > fMaxRed) + { + fMaxRed = fRed; + } + } + assert(fMinRed <= fMaxRed); + + float fRedRange = (fMaxRed - fMinRed); + + // try each modifier table entry + for (unsigned int uiTableEntry = 0; uiTableEntry < MODIFIER_TABLE_ENTRYS; uiTableEntry++) + { + for (unsigned int uiMinVirtualSelector = 0; + uiMinVirtualSelector <= (8- a_uiSelectorsUsed); + uiMinVirtualSelector++) + { + unsigned int uiMaxVirtualSelector = uiMinVirtualSelector + a_uiSelectorsUsed - 1; + + unsigned int uiMinSelector = auiVirtualSelectorMap[uiMinVirtualSelector]; + unsigned int uiMaxSelector = auiVirtualSelectorMap[uiMaxVirtualSelector]; + + float fTableEntryCenter = -s_aafModifierTable[uiTableEntry][uiMinSelector]; + + float fTableEntryRange = s_aafModifierTable[uiTableEntry][uiMaxSelector] - + s_aafModifierTable[uiTableEntry][uiMinSelector]; + + float fCenterRatio = fTableEntryCenter / fTableEntryRange; + + float fCenter = fMinRed + fCenterRatio*fRedRange; + fCenter = roundf(255.0f * fCenter) / 255.0f; + + float fMinBase = fCenter - (a_fBaseRadius / 255.0f); + if (fMinBase < 0.0f) + { + fMinBase = 0.0f; + } + + float fMaxBase = fCenter + (a_fBaseRadius / 255.0f); + if (fMaxBase > 1.0f) + { + fMaxBase = 1.0f; + } + + for (float fBase = fMinBase; fBase <= fMaxBase; fBase += (0.999999f / 255.0f)) + { + float fRangeMultiplier = roundf(fRedRange / fTableEntryRange); + + float fMinMultiplier = fRangeMultiplier - a_fMultiplierRadius; + if (fMinMultiplier < 1.0f) + { + fMinMultiplier = 0.0f; + } + else if (fMinMultiplier > 15.0f) + { + fMinMultiplier = 15.0f; + } + + float fMaxMultiplier = fRangeMultiplier + a_fMultiplierRadius; + if (fMaxMultiplier < 1.0f) + { + fMaxMultiplier = 1.0f; + } + else if (fMaxMultiplier > 15.0f) + { + fMaxMultiplier = 15.0f; + } + + for (float fMultiplier = fMinMultiplier; fMultiplier <= fMaxMultiplier; fMultiplier += 1.0f) + { + // find best selector for each pixel + unsigned int auiBestSelectors[PIXELS]; + float afBestRedError[PIXELS]; + float afBestPixelRed[PIXELS]; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + float fBestPixelRedError = FLT_MAX; + + for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++) + { + float fPixelRed = DecodePixelRed(fBase * 255.0f, fMultiplier, uiTableEntry, uiSelector); + + ColorFloatRGBA frgba(fPixelRed, m_pafrgbaSource[uiPixel].fG,0.0f,1.0f); + + float fPixelRedError = CalcPixelError(frgba, 1.0f, m_pafrgbaSource[uiPixel]); + + if (fPixelRedError < fBestPixelRedError) + { + fBestPixelRedError = fPixelRedError; + auiBestSelectors[uiPixel] = uiSelector; + afBestRedError[uiPixel] = fBestPixelRedError; + afBestPixelRed[uiPixel] = fPixelRed; + } + } + } + float fBlockError = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + fBlockError += afBestRedError[uiPixel]; + } + if (fBlockError < m_fRedBlockError) + { + m_fRedBlockError = fBlockError; + + if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::R11 || m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11) + { + m_fRedBase = 255.0f * fBase; + } + else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_R11 || m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11) + { + m_fRedBase = (fBase * 255) - 128; + } + else + { + assert(0); + } + m_fRedMultiplier = fMultiplier; + m_uiRedModifierTableIndex = uiTableEntry; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiRedSelectors[uiPixel] = auiBestSelectors[uiPixel]; + float fBestPixelRed = afBestPixelRed[uiPixel]; + m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(fBestPixelRed, 0.0f, 0.0f, 1.0f); + m_afDecodedAlphas[uiPixel] = 1.0f; + } + } + } + } + + } + } + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state + // + void Block4x4Encoding_R11::SetEncodingBits(void) + { + if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::R11 || m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11) + { + m_pencodingbitsR11->data.base = (unsigned char)roundf(m_fRedBase); + } + else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_R11 || m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11) + { + m_pencodingbitsR11->data.base = (signed char)roundf(m_fRedBase); + } + else + { + assert(0); + } + m_pencodingbitsR11->data.table = m_uiRedModifierTableIndex; + m_pencodingbitsR11->data.multiplier = (unsigned char)roundf(m_fRedMultiplier); + + unsigned long long int ulliSelectorBits = 0; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + unsigned int uiShift = 45 - (3 * uiPixel); + ulliSelectorBits |= ((unsigned long long int)m_auiRedSelectors[uiPixel]) << uiShift; + } + + m_pencodingbitsR11->data.selectors0 = ulliSelectorBits >> 40; + m_pencodingbitsR11->data.selectors1 = ulliSelectorBits >> 32; + m_pencodingbitsR11->data.selectors2 = ulliSelectorBits >> 24; + m_pencodingbitsR11->data.selectors3 = ulliSelectorBits >> 16; + m_pencodingbitsR11->data.selectors4 = ulliSelectorBits >> 8; + m_pencodingbitsR11->data.selectors5 = ulliSelectorBits; + } + + // ---------------------------------------------------------------------------------------------------- + // +} diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_R11.h b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_R11.h new file mode 100644 index 0000000000..b40c1e0036 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_R11.h @@ -0,0 +1,122 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcBlock4x4Encoding_RGB8.h" + +namespace Etc +{ + class Block4x4EncodingBits_R11; + + // ################################################################################ + // Block4x4Encoding_R11 + // ################################################################################ + + class Block4x4Encoding_R11 : public Block4x4Encoding_RGB8 + { + public: + + Block4x4Encoding_R11(void); + virtual ~Block4x4Encoding_R11(void); + + virtual void InitFromSource(Block4x4 *a_pblockParent, + ColorFloatRGBA *a_pafrgbaSource, + unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric); + + virtual void InitFromEncodingBits(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + ErrorMetric a_errormetric); + + virtual void PerformIteration(float a_fEffort); + + virtual void SetEncodingBits(void); + + inline float GetRedBase(void) const + { + return m_fRedBase; + } + + inline float GetRedMultiplier(void) const + { + return m_fRedMultiplier; + } + + inline int GetRedTableIndex(void) const + { + return m_uiRedModifierTableIndex; + } + + inline const unsigned int * GetRedSelectors(void) const + { + return m_auiRedSelectors; + } + + protected: + + static const unsigned int MODIFIER_TABLE_ENTRYS = 16; + static const unsigned int SELECTOR_BITS = 3; + static const unsigned int SELECTORS = 1 << SELECTOR_BITS; + + static float s_aafModifierTable[MODIFIER_TABLE_ENTRYS][SELECTORS]; + + void CalculateR11(unsigned int a_uiSelectorsUsed, + float a_fBaseRadius, float a_fMultiplierRadius); + + + + + inline float DecodePixelRed(float a_fBase, float a_fMultiplier, + unsigned int a_uiTableIndex, unsigned int a_uiSelector) + { + float fMultiplier = a_fMultiplier; + if (fMultiplier <= 0.0f) + { + fMultiplier = 1.0f / 8.0f; + } + + float fPixelRed = a_fBase * 8 + 4 + + 8 * fMultiplier*s_aafModifierTable[a_uiTableIndex][a_uiSelector]*255; + fPixelRed /= 2047.0f; + + if (fPixelRed < 0.0f) + { + fPixelRed = 0.0f; + } + else if (fPixelRed > 1.0f) + { + fPixelRed = 1.0f; + } + + return fPixelRed; + } + + Block4x4EncodingBits_R11 *m_pencodingbitsR11; + + float m_fRedBase; + float m_fRedMultiplier; + float m_fRedBlockError; + unsigned int m_uiRedModifierTableIndex; + unsigned int m_auiRedSelectors[PIXELS]; + + + }; + + // ---------------------------------------------------------------------------------------------------- + // + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RG11.cpp b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RG11.cpp new file mode 100644 index 0000000000..417835db51 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RG11.cpp @@ -0,0 +1,447 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +EtcBlock4x4Encoding_RG11.cpp + +Block4x4Encoding_RG11 is the encoder to use when targetting file format RG11 and SRG11 (signed RG11). + +*/ + +#include "EtcConfig.h" +#include "EtcBlock4x4Encoding_RG11.h" + +#include "EtcBlock4x4EncodingBits.h" +#include "EtcBlock4x4.h" + +#include +#include +#include +#include +#include + +namespace Etc +{ + // ---------------------------------------------------------------------------------------------------- + // + Block4x4Encoding_RG11::Block4x4Encoding_RG11(void) + { + m_pencodingbitsRG11 = nullptr; + } + + Block4x4Encoding_RG11::~Block4x4Encoding_RG11(void) {} + // ---------------------------------------------------------------------------------------------------- + // initialization prior to encoding + // a_pblockParent points to the block associated with this encoding + // a_errormetric is used to choose the best encoding + // a_pafrgbaSource points to a 4x4 block subset of the source image + // a_paucEncodingBits points to the final encoding bits + // + void Block4x4Encoding_RG11::InitFromSource(Block4x4 *a_pblockParent, + ColorFloatRGBA *a_pafrgbaSource, + unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric) + { + Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric); + + m_pencodingbitsRG11 = (Block4x4EncodingBits_RG11 *)a_paucEncodingBits; + } + + // ---------------------------------------------------------------------------------------------------- + // initialization from the encoding bits of a previous encoding + // a_pblockParent points to the block associated with this encoding + // a_errormetric is used to choose the best encoding + // a_pafrgbaSource points to a 4x4 block subset of the source image + // a_paucEncodingBits points to the final encoding bits of a previous encoding + // + void Block4x4Encoding_RG11::InitFromEncodingBits(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + ErrorMetric a_errormetric) + { + + m_pencodingbitsRG11 = (Block4x4EncodingBits_RG11 *)a_paucEncodingBits; + + // init RGB portion + Block4x4Encoding_RGB8::InitFromEncodingBits(a_pblockParent, + (unsigned char *)m_pencodingbitsRG11, + a_pafrgbaSource, + a_errormetric); + m_fError = 0.0f; + + { + m_mode = MODE_RG11; + if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11) + { + m_fRedBase = (float)(signed char)m_pencodingbitsRG11->data.baseR; + m_fGrnBase = (float)(signed char)m_pencodingbitsRG11->data.baseG; + } + else + { + m_fRedBase = (float)(unsigned char)m_pencodingbitsRG11->data.baseR; + m_fGrnBase = (float)(unsigned char)m_pencodingbitsRG11->data.baseG; + } + m_fRedMultiplier = (float)m_pencodingbitsRG11->data.multiplierR; + m_fGrnMultiplier = (float)m_pencodingbitsRG11->data.multiplierG; + m_uiRedModifierTableIndex = m_pencodingbitsRG11->data.tableIndexR; + m_uiGrnModifierTableIndex = m_pencodingbitsRG11->data.tableIndexG; + + unsigned long long int ulliSelectorBitsR = 0; + ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR0 << 40; + ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR1 << 32; + ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR2 << 24; + ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR3 << 16; + ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR4 << 8; + ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR5; + + unsigned long long int ulliSelectorBitsG = 0; + ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG0 << 40; + ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG1 << 32; + ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG2 << 24; + ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG3 << 16; + ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG4 << 8; + ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG5; + + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + unsigned int uiShift = 45 - (3 * uiPixel); + m_auiRedSelectors[uiPixel] = (ulliSelectorBitsR >> uiShift) & (SELECTORS - 1); + m_auiGrnSelectors[uiPixel] = (ulliSelectorBitsG >> uiShift) & (SELECTORS - 1); + } + + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + float fRedDecodedData = 0.0f; + float fGrnDecodedData = 0.0f; + if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11) + { + fRedDecodedData = DecodePixelRed(m_fRedBase, m_fRedMultiplier, m_uiRedModifierTableIndex, m_auiRedSelectors[uiPixel]); + fGrnDecodedData = DecodePixelRed(m_fGrnBase, m_fGrnMultiplier, m_uiGrnModifierTableIndex, m_auiGrnSelectors[uiPixel]); + } + else if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11) + { + fRedDecodedData = DecodePixelRed(m_fRedBase + 128, m_fRedMultiplier, m_uiRedModifierTableIndex, m_auiRedSelectors[uiPixel]); + fGrnDecodedData = DecodePixelRed(m_fGrnBase + 128, m_fGrnMultiplier, m_uiGrnModifierTableIndex, m_auiGrnSelectors[uiPixel]); + } + else + { + assert(0); + } + m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(fRedDecodedData, fGrnDecodedData, 0.0f, 1.0f); + } + + } + + CalcBlockError(); + } + + // ---------------------------------------------------------------------------------------------------- + // perform a single encoding iteration + // replace the encoding if a better encoding was found + // subsequent iterations generally take longer for each iteration + // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort + // + void Block4x4Encoding_RG11::PerformIteration(float a_fEffort) + { + assert(!m_boolDone); + + switch (m_uiEncodingIterations) + { + case 0: + m_fError = FLT_MAX; + m_fGrnBlockError = FLT_MAX; // artificially high value + m_fRedBlockError = FLT_MAX; + CalculateR11(8, 0.0f, 0.0f); + CalculateG11(8, 0.0f, 0.0f); + m_fError = (m_fGrnBlockError + m_fRedBlockError); + break; + + case 1: + CalculateR11(8, 2.0f, 1.0f); + CalculateG11(8, 2.0f, 1.0f); + m_fError = (m_fGrnBlockError + m_fRedBlockError); + if (a_fEffort <= 24.5f) + { + m_boolDone = true; + } + break; + + case 2: + CalculateR11(8, 12.0f, 1.0f); + CalculateG11(8, 12.0f, 1.0f); + m_fError = (m_fGrnBlockError + m_fRedBlockError); + if (a_fEffort <= 49.5f) + { + m_boolDone = true; + } + break; + + case 3: + CalculateR11(7, 6.0f, 1.0f); + CalculateG11(7, 6.0f, 1.0f); + m_fError = (m_fGrnBlockError + m_fRedBlockError); + break; + + case 4: + CalculateR11(6, 3.0f, 1.0f); + CalculateG11(6, 3.0f, 1.0f); + m_fError = (m_fGrnBlockError + m_fRedBlockError); + break; + + case 5: + CalculateR11(5, 1.0f, 0.0f); + CalculateG11(5, 1.0f, 0.0f); + m_fError = (m_fGrnBlockError + m_fRedBlockError); + m_boolDone = true; + break; + + default: + assert(0); + break; + } + + m_uiEncodingIterations++; + SetDoneIfPerfect(); + } + + // ---------------------------------------------------------------------------------------------------- + // find the best combination of base color, multiplier and selectors + // + // a_uiSelectorsUsed limits the number of selector combinations to try + // a_fBaseRadius limits the range of base colors to try + // a_fMultiplierRadius limits the range of multipliers to try + // + void Block4x4Encoding_RG11::CalculateG11(unsigned int a_uiSelectorsUsed, + float a_fBaseRadius, float a_fMultiplierRadius) + { + // maps from virtual (monotonic) selector to etc selector + static const unsigned int auiVirtualSelectorMap[8] = { 3, 2, 1, 0, 4, 5, 6, 7 }; + + // find min/max Grn + float fMinGrn = 1.0f; + float fMaxGrn = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + // ignore border pixels + float fAlpha = m_pafrgbaSource[uiPixel].fA; + if (isnan(fAlpha)) + { + continue; + } + + float fGrn = m_pafrgbaSource[uiPixel].fG; + + if (fGrn < fMinGrn) + { + fMinGrn = fGrn; + } + if (fGrn > fMaxGrn) + { + fMaxGrn = fGrn; + } + } + assert(fMinGrn <= fMaxGrn); + + float fGrnRange = (fMaxGrn - fMinGrn); + + // try each modifier table entry + for (unsigned int uiTableEntry = 0; uiTableEntry < MODIFIER_TABLE_ENTRYS; uiTableEntry++) + { + for (unsigned int uiMinVirtualSelector = 0; + uiMinVirtualSelector <= (8 - a_uiSelectorsUsed); + uiMinVirtualSelector++) + { + unsigned int uiMaxVirtualSelector = uiMinVirtualSelector + a_uiSelectorsUsed - 1; + + unsigned int uiMinSelector = auiVirtualSelectorMap[uiMinVirtualSelector]; + unsigned int uiMaxSelector = auiVirtualSelectorMap[uiMaxVirtualSelector]; + + float fTableEntryCenter = -s_aafModifierTable[uiTableEntry][uiMinSelector]; + + float fTableEntryRange = s_aafModifierTable[uiTableEntry][uiMaxSelector] - + s_aafModifierTable[uiTableEntry][uiMinSelector]; + + float fCenterRatio = fTableEntryCenter / fTableEntryRange; + + float fCenter = fMinGrn + fCenterRatio*fGrnRange; + fCenter = roundf(255.0f * fCenter) / 255.0f; + + float fMinBase = fCenter - (a_fBaseRadius / 255.0f); + if (fMinBase < 0.0f) + { + fMinBase = 0.0f; + } + + float fMaxBase = fCenter + (a_fBaseRadius / 255.0f); + if (fMaxBase > 1.0f) + { + fMaxBase = 1.0f; + } + + for (float fBase = fMinBase; fBase <= fMaxBase; fBase += (0.999999f / 255.0f)) + { + float fRangeMultiplier = roundf(fGrnRange / fTableEntryRange); + + float fMinMultiplier = fRangeMultiplier - a_fMultiplierRadius; + if (fMinMultiplier < 1.0f) + { + fMinMultiplier = 0.0f; + } + else if (fMinMultiplier > 15.0f) + { + fMinMultiplier = 15.0f; + } + + float fMaxMultiplier = fRangeMultiplier + a_fMultiplierRadius; + if (fMaxMultiplier < 1.0f) + { + fMaxMultiplier = 1.0f; + } + else if (fMaxMultiplier > 15.0f) + { + fMaxMultiplier = 15.0f; + } + + for (float fMultiplier = fMinMultiplier; fMultiplier <= fMaxMultiplier; fMultiplier += 1.0f) + { + // find best selector for each pixel + unsigned int auiBestSelectors[PIXELS]; + float afBestGrnError[PIXELS]; + float afBestPixelGrn[PIXELS]; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + float fBestPixelGrnError = FLT_MAX; + + for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++) + { + //DecodePixelRed is not red channel specific + float fPixelGrn = DecodePixelRed(fBase * 255.0f, fMultiplier, uiTableEntry, uiSelector); + + ColorFloatRGBA frgba(m_pafrgbaSource[uiPixel].fR, fPixelGrn, 0.0f, 1.0f); + + float fPixelGrnError = CalcPixelError(frgba, 1.0f, m_pafrgbaSource[uiPixel]); + + if (fPixelGrnError < fBestPixelGrnError) + { + fBestPixelGrnError = fPixelGrnError; + auiBestSelectors[uiPixel] = uiSelector; + afBestGrnError[uiPixel] = fBestPixelGrnError; + afBestPixelGrn[uiPixel] = fPixelGrn; + } + } + } + float fBlockError = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + fBlockError += afBestGrnError[uiPixel]; + } + + if (fBlockError < m_fGrnBlockError) + { + m_fGrnBlockError = fBlockError; + + if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11) + { + m_fGrnBase = 255.0f * fBase; + } + else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11) + { + m_fGrnBase = (fBase * 255) - 128; + } + else + { + assert(0); + } + m_fGrnMultiplier = fMultiplier; + m_uiGrnModifierTableIndex = uiTableEntry; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiGrnSelectors[uiPixel] = auiBestSelectors[uiPixel]; + m_afrgbaDecodedColors[uiPixel].fG = afBestPixelGrn[uiPixel]; + m_afDecodedAlphas[uiPixel] = 1.0f; + } + } + } + } + + } + } + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state + // + void Block4x4Encoding_RG11::SetEncodingBits(void) + { + unsigned long long int ulliSelectorBitsR = 0; + unsigned long long int ulliSelectorBitsG = 0; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + unsigned int uiShift = 45 - (3 * uiPixel); + ulliSelectorBitsR |= ((unsigned long long int)m_auiRedSelectors[uiPixel]) << uiShift; + ulliSelectorBitsG |= ((unsigned long long int)m_auiGrnSelectors[uiPixel]) << uiShift; + } + if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11) + { + m_pencodingbitsRG11->data.baseR = (unsigned char)roundf(m_fRedBase); + } + else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11) + { + m_pencodingbitsRG11->data.baseR = (signed char)roundf(m_fRedBase); + } + else + { + assert(0); + } + m_pencodingbitsRG11->data.tableIndexR = m_uiRedModifierTableIndex; + m_pencodingbitsRG11->data.multiplierR = (unsigned char)roundf(m_fRedMultiplier); + + m_pencodingbitsRG11->data.selectorsR0 = ulliSelectorBitsR >> 40; + m_pencodingbitsRG11->data.selectorsR1 = ulliSelectorBitsR >> 32; + m_pencodingbitsRG11->data.selectorsR2 = ulliSelectorBitsR >> 24; + m_pencodingbitsRG11->data.selectorsR3 = ulliSelectorBitsR >> 16; + m_pencodingbitsRG11->data.selectorsR4 = ulliSelectorBitsR >> 8; + m_pencodingbitsRG11->data.selectorsR5 = ulliSelectorBitsR; + + if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11) + { + m_pencodingbitsRG11->data.baseG = (unsigned char)roundf(m_fGrnBase); + } + else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11) + { + m_pencodingbitsRG11->data.baseG = (signed char)roundf(m_fGrnBase); + } + else + { + assert(0); + } + m_pencodingbitsRG11->data.tableIndexG = m_uiGrnModifierTableIndex; + m_pencodingbitsRG11->data.multiplierG = (unsigned char)roundf(m_fGrnMultiplier); + + m_pencodingbitsRG11->data.selectorsG0 = ulliSelectorBitsG >> 40; + m_pencodingbitsRG11->data.selectorsG1 = ulliSelectorBitsG >> 32; + m_pencodingbitsRG11->data.selectorsG2 = ulliSelectorBitsG >> 24; + m_pencodingbitsRG11->data.selectorsG3 = ulliSelectorBitsG >> 16; + m_pencodingbitsRG11->data.selectorsG4 = ulliSelectorBitsG >> 8; + m_pencodingbitsRG11->data.selectorsG5 = ulliSelectorBitsG; + + } + + // ---------------------------------------------------------------------------------------------------- + // +} diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RG11.h b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RG11.h new file mode 100644 index 0000000000..d4993b8c5f --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RG11.h @@ -0,0 +1,86 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcBlock4x4Encoding_RGB8.h" +#include "EtcBlock4x4Encoding_R11.h" + +namespace Etc +{ + class Block4x4EncodingBits_RG11; + + // ################################################################################ + // Block4x4Encoding_RG11 + // ################################################################################ + + class Block4x4Encoding_RG11 : public Block4x4Encoding_R11 + { + float m_fGrnBase; + float m_fGrnMultiplier; + float m_fGrnBlockError; + unsigned int m_auiGrnSelectors[PIXELS]; + unsigned int m_uiGrnModifierTableIndex; + public: + + Block4x4Encoding_RG11(void); + virtual ~Block4x4Encoding_RG11(void); + + virtual void InitFromSource(Block4x4 *a_pblockParent, + ColorFloatRGBA *a_pafrgbaSource, + + unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric); + + virtual void InitFromEncodingBits(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + + ErrorMetric a_errormetric); + + virtual void PerformIteration(float a_fEffort); + + virtual void SetEncodingBits(void); + + Block4x4EncodingBits_RG11 *m_pencodingbitsRG11; + + void CalculateG11(unsigned int a_uiSelectorsUsed, float a_fBaseRadius, float a_fMultiplierRadius); + + inline float GetGrnBase(void) const + { + return m_fGrnBase; + } + + inline float GetGrnMultiplier(void) const + { + return m_fGrnMultiplier; + } + + inline int GetGrnTableIndex(void) const + { + return m_uiGrnModifierTableIndex; + } + + inline const unsigned int * GetGrnSelectors(void) const + { + return m_auiGrnSelectors; + } + + }; + + // ---------------------------------------------------------------------------------------------------- + // + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGB8.cpp b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGB8.cpp new file mode 100644 index 0000000000..5656556db9 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGB8.cpp @@ -0,0 +1,1730 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +EtcBlock4x4Encoding_RGB8.cpp + +Block4x4Encoding_RGB8 is the encoder to use for the ETC2 extensions when targetting file format RGB8. +This encoder is also used for the ETC2 subset of file format RGBA8. + +Block4x4Encoding_ETC1 encodes the ETC1 subset of RGB8. + +*/ + +#include "EtcConfig.h" +#include "EtcBlock4x4Encoding_RGB8.h" + +#include "EtcBlock4x4EncodingBits.h" +#include "EtcBlock4x4.h" +#include "EtcMath.h" + +#include +#include +#include +#include +#include + +namespace Etc +{ + float Block4x4Encoding_RGB8::s_afTHDistanceTable[TH_DISTANCES] = + { + 3.0f / 255.0f, + 6.0f / 255.0f, + 11.0f / 255.0f, + 16.0f / 255.0f, + 23.0f / 255.0f, + 32.0f / 255.0f, + 41.0f / 255.0f, + 64.0f / 255.0f + }; + + // ---------------------------------------------------------------------------------------------------- + // + Block4x4Encoding_RGB8::Block4x4Encoding_RGB8(void) + { + + m_pencodingbitsRGB8 = nullptr; + + } + + Block4x4Encoding_RGB8::~Block4x4Encoding_RGB8(void) {} + // ---------------------------------------------------------------------------------------------------- + // initialization from the encoding bits of a previous encoding + // a_pblockParent points to the block associated with this encoding + // a_errormetric is used to choose the best encoding + // a_pafrgbaSource points to a 4x4 block subset of the source image + // a_paucEncodingBits points to the final encoding bits of a previous encoding + // + void Block4x4Encoding_RGB8::InitFromEncodingBits(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + ErrorMetric a_errormetric) + { + + // handle ETC1 modes + Block4x4Encoding_ETC1::InitFromEncodingBits(a_pblockParent, + a_paucEncodingBits, a_pafrgbaSource,a_errormetric); + + m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits; + + // detect if there is a T, H or Planar mode present + if (m_pencodingbitsRGB8->differential.diff) + { + int iRed1 = (int)m_pencodingbitsRGB8->differential.red1; + int iDRed2 = m_pencodingbitsRGB8->differential.dred2; + int iRed2 = iRed1 + iDRed2; + + int iGreen1 = (int)m_pencodingbitsRGB8->differential.green1; + int iDGreen2 = m_pencodingbitsRGB8->differential.dgreen2; + int iGreen2 = iGreen1 + iDGreen2; + + int iBlue1 = (int)m_pencodingbitsRGB8->differential.blue1; + int iDBlue2 = m_pencodingbitsRGB8->differential.dblue2; + int iBlue2 = iBlue1 + iDBlue2; + + if (iRed2 < 0 || iRed2 > 31) + { + InitFromEncodingBits_T(); + } + else if (iGreen2 < 0 || iGreen2 > 31) + { + InitFromEncodingBits_H(); + } + else if (iBlue2 < 0 || iBlue2 > 31) + { + InitFromEncodingBits_Planar(); + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // initialization from the encoding bits of a previous encoding if T mode is detected + // + void Block4x4Encoding_RGB8::InitFromEncodingBits_T(void) + { + + m_mode = MODE_T; + + unsigned char ucRed1 = (unsigned char)((m_pencodingbitsRGB8->t.red1a << 2) + + m_pencodingbitsRGB8->t.red1b); + unsigned char ucGreen1 = m_pencodingbitsRGB8->t.green1; + unsigned char ucBlue1 = m_pencodingbitsRGB8->t.blue1; + + unsigned char ucRed2 = m_pencodingbitsRGB8->t.red2; + unsigned char ucGreen2 = m_pencodingbitsRGB8->t.green2; + unsigned char ucBlue2 = m_pencodingbitsRGB8->t.blue2; + + m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1); + m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2); + + m_uiCW1 = (m_pencodingbitsRGB8->t.da << 1) + m_pencodingbitsRGB8->t.db; + + Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors(); + + DecodePixels_T(); + + CalcBlockError(); + + } + + // ---------------------------------------------------------------------------------------------------- + // initialization from the encoding bits of a previous encoding if H mode is detected + // + void Block4x4Encoding_RGB8::InitFromEncodingBits_H(void) + { + + m_mode = MODE_H; + + unsigned char ucRed1 = m_pencodingbitsRGB8->h.red1; + unsigned char ucGreen1 = (unsigned char)((m_pencodingbitsRGB8->h.green1a << 1) + + m_pencodingbitsRGB8->h.green1b); + unsigned char ucBlue1 = (unsigned char)((m_pencodingbitsRGB8->h.blue1a << 3) + + (m_pencodingbitsRGB8->h.blue1b << 1) + + m_pencodingbitsRGB8->h.blue1c); + + unsigned char ucRed2 = m_pencodingbitsRGB8->h.red2; + unsigned char ucGreen2 = (unsigned char)((m_pencodingbitsRGB8->h.green2a << 1) + + m_pencodingbitsRGB8->h.green2b); + unsigned char ucBlue2 = m_pencodingbitsRGB8->h.blue2; + + m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1); + m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2); + + // used to determine the LSB of the CW + unsigned int uiRGB1 = (unsigned int)(((int)ucRed1 << 16) + ((int)ucGreen1 << 8) + (int)ucBlue1); + unsigned int uiRGB2 = (unsigned int)(((int)ucRed2 << 16) + ((int)ucGreen2 << 8) + (int)ucBlue2); + + m_uiCW1 = (m_pencodingbitsRGB8->h.da << 2) + (m_pencodingbitsRGB8->h.db << 1); + if (uiRGB1 >= uiRGB2) + { + m_uiCW1++; + } + + Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors(); + + DecodePixels_H(); + + CalcBlockError(); + + } + + // ---------------------------------------------------------------------------------------------------- + // initialization from the encoding bits of a previous encoding if Planar mode is detected + // + void Block4x4Encoding_RGB8::InitFromEncodingBits_Planar(void) + { + + m_mode = MODE_PLANAR; + + unsigned char ucOriginRed = m_pencodingbitsRGB8->planar.originRed; + unsigned char ucOriginGreen = (unsigned char)((m_pencodingbitsRGB8->planar.originGreen1 << 6) + + m_pencodingbitsRGB8->planar.originGreen2); + unsigned char ucOriginBlue = (unsigned char)((m_pencodingbitsRGB8->planar.originBlue1 << 5) + + (m_pencodingbitsRGB8->planar.originBlue2 << 3) + + (m_pencodingbitsRGB8->planar.originBlue3 << 1) + + m_pencodingbitsRGB8->planar.originBlue4); + + unsigned char ucHorizRed = (unsigned char)((m_pencodingbitsRGB8->planar.horizRed1 << 1) + + m_pencodingbitsRGB8->planar.horizRed2); + unsigned char ucHorizGreen = m_pencodingbitsRGB8->planar.horizGreen; + unsigned char ucHorizBlue = (unsigned char)((m_pencodingbitsRGB8->planar.horizBlue1 << 5) + + m_pencodingbitsRGB8->planar.horizBlue2); + + unsigned char ucVertRed = (unsigned char)((m_pencodingbitsRGB8->planar.vertRed1 << 3) + + m_pencodingbitsRGB8->planar.vertRed2); + unsigned char ucVertGreen = (unsigned char)((m_pencodingbitsRGB8->planar.vertGreen1 << 2) + + m_pencodingbitsRGB8->planar.vertGreen2); + unsigned char ucVertBlue = m_pencodingbitsRGB8->planar.vertBlue; + + m_frgbaColor1 = ColorFloatRGBA::ConvertFromR6G7B6(ucOriginRed, ucOriginGreen, ucOriginBlue); + m_frgbaColor2 = ColorFloatRGBA::ConvertFromR6G7B6(ucHorizRed, ucHorizGreen, ucHorizBlue); + m_frgbaColor3 = ColorFloatRGBA::ConvertFromR6G7B6(ucVertRed, ucVertGreen, ucVertBlue); + + DecodePixels_Planar(); + + CalcBlockError(); + + } + + // ---------------------------------------------------------------------------------------------------- + // perform a single encoding iteration + // replace the encoding if a better encoding was found + // subsequent iterations generally take longer for each iteration + // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort + // + void Block4x4Encoding_RGB8::PerformIteration(float a_fEffort) + { + assert(!m_boolDone); + + switch (m_uiEncodingIterations) + { + case 0: + Block4x4Encoding_ETC1::PerformFirstIteration(); + if (m_boolDone) + { + break; + } + TryPlanar(0); + SetDoneIfPerfect(); + if (m_boolDone) + { + break; + } + TryTAndH(0); + break; + + case 1: + Block4x4Encoding_ETC1::TryDifferential(m_boolMostLikelyFlip, 1, 0, 0); + break; + + case 2: + Block4x4Encoding_ETC1::TryIndividual(m_boolMostLikelyFlip, 1); + break; + + case 3: + Block4x4Encoding_ETC1::TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0); + break; + + case 4: + Block4x4Encoding_ETC1::TryIndividual(!m_boolMostLikelyFlip, 1); + break; + + case 5: + TryPlanar(1); + if (a_fEffort <= 49.5f) + { + m_boolDone = true; + } + break; + + case 6: + TryTAndH(1); + if (a_fEffort <= 59.5f) + { + m_boolDone = true; + } + break; + + case 7: + Block4x4Encoding_ETC1::TryDegenerates1(); + if (a_fEffort <= 69.5f) + { + m_boolDone = true; + } + break; + + case 8: + Block4x4Encoding_ETC1::TryDegenerates2(); + if (a_fEffort <= 79.5f) + { + m_boolDone = true; + } + break; + + case 9: + Block4x4Encoding_ETC1::TryDegenerates3(); + if (a_fEffort <= 89.5f) + { + m_boolDone = true; + } + break; + + case 10: + Block4x4Encoding_ETC1::TryDegenerates4(); + m_boolDone = true; + break; + + default: + assert(0); + break; + } + + m_uiEncodingIterations++; + + SetDoneIfPerfect(); + } + + // ---------------------------------------------------------------------------------------------------- + // try encoding in Planar mode + // save this encoding if it improves the error + // + void Block4x4Encoding_RGB8::TryPlanar(unsigned int a_uiRadius) + { + Block4x4Encoding_RGB8 encodingTry = *this; + + // init "try" + { + encodingTry.m_mode = MODE_PLANAR; + encodingTry.m_boolDiff = true; + encodingTry.m_boolFlip = false; + } + + encodingTry.CalculatePlanarCornerColors(); + + encodingTry.DecodePixels_Planar(); + + encodingTry.CalcBlockError(); + + if (a_uiRadius > 0) + { + encodingTry.TwiddlePlanar(); + } + + if (encodingTry.m_fError < m_fError) + { + m_mode = MODE_PLANAR; + m_boolDiff = true; + m_boolFlip = false; + m_frgbaColor1 = encodingTry.m_frgbaColor1; + m_frgbaColor2 = encodingTry.m_frgbaColor2; + m_frgbaColor3 = encodingTry.m_frgbaColor3; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; + } + + m_fError = encodingTry.m_fError; + } + + } + + // ---------------------------------------------------------------------------------------------------- + // try encoding in T mode or H mode + // save this encoding if it improves the error + // + void Block4x4Encoding_RGB8::TryTAndH(unsigned int a_uiRadius) + { + + CalculateBaseColorsForTAndH(); + + TryT(a_uiRadius); + + TryH(a_uiRadius); + + } + + // ---------------------------------------------------------------------------------------------------- + // calculate original values for base colors + // store them in m_frgbaOriginalColor1 and m_frgbaOriginalColor2 + // + void Block4x4Encoding_RGB8::CalculateBaseColorsForTAndH(void) + { + + bool boolRGBX = m_pblockParent->GetImageSource()->GetErrorMetric() == ErrorMetric::RGBX; + + ColorFloatRGBA frgbaBlockAverage = (m_frgbaSourceAverageLeft + m_frgbaSourceAverageRight) * 0.5f; + + // find pixel farthest from average gray line + unsigned int uiFarthestPixel = 0; + float fFarthestGrayDistance2 = 0.0f; + unsigned int uiTransparentPixels = 0; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + // don't count transparent + if (m_pafrgbaSource[uiPixel].fA == 0.0f && !boolRGBX) + { + uiTransparentPixels++; + } + else + { + float fGrayDistance2 = CalcGrayDistance2(m_pafrgbaSource[uiPixel], frgbaBlockAverage); + + if (fGrayDistance2 > fFarthestGrayDistance2) + { + uiFarthestPixel = uiPixel; + fFarthestGrayDistance2 = fGrayDistance2; + } + } + } + // a transparent block should not reach this method + assert(uiTransparentPixels < PIXELS); + + // set the original base colors to: + // half way to the farthest pixel and + // the mirror color on the other side of the average + ColorFloatRGBA frgbaOffset = (m_pafrgbaSource[uiFarthestPixel] - frgbaBlockAverage) * 0.5f; + m_frgbaOriginalColor1_TAndH = (frgbaBlockAverage + frgbaOffset).QuantizeR4G4B4(); + m_frgbaOriginalColor2_TAndH = (frgbaBlockAverage - frgbaOffset).ClampRGB().QuantizeR4G4B4(); // the "other side" might be out of range + + // move base colors to find best fit + for (unsigned int uiIteration = 0; uiIteration < 10; uiIteration++) + { + // find the center of pixels closest to each color + float fPixelsCloserToColor1 = 0.0f; + ColorFloatRGBA frgbSumPixelsCloserToColor1; + float fPixelsCloserToColor2 = 0.0f; + ColorFloatRGBA frgbSumPixelsCloserToColor2; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + // don't count transparent pixels + if (m_pafrgbaSource[uiPixel].fA == 0.0f) + { + continue; + } + + float fGrayDistance2ToColor1 = CalcGrayDistance2(m_pafrgbaSource[uiPixel], m_frgbaOriginalColor1_TAndH); + float fGrayDistance2ToColor2 = CalcGrayDistance2(m_pafrgbaSource[uiPixel], m_frgbaOriginalColor2_TAndH); + + ColorFloatRGBA frgbaAlphaWeightedSource = m_pafrgbaSource[uiPixel] * m_pafrgbaSource[uiPixel].fA; + + if (fGrayDistance2ToColor1 <= fGrayDistance2ToColor2) + { + fPixelsCloserToColor1 += m_pafrgbaSource[uiPixel].fA; + frgbSumPixelsCloserToColor1 = frgbSumPixelsCloserToColor1 + frgbaAlphaWeightedSource; + } + else + { + fPixelsCloserToColor2 += m_pafrgbaSource[uiPixel].fA; + frgbSumPixelsCloserToColor2 = frgbSumPixelsCloserToColor2 + frgbaAlphaWeightedSource; + } + } + if (fPixelsCloserToColor1 == 0.0f || fPixelsCloserToColor2 == 0.0f) + { + break; + } + + ColorFloatRGBA frgbAvgColor1Pixels = (frgbSumPixelsCloserToColor1 * (1.0f / fPixelsCloserToColor1)).QuantizeR4G4B4(); + ColorFloatRGBA frgbAvgColor2Pixels = (frgbSumPixelsCloserToColor2 * (1.0f / fPixelsCloserToColor2)).QuantizeR4G4B4(); + + if (frgbAvgColor1Pixels.fR == m_frgbaOriginalColor1_TAndH.fR && + frgbAvgColor1Pixels.fG == m_frgbaOriginalColor1_TAndH.fG && + frgbAvgColor1Pixels.fB == m_frgbaOriginalColor1_TAndH.fB && + frgbAvgColor2Pixels.fR == m_frgbaOriginalColor2_TAndH.fR && + frgbAvgColor2Pixels.fG == m_frgbaOriginalColor2_TAndH.fG && + frgbAvgColor2Pixels.fB == m_frgbaOriginalColor2_TAndH.fB) + { + break; + } + + m_frgbaOriginalColor1_TAndH = frgbAvgColor1Pixels; + m_frgbaOriginalColor2_TAndH = frgbAvgColor2Pixels; + } + + } + + // ---------------------------------------------------------------------------------------------------- + // try encoding in T mode + // save this encoding if it improves the error + // + // since pixels that use base color1 don't use the distance table, color1 and color2 can be twiddled independently + // better encoding can be found if TWIDDLE_RADIUS is set to 2, but it will be much slower + // + void Block4x4Encoding_RGB8::TryT(unsigned int a_uiRadius) + { + Block4x4Encoding_RGB8 encodingTry = *this; + + // init "try" + { + encodingTry.m_mode = MODE_T; + encodingTry.m_boolDiff = true; + encodingTry.m_boolFlip = false; + encodingTry.m_fError = FLT_MAX; + } + + int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f); + int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f); + int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f); + + int iMinRed1 = iColor1Red - (int)a_uiRadius; + if (iMinRed1 < 0) + { + iMinRed1 = 0; + } + int iMaxRed1 = iColor1Red + (int)a_uiRadius; + if (iMaxRed1 > 15) + { + iMinRed1 = 15; + } + + int iMinGreen1 = iColor1Green - (int)a_uiRadius; + if (iMinGreen1 < 0) + { + iMinGreen1 = 0; + } + int iMaxGreen1 = iColor1Green + (int)a_uiRadius; + if (iMaxGreen1 > 15) + { + iMinGreen1 = 15; + } + + int iMinBlue1 = iColor1Blue - (int)a_uiRadius; + if (iMinBlue1 < 0) + { + iMinBlue1 = 0; + } + int iMaxBlue1 = iColor1Blue + (int)a_uiRadius; + if (iMaxBlue1 > 15) + { + iMinBlue1 = 15; + } + + int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f); + int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f); + int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f); + + int iMinRed2 = iColor2Red - (int)a_uiRadius; + if (iMinRed2 < 0) + { + iMinRed2 = 0; + } + int iMaxRed2 = iColor2Red + (int)a_uiRadius; + if (iMaxRed2 > 15) + { + iMinRed2 = 15; + } + + int iMinGreen2 = iColor2Green - (int)a_uiRadius; + if (iMinGreen2 < 0) + { + iMinGreen2 = 0; + } + int iMaxGreen2 = iColor2Green + (int)a_uiRadius; + if (iMaxGreen2 > 15) + { + iMinGreen2 = 15; + } + + int iMinBlue2 = iColor2Blue - (int)a_uiRadius; + if (iMinBlue2 < 0) + { + iMinBlue2 = 0; + } + int iMaxBlue2 = iColor2Blue + (int)a_uiRadius; + if (iMaxBlue2 > 15) + { + iMinBlue2 = 15; + } + + for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++) + { + encodingTry.m_uiCW1 = uiDistance; + + // twiddle m_frgbaOriginalColor2_TAndH + // twiddle color2 first, since it affects 3 selectors, while color1 only affects one selector + // + for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++) + { + for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++) + { + for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++) + { + for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++) + { + if (uiBaseColorSwaps == 0) + { + encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH; + encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2); + } + else + { + encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2); + encodingTry.m_frgbaColor2 = m_frgbaOriginalColor1_TAndH; + } + + encodingTry.TryT_BestSelectorCombination(); + + if (encodingTry.m_fError < m_fError) + { + m_mode = encodingTry.m_mode; + m_boolDiff = encodingTry.m_boolDiff; + m_boolFlip = encodingTry.m_boolFlip; + + m_frgbaColor1 = encodingTry.m_frgbaColor1; + m_frgbaColor2 = encodingTry.m_frgbaColor2; + m_uiCW1 = encodingTry.m_uiCW1; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel]; + m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; + } + + m_fError = encodingTry.m_fError; + } + } + } + } + } + + // twiddle m_frgbaOriginalColor1_TAndH + for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++) + { + for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++) + { + for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++) + { + for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++) + { + if (uiBaseColorSwaps == 0) + { + encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1); + encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH; + } + else + { + encodingTry.m_frgbaColor1 = m_frgbaOriginalColor2_TAndH; + encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1); + } + + encodingTry.TryT_BestSelectorCombination(); + + if (encodingTry.m_fError < m_fError) + { + m_mode = encodingTry.m_mode; + m_boolDiff = encodingTry.m_boolDiff; + m_boolFlip = encodingTry.m_boolFlip; + + m_frgbaColor1 = encodingTry.m_frgbaColor1; + m_frgbaColor2 = encodingTry.m_frgbaColor2; + m_uiCW1 = encodingTry.m_uiCW1; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel]; + m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; + } + + m_fError = encodingTry.m_fError; + } + } + } + } + } + + } + + } + + // ---------------------------------------------------------------------------------------------------- + // find best selector combination for TryT + // called on an encodingTry + // + void Block4x4Encoding_RGB8::TryT_BestSelectorCombination(void) + { + + float fDistance = s_afTHDistanceTable[m_uiCW1]; + + unsigned int auiBestPixelSelectors[PIXELS]; + float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, + FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; + ColorFloatRGBA afrgbaBestDecodedPixels[PIXELS]; + ColorFloatRGBA afrgbaDecodedPixel[SELECTORS]; + + assert(SELECTORS == 4); + afrgbaDecodedPixel[0] = m_frgbaColor1; + afrgbaDecodedPixel[1] = (m_frgbaColor2 + fDistance).ClampRGB(); + afrgbaDecodedPixel[2] = m_frgbaColor2; + afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB(); + + // try each selector + for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++) + { + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + + float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel], + m_pafrgbaSource[uiPixel]); + + if (fPixelError < afBestPixelErrors[uiPixel]) + { + afBestPixelErrors[uiPixel] = fPixelError; + auiBestPixelSelectors[uiPixel] = uiSelector; + afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector]; + } + } + } + + + // add up all of the pixel errors + float fBlockError = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + fBlockError += afBestPixelErrors[uiPixel]; + } + + if (fBlockError < m_fError) + { + m_fError = fBlockError; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel]; + m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel]; + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // try encoding in T mode + // save this encoding if it improves the error + // + // since all pixels use the distance table, color1 and color2 can NOT be twiddled independently + // TWIDDLE_RADIUS of 2 is WAY too slow + // + void Block4x4Encoding_RGB8::TryH(unsigned int a_uiRadius) + { + Block4x4Encoding_RGB8 encodingTry = *this; + + // init "try" + { + encodingTry.m_mode = MODE_H; + encodingTry.m_boolDiff = true; + encodingTry.m_boolFlip = false; + encodingTry.m_fError = FLT_MAX; + } + + int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f); + int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f); + int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f); + + int iMinRed1 = iColor1Red - (int)a_uiRadius; + if (iMinRed1 < 0) + { + iMinRed1 = 0; + } + int iMaxRed1 = iColor1Red + (int)a_uiRadius; + if (iMaxRed1 > 15) + { + iMinRed1 = 15; + } + + int iMinGreen1 = iColor1Green - (int)a_uiRadius; + if (iMinGreen1 < 0) + { + iMinGreen1 = 0; + } + int iMaxGreen1 = iColor1Green + (int)a_uiRadius; + if (iMaxGreen1 > 15) + { + iMinGreen1 = 15; + } + + int iMinBlue1 = iColor1Blue - (int)a_uiRadius; + if (iMinBlue1 < 0) + { + iMinBlue1 = 0; + } + int iMaxBlue1 = iColor1Blue + (int)a_uiRadius; + if (iMaxBlue1 > 15) + { + iMinBlue1 = 15; + } + + int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f); + int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f); + int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f); + + int iMinRed2 = iColor2Red - (int)a_uiRadius; + if (iMinRed2 < 0) + { + iMinRed2 = 0; + } + int iMaxRed2 = iColor2Red + (int)a_uiRadius; + if (iMaxRed2 > 15) + { + iMinRed2 = 15; + } + + int iMinGreen2 = iColor2Green - (int)a_uiRadius; + if (iMinGreen2 < 0) + { + iMinGreen2 = 0; + } + int iMaxGreen2 = iColor2Green + (int)a_uiRadius; + if (iMaxGreen2 > 15) + { + iMinGreen2 = 15; + } + + int iMinBlue2 = iColor2Blue - (int)a_uiRadius; + if (iMinBlue2 < 0) + { + iMinBlue2 = 0; + } + int iMaxBlue2 = iColor2Blue + (int)a_uiRadius; + if (iMaxBlue2 > 15) + { + iMinBlue2 = 15; + } + + for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++) + { + encodingTry.m_uiCW1 = uiDistance; + + // twiddle m_frgbaOriginalColor1_TAndH + for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++) + { + for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++) + { + for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++) + { + encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1); + encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH; + + // if color1 == color2, H encoding issues can pop up, so abort + if (iRed1 == iColor2Red && iGreen1 == iColor2Green && iBlue1 == iColor2Blue) + { + continue; + } + + encodingTry.TryH_BestSelectorCombination(); + + if (encodingTry.m_fError < m_fError) + { + m_mode = encodingTry.m_mode; + m_boolDiff = encodingTry.m_boolDiff; + m_boolFlip = encodingTry.m_boolFlip; + + m_frgbaColor1 = encodingTry.m_frgbaColor1; + m_frgbaColor2 = encodingTry.m_frgbaColor2; + m_uiCW1 = encodingTry.m_uiCW1; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel]; + m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; + } + + m_fError = encodingTry.m_fError; + } + } + } + } + + // twiddle m_frgbaOriginalColor2_TAndH + for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++) + { + for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++) + { + for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++) + { + encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH; + encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2); + + // if color1 == color2, H encoding issues can pop up, so abort + if (iRed2 == iColor1Red && iGreen2 == iColor1Green && iBlue2 == iColor1Blue) + { + continue; + } + + encodingTry.TryH_BestSelectorCombination(); + + if (encodingTry.m_fError < m_fError) + { + m_mode = encodingTry.m_mode; + m_boolDiff = encodingTry.m_boolDiff; + m_boolFlip = encodingTry.m_boolFlip; + + m_frgbaColor1 = encodingTry.m_frgbaColor1; + m_frgbaColor2 = encodingTry.m_frgbaColor2; + m_uiCW1 = encodingTry.m_uiCW1; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel]; + m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; + } + + m_fError = encodingTry.m_fError; + } + } + } + } + + } + + } + + // ---------------------------------------------------------------------------------------------------- + // find best selector combination for TryH + // called on an encodingTry + // + void Block4x4Encoding_RGB8::TryH_BestSelectorCombination(void) + { + + float fDistance = s_afTHDistanceTable[m_uiCW1]; + + unsigned int auiBestPixelSelectors[PIXELS]; + float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, + FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; + ColorFloatRGBA afrgbaBestDecodedPixels[PIXELS]; + ColorFloatRGBA afrgbaDecodedPixel[SELECTORS]; + + assert(SELECTORS == 4); + afrgbaDecodedPixel[0] = (m_frgbaColor1 + fDistance).ClampRGB(); + afrgbaDecodedPixel[1] = (m_frgbaColor1 - fDistance).ClampRGB(); + afrgbaDecodedPixel[2] = (m_frgbaColor2 + fDistance).ClampRGB(); + afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB(); + + // try each selector + for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++) + { + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + + float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel], + m_pafrgbaSource[uiPixel]); + + if (fPixelError < afBestPixelErrors[uiPixel]) + { + afBestPixelErrors[uiPixel] = fPixelError; + auiBestPixelSelectors[uiPixel] = uiSelector; + afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector]; + } + } + } + + + // add up all of the pixel errors + float fBlockError = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + fBlockError += afBestPixelErrors[uiPixel]; + } + + if (fBlockError < m_fError) + { + m_fError = fBlockError; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel]; + m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel]; + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // use linear regression to find the best fit for colors along the edges of the 4x4 block + // + void Block4x4Encoding_RGB8::CalculatePlanarCornerColors(void) + { + ColorFloatRGBA afrgbaRegression[MAX_PLANAR_REGRESSION_SIZE]; + ColorFloatRGBA frgbaSlope; + ColorFloatRGBA frgbaOffset; + + // top edge + afrgbaRegression[0] = m_pafrgbaSource[0]; + afrgbaRegression[1] = m_pafrgbaSource[4]; + afrgbaRegression[2] = m_pafrgbaSource[8]; + afrgbaRegression[3] = m_pafrgbaSource[12]; + ColorRegression(afrgbaRegression, 4, &frgbaSlope, &frgbaOffset); + m_frgbaColor1 = frgbaOffset; + m_frgbaColor2 = (frgbaSlope * 4.0f) + frgbaOffset; + + // left edge + afrgbaRegression[0] = m_pafrgbaSource[0]; + afrgbaRegression[1] = m_pafrgbaSource[1]; + afrgbaRegression[2] = m_pafrgbaSource[2]; + afrgbaRegression[3] = m_pafrgbaSource[3]; + ColorRegression(afrgbaRegression, 4, &frgbaSlope, &frgbaOffset); + m_frgbaColor1 = (m_frgbaColor1 + frgbaOffset) * 0.5f; // average with top edge + m_frgbaColor3 = (frgbaSlope * 4.0f) + frgbaOffset; + + // right edge + afrgbaRegression[0] = m_pafrgbaSource[12]; + afrgbaRegression[1] = m_pafrgbaSource[13]; + afrgbaRegression[2] = m_pafrgbaSource[14]; + afrgbaRegression[3] = m_pafrgbaSource[15]; + ColorRegression(afrgbaRegression, 4, &frgbaSlope, &frgbaOffset); + m_frgbaColor2 = (m_frgbaColor2 + frgbaOffset) * 0.5f; // average with top edge + + // bottom edge + afrgbaRegression[0] = m_pafrgbaSource[3]; + afrgbaRegression[1] = m_pafrgbaSource[7]; + afrgbaRegression[2] = m_pafrgbaSource[11]; + afrgbaRegression[3] = m_pafrgbaSource[15]; + ColorRegression(afrgbaRegression, 4, &frgbaSlope, &frgbaOffset); + m_frgbaColor3 = (m_frgbaColor3 + frgbaOffset) * 0.5f; // average with left edge + + // quantize corner colors to 6/7/6 + m_frgbaColor1 = m_frgbaColor1.QuantizeR6G7B6(); + m_frgbaColor2 = m_frgbaColor2.QuantizeR6G7B6(); + m_frgbaColor3 = m_frgbaColor3.QuantizeR6G7B6(); + + } + + // ---------------------------------------------------------------------------------------------------- + // try different corner colors by slightly changing R, G and B independently + // + // R, G and B decoding and errors are independent, so R, G and B twiddles can be independent + // + // return true if improvement + // + bool Block4x4Encoding_RGB8::TwiddlePlanar(void) + { + bool boolImprovement = false; + + while (TwiddlePlanarR()) + { + boolImprovement = true; + } + + while (TwiddlePlanarG()) + { + boolImprovement = true; + } + + while (TwiddlePlanarB()) + { + boolImprovement = true; + } + + return boolImprovement; + } + + // ---------------------------------------------------------------------------------------------------- + // try different corner colors by slightly changing R + // + bool Block4x4Encoding_RGB8::TwiddlePlanarR() + { + bool boolImprovement = false; + + Block4x4Encoding_RGB8 encodingTry = *this; + + // init "try" + { + encodingTry.m_mode = MODE_PLANAR; + encodingTry.m_boolDiff = true; + encodingTry.m_boolFlip = false; + } + + int iOriginRed = encodingTry.m_frgbaColor1.IntRed(63.0f); + int iHorizRed = encodingTry.m_frgbaColor2.IntRed(63.0f); + int iVertRed = encodingTry.m_frgbaColor3.IntRed(63.0f); + + for (int iTryOriginRed = iOriginRed - 1; iTryOriginRed <= iOriginRed + 1; iTryOriginRed++) + { + // check for out of range + if (iTryOriginRed < 0 || iTryOriginRed > 63) + { + continue; + } + + encodingTry.m_frgbaColor1.fR = ((iTryOriginRed << 2) + (iTryOriginRed >> 4)) / 255.0f; + + for (int iTryHorizRed = iHorizRed - 1; iTryHorizRed <= iHorizRed + 1; iTryHorizRed++) + { + // check for out of range + if (iTryHorizRed < 0 || iTryHorizRed > 63) + { + continue; + } + + encodingTry.m_frgbaColor2.fR = ((iTryHorizRed << 2) + (iTryHorizRed >> 4)) / 255.0f; + + for (int iTryVertRed = iVertRed - 1; iTryVertRed <= iVertRed + 1; iTryVertRed++) + { + // check for out of range + if (iTryVertRed < 0 || iTryVertRed > 63) + { + continue; + } + + // don't bother with null twiddle + if (iTryOriginRed == iOriginRed && iTryHorizRed == iHorizRed && iTryVertRed == iVertRed) + { + continue; + } + + encodingTry.m_frgbaColor3.fR = ((iTryVertRed << 2) + (iTryVertRed >> 4)) / 255.0f; + + encodingTry.DecodePixels_Planar(); + + encodingTry.CalcBlockError(); + + if (encodingTry.m_fError < m_fError) + { + m_mode = MODE_PLANAR; + m_boolDiff = true; + m_boolFlip = false; + m_frgbaColor1 = encodingTry.m_frgbaColor1; + m_frgbaColor2 = encodingTry.m_frgbaColor2; + m_frgbaColor3 = encodingTry.m_frgbaColor3; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; + } + + m_fError = encodingTry.m_fError; + + boolImprovement = true; + } + } + } + } + + return boolImprovement; + } + + // ---------------------------------------------------------------------------------------------------- + // try different corner colors by slightly changing G + // + bool Block4x4Encoding_RGB8::TwiddlePlanarG() + { + bool boolImprovement = false; + + Block4x4Encoding_RGB8 encodingTry = *this; + + // init "try" + { + encodingTry.m_mode = MODE_PLANAR; + encodingTry.m_boolDiff = true; + encodingTry.m_boolFlip = false; + } + + int iOriginGreen = encodingTry.m_frgbaColor1.IntGreen(127.0f); + int iHorizGreen = encodingTry.m_frgbaColor2.IntGreen(127.0f); + int iVertGreen = encodingTry.m_frgbaColor3.IntGreen(127.0f); + + for (int iTryOriginGreen = iOriginGreen - 1; iTryOriginGreen <= iOriginGreen + 1; iTryOriginGreen++) + { + // check for out of range + if (iTryOriginGreen < 0 || iTryOriginGreen > 127) + { + continue; + } + + encodingTry.m_frgbaColor1.fG = ((iTryOriginGreen << 1) + (iTryOriginGreen >> 6)) / 255.0f; + + for (int iTryHorizGreen = iHorizGreen - 1; iTryHorizGreen <= iHorizGreen + 1; iTryHorizGreen++) + { + // check for out of range + if (iTryHorizGreen < 0 || iTryHorizGreen > 127) + { + continue; + } + + encodingTry.m_frgbaColor2.fG = ((iTryHorizGreen << 1) + (iTryHorizGreen >> 6)) / 255.0f; + + for (int iTryVertGreen = iVertGreen - 1; iTryVertGreen <= iVertGreen + 1; iTryVertGreen++) + { + // check for out of range + if (iTryVertGreen < 0 || iTryVertGreen > 127) + { + continue; + } + + // don't bother with null twiddle + if (iTryOriginGreen == iOriginGreen && + iTryHorizGreen == iHorizGreen && + iTryVertGreen == iVertGreen) + { + continue; + } + + encodingTry.m_frgbaColor3.fG = ((iTryVertGreen << 1) + (iTryVertGreen >> 6)) / 255.0f; + + encodingTry.DecodePixels_Planar(); + + encodingTry.CalcBlockError(); + + if (encodingTry.m_fError < m_fError) + { + m_mode = MODE_PLANAR; + m_boolDiff = true; + m_boolFlip = false; + m_frgbaColor1 = encodingTry.m_frgbaColor1; + m_frgbaColor2 = encodingTry.m_frgbaColor2; + m_frgbaColor3 = encodingTry.m_frgbaColor3; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; + } + + m_fError = encodingTry.m_fError; + + boolImprovement = true; + } + } + } + } + + return boolImprovement; + } + + // ---------------------------------------------------------------------------------------------------- + // try different corner colors by slightly changing B + // + bool Block4x4Encoding_RGB8::TwiddlePlanarB() + { + bool boolImprovement = false; + + Block4x4Encoding_RGB8 encodingTry = *this; + + // init "try" + { + encodingTry.m_mode = MODE_PLANAR; + encodingTry.m_boolDiff = true; + encodingTry.m_boolFlip = false; + } + + int iOriginBlue = encodingTry.m_frgbaColor1.IntBlue(63.0f); + int iHorizBlue = encodingTry.m_frgbaColor2.IntBlue(63.0f); + int iVertBlue = encodingTry.m_frgbaColor3.IntBlue(63.0f); + + for (int iTryOriginBlue = iOriginBlue - 1; iTryOriginBlue <= iOriginBlue + 1; iTryOriginBlue++) + { + // check for out of range + if (iTryOriginBlue < 0 || iTryOriginBlue > 63) + { + continue; + } + + encodingTry.m_frgbaColor1.fB = ((iTryOriginBlue << 2) + (iTryOriginBlue >> 4)) / 255.0f; + + for (int iTryHorizBlue = iHorizBlue - 1; iTryHorizBlue <= iHorizBlue + 1; iTryHorizBlue++) + { + // check for out of range + if (iTryHorizBlue < 0 || iTryHorizBlue > 63) + { + continue; + } + + encodingTry.m_frgbaColor2.fB = ((iTryHorizBlue << 2) + (iTryHorizBlue >> 4)) / 255.0f; + + for (int iTryVertBlue = iVertBlue - 1; iTryVertBlue <= iVertBlue + 1; iTryVertBlue++) + { + // check for out of range + if (iTryVertBlue < 0 || iTryVertBlue > 63) + { + continue; + } + + // don't bother with null twiddle + if (iTryOriginBlue == iOriginBlue && iTryHorizBlue == iHorizBlue && iTryVertBlue == iVertBlue) + { + continue; + } + + encodingTry.m_frgbaColor3.fB = ((iTryVertBlue << 2) + (iTryVertBlue >> 4)) / 255.0f; + + encodingTry.DecodePixels_Planar(); + + encodingTry.CalcBlockError(); + + if (encodingTry.m_fError < m_fError) + { + m_mode = MODE_PLANAR; + m_boolDiff = true; + m_boolFlip = false; + m_frgbaColor1 = encodingTry.m_frgbaColor1; + m_frgbaColor2 = encodingTry.m_frgbaColor2; + m_frgbaColor3 = encodingTry.m_frgbaColor3; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; + } + + m_fError = encodingTry.m_fError; + + boolImprovement = true; + } + } + } + } + + return boolImprovement; + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state + // + void Block4x4Encoding_RGB8::SetEncodingBits(void) + { + + switch (m_mode) + { + case MODE_ETC1: + Block4x4Encoding_ETC1::SetEncodingBits(); + break; + + case MODE_T: + SetEncodingBits_T(); + break; + + case MODE_H: + SetEncodingBits_H(); + break; + + case MODE_PLANAR: + SetEncodingBits_Planar(); + break; + + default: + assert(false); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state for T mode + // + void Block4x4Encoding_RGB8::SetEncodingBits_T(void) + { + static const bool SANITY_CHECK = true; + + assert(m_mode == MODE_T); + assert(m_boolDiff == true); + + unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f); + unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f); + unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f); + + unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f); + unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f); + unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f); + + m_pencodingbitsRGB8->t.red1a = uiRed1 >> 2; + m_pencodingbitsRGB8->t.red1b = uiRed1; + m_pencodingbitsRGB8->t.green1 = uiGreen1; + m_pencodingbitsRGB8->t.blue1 = uiBlue1; + + m_pencodingbitsRGB8->t.red2 = uiRed2; + m_pencodingbitsRGB8->t.green2 = uiGreen2; + m_pencodingbitsRGB8->t.blue2 = uiBlue2; + + m_pencodingbitsRGB8->t.da = m_uiCW1 >> 1; + m_pencodingbitsRGB8->t.db = m_uiCW1; + + m_pencodingbitsRGB8->t.diff = 1; + + Block4x4Encoding_ETC1::SetEncodingBits_Selectors(); + + // create an invalid R differential to trigger T mode + m_pencodingbitsRGB8->t.detect1 = 0; + m_pencodingbitsRGB8->t.detect2 = 0; + int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; + if (iRed2 >= 4) + { + m_pencodingbitsRGB8->t.detect1 = 7; + m_pencodingbitsRGB8->t.detect2 = 0; + } + else + { + m_pencodingbitsRGB8->t.detect1 = 0; + m_pencodingbitsRGB8->t.detect2 = 1; + } + + if (SANITY_CHECK) + { + iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; + + // make sure red overflows + assert(iRed2 < 0 || iRed2 > 31); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state for H mode + // + // colors and selectors may need to swap in order to generate lsb of distance index + // + void Block4x4Encoding_RGB8::SetEncodingBits_H(void) + { + static const bool SANITY_CHECK = true; + + assert(m_mode == MODE_H); + assert(m_boolDiff == true); + + unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f); + unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f); + unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f); + + unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f); + unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f); + unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f); + + unsigned int uiColor1 = (uiRed1 << 16) + (uiGreen1 << 8) + uiBlue1; + unsigned int uiColor2 = (uiRed2 << 16) + (uiGreen2 << 8) + uiBlue2; + + bool boolOddDistance = m_uiCW1 & 1; + bool boolSwapColors = (uiColor1 < uiColor2) ^ !boolOddDistance; + + if (boolSwapColors) + { + m_pencodingbitsRGB8->h.red1 = uiRed2; + m_pencodingbitsRGB8->h.green1a = uiGreen2 >> 1; + m_pencodingbitsRGB8->h.green1b = uiGreen2; + m_pencodingbitsRGB8->h.blue1a = uiBlue2 >> 3; + m_pencodingbitsRGB8->h.blue1b = uiBlue2 >> 1; + m_pencodingbitsRGB8->h.blue1c = uiBlue2; + + m_pencodingbitsRGB8->h.red2 = uiRed1; + m_pencodingbitsRGB8->h.green2a = uiGreen1 >> 1; + m_pencodingbitsRGB8->h.green2b = uiGreen1; + m_pencodingbitsRGB8->h.blue2 = uiBlue1; + + m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2; + m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1; + } + else + { + m_pencodingbitsRGB8->h.red1 = uiRed1; + m_pencodingbitsRGB8->h.green1a = uiGreen1 >> 1; + m_pencodingbitsRGB8->h.green1b = uiGreen1; + m_pencodingbitsRGB8->h.blue1a = uiBlue1 >> 3; + m_pencodingbitsRGB8->h.blue1b = uiBlue1 >> 1; + m_pencodingbitsRGB8->h.blue1c = uiBlue1; + + m_pencodingbitsRGB8->h.red2 = uiRed2; + m_pencodingbitsRGB8->h.green2a = uiGreen2 >> 1; + m_pencodingbitsRGB8->h.green2b = uiGreen2; + m_pencodingbitsRGB8->h.blue2 = uiBlue2; + + m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2; + m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1; + } + + m_pencodingbitsRGB8->h.diff = 1; + + Block4x4Encoding_ETC1::SetEncodingBits_Selectors(); + + if (boolSwapColors) + { + m_pencodingbitsRGB8->h.selectors ^= 0x0000FFFF; + } + + // create an invalid R differential to trigger T mode + m_pencodingbitsRGB8->h.detect1 = 0; + m_pencodingbitsRGB8->h.detect2 = 0; + m_pencodingbitsRGB8->h.detect3 = 0; + int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; + int iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2; + if (iRed2 < 0 || iRed2 > 31) + { + m_pencodingbitsRGB8->h.detect1 = 1; + } + if (iGreen2 >= 4) + { + m_pencodingbitsRGB8->h.detect2 = 7; + m_pencodingbitsRGB8->h.detect3 = 0; + } + else + { + m_pencodingbitsRGB8->h.detect2 = 0; + m_pencodingbitsRGB8->h.detect3 = 1; + } + + if (SANITY_CHECK) + { + iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; + iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2; + + // make sure red doesn't overflow and green does + assert(iRed2 >= 0 && iRed2 <= 31); + assert(iGreen2 < 0 || iGreen2 > 31); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state for Planar mode + // + void Block4x4Encoding_RGB8::SetEncodingBits_Planar(void) + { + static const bool SANITY_CHECK = true; + + assert(m_mode == MODE_PLANAR); + assert(m_boolDiff == true); + + unsigned int uiOriginRed = (unsigned int)m_frgbaColor1.IntRed(63.0f); + unsigned int uiOriginGreen = (unsigned int)m_frgbaColor1.IntGreen(127.0f); + unsigned int uiOriginBlue = (unsigned int)m_frgbaColor1.IntBlue(63.0f); + + unsigned int uiHorizRed = (unsigned int)m_frgbaColor2.IntRed(63.0f); + unsigned int uiHorizGreen = (unsigned int)m_frgbaColor2.IntGreen(127.0f); + unsigned int uiHorizBlue = (unsigned int)m_frgbaColor2.IntBlue(63.0f); + + unsigned int uiVertRed = (unsigned int)m_frgbaColor3.IntRed(63.0f); + unsigned int uiVertGreen = (unsigned int)m_frgbaColor3.IntGreen(127.0f); + unsigned int uiVertBlue = (unsigned int)m_frgbaColor3.IntBlue(63.0f); + + m_pencodingbitsRGB8->planar.originRed = uiOriginRed; + m_pencodingbitsRGB8->planar.originGreen1 = uiOriginGreen >> 6; + m_pencodingbitsRGB8->planar.originGreen2 = uiOriginGreen; + m_pencodingbitsRGB8->planar.originBlue1 = uiOriginBlue >> 5; + m_pencodingbitsRGB8->planar.originBlue2 = uiOriginBlue >> 3; + m_pencodingbitsRGB8->planar.originBlue3 = uiOriginBlue >> 1; + m_pencodingbitsRGB8->planar.originBlue4 = uiOriginBlue; + + m_pencodingbitsRGB8->planar.horizRed1 = uiHorizRed >> 1; + m_pencodingbitsRGB8->planar.horizRed2 = uiHorizRed; + m_pencodingbitsRGB8->planar.horizGreen = uiHorizGreen; + m_pencodingbitsRGB8->planar.horizBlue1 = uiHorizBlue >> 5; + m_pencodingbitsRGB8->planar.horizBlue2 = uiHorizBlue; + + m_pencodingbitsRGB8->planar.vertRed1 = uiVertRed >> 3; + m_pencodingbitsRGB8->planar.vertRed2 = uiVertRed; + m_pencodingbitsRGB8->planar.vertGreen1 = uiVertGreen >> 2; + m_pencodingbitsRGB8->planar.vertGreen2 = uiVertGreen; + m_pencodingbitsRGB8->planar.vertBlue = uiVertBlue; + + m_pencodingbitsRGB8->planar.diff = 1; + + // create valid RG differentials and an invalid B differential to trigger planar mode + m_pencodingbitsRGB8->planar.detect1 = 0; + m_pencodingbitsRGB8->planar.detect2 = 0; + m_pencodingbitsRGB8->planar.detect3 = 0; + m_pencodingbitsRGB8->planar.detect4 = 0; + int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; + int iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2; + int iBlue2 = (int)m_pencodingbitsRGB8->differential.blue1 + (int)m_pencodingbitsRGB8->differential.dblue2; + if (iRed2 < 0 || iRed2 > 31) + { + m_pencodingbitsRGB8->planar.detect1 = 1; + } + if (iGreen2 < 0 || iGreen2 > 31) + { + m_pencodingbitsRGB8->planar.detect2 = 1; + } + if (iBlue2 >= 4) + { + m_pencodingbitsRGB8->planar.detect3 = 7; + m_pencodingbitsRGB8->planar.detect4 = 0; + } + else + { + m_pencodingbitsRGB8->planar.detect3 = 0; + m_pencodingbitsRGB8->planar.detect4 = 1; + } + + if (SANITY_CHECK) + { + iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; + iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2; + iBlue2 = (int)m_pencodingbitsRGB8->differential.blue1 + (int)m_pencodingbitsRGB8->differential.dblue2; + + // make sure red and green don't overflow and blue does + assert(iRed2 >= 0 && iRed2 <= 31); + assert(iGreen2 >= 0 && iGreen2 <= 31); + assert(iBlue2 < 0 || iBlue2 > 31); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // set the decoded colors and decoded alpha based on the encoding state for T mode + // + void Block4x4Encoding_RGB8::DecodePixels_T(void) + { + + float fDistance = s_afTHDistanceTable[m_uiCW1]; + ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f); + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + switch (m_auiSelectors[uiPixel]) + { + case 0: + m_afrgbaDecodedColors[uiPixel] = m_frgbaColor1; + break; + + case 1: + m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB(); + break; + + case 2: + m_afrgbaDecodedColors[uiPixel] = m_frgbaColor2; + break; + + case 3: + m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB(); + break; + } + + } + + } + + // ---------------------------------------------------------------------------------------------------- + // set the decoded colors and decoded alpha based on the encoding state for H mode + // + void Block4x4Encoding_RGB8::DecodePixels_H(void) + { + + float fDistance = s_afTHDistanceTable[m_uiCW1]; + ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f); + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + switch (m_auiSelectors[uiPixel]) + { + case 0: + m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 + frgbaDistance).ClampRGB(); + break; + + case 1: + m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 - frgbaDistance).ClampRGB(); + break; + + case 2: + m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB(); + break; + + case 3: + m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB(); + break; + } + + } + + } + + // ---------------------------------------------------------------------------------------------------- + // set the decoded colors and decoded alpha based on the encoding state for Planar mode + // + void Block4x4Encoding_RGB8::DecodePixels_Planar(void) + { + + int iRO = (int)roundf(m_frgbaColor1.fR * 255.0f); + int iGO = (int)roundf(m_frgbaColor1.fG * 255.0f); + int iBO = (int)roundf(m_frgbaColor1.fB * 255.0f); + + int iRH = (int)roundf(m_frgbaColor2.fR * 255.0f); + int iGH = (int)roundf(m_frgbaColor2.fG * 255.0f); + int iBH = (int)roundf(m_frgbaColor2.fB * 255.0f); + + int iRV = (int)roundf(m_frgbaColor3.fR * 255.0f); + int iGV = (int)roundf(m_frgbaColor3.fG * 255.0f); + int iBV = (int)roundf(m_frgbaColor3.fB * 255.0f); + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + int iX = (int)(uiPixel >> 2); + int iY = (int)(uiPixel & 3); + + int iR = (iX*(iRH - iRO) + iY*(iRV - iRO) + 4*iRO + 2) >> 2; + int iG = (iX*(iGH - iGO) + iY*(iGV - iGO) + 4*iGO + 2) >> 2; + int iB = (iX*(iBH - iBO) + iY*(iBV - iBO) + 4*iBO + 2) >> 2; + + ColorFloatRGBA frgba; + frgba.fR = (float)iR / 255.0f; + frgba.fG = (float)iG / 255.0f; + frgba.fB = (float)iB / 255.0f; + frgba.fA = 1.0f; + + m_afrgbaDecodedColors[uiPixel] = frgba.ClampRGB(); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // perform a linear regression for the a_uiPixels in a_pafrgbaPixels[] + // + // output the closest color line using a_pfrgbaSlope and a_pfrgbaOffset + // + void Block4x4Encoding_RGB8::ColorRegression(ColorFloatRGBA *a_pafrgbaPixels, unsigned int a_uiPixels, + ColorFloatRGBA *a_pfrgbaSlope, ColorFloatRGBA *a_pfrgbaOffset) + { + typedef struct + { + float f[4]; + } Float4; + + Float4 *paf4Pixels = (Float4 *)(a_pafrgbaPixels); + Float4 *pf4Slope = (Float4 *)(a_pfrgbaSlope); + Float4 *pf4Offset = (Float4 *)(a_pfrgbaOffset); + + float afX[MAX_PLANAR_REGRESSION_SIZE]; + float afY[MAX_PLANAR_REGRESSION_SIZE]; + + // handle r, g and b separately. don't bother with a + for (unsigned int uiComponent = 0; uiComponent < 3; uiComponent++) + { + for (unsigned int uiPixel = 0; uiPixel < a_uiPixels; uiPixel++) + { + afX[uiPixel] = (float)uiPixel; + afY[uiPixel] = paf4Pixels[uiPixel].f[uiComponent]; + + } + Etc::Regression(afX, afY, a_uiPixels, + &(pf4Slope->f[uiComponent]), &(pf4Offset->f[uiComponent])); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // +} diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGB8.h b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGB8.h new file mode 100644 index 0000000000..03754d5e3b --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGB8.h @@ -0,0 +1,96 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcBlock4x4Encoding_ETC1.h" + +namespace Etc +{ + + class Block4x4Encoding_RGB8 : public Block4x4Encoding_ETC1 + { + public: + + Block4x4Encoding_RGB8(void); + virtual ~Block4x4Encoding_RGB8(void); + + virtual void InitFromEncodingBits(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + + ErrorMetric a_errormetric); + + virtual void PerformIteration(float a_fEffort); + + virtual void SetEncodingBits(void); + + inline ColorFloatRGBA GetColor3(void) const + { + return m_frgbaColor3; + } + + protected: + + static const unsigned int PLANAR_CORNER_COLORS = 3; + static const unsigned int MAX_PLANAR_REGRESSION_SIZE = 4; + static const unsigned int TH_DISTANCES = 8; + + static float s_afTHDistanceTable[TH_DISTANCES]; + + void TryPlanar(unsigned int a_uiRadius); + void TryTAndH(unsigned int a_uiRadius); + + void InitFromEncodingBits_Planar(void); + + ColorFloatRGBA m_frgbaColor3; // used for planar + + void SetEncodingBits_T(void); + void SetEncodingBits_H(void); + void SetEncodingBits_Planar(void); + + // state shared between iterations + ColorFloatRGBA m_frgbaOriginalColor1_TAndH; + ColorFloatRGBA m_frgbaOriginalColor2_TAndH; + + void CalculateBaseColorsForTAndH(void); + void TryT(unsigned int a_uiRadius); + void TryT_BestSelectorCombination(void); + void TryH(unsigned int a_uiRadius); + void TryH_BestSelectorCombination(void); + + private: + + void InitFromEncodingBits_T(void); + void InitFromEncodingBits_H(void); + + void CalculatePlanarCornerColors(void); + + void ColorRegression(ColorFloatRGBA *a_pafrgbaPixels, unsigned int a_uiPixels, + ColorFloatRGBA *a_pfrgbaSlope, ColorFloatRGBA *a_pfrgbaOffset); + + bool TwiddlePlanar(void); + bool TwiddlePlanarR(); + bool TwiddlePlanarG(); + bool TwiddlePlanarB(); + + void DecodePixels_T(void); + void DecodePixels_H(void); + void DecodePixels_Planar(void); + + }; + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGB8A1.cpp b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGB8A1.cpp new file mode 100644 index 0000000000..ba2b42fb05 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGB8A1.cpp @@ -0,0 +1,1819 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +EtcBlock4x4Encoding_RGB8A1.cpp contains: + Block4x4Encoding_RGB8A1 + Block4x4Encoding_RGB8A1_Opaque + Block4x4Encoding_RGB8A1_Transparent + +These encoders are used when targetting file format RGB8A1. + +Block4x4Encoding_RGB8A1_Opaque is used when all pixels in the 4x4 block are opaque +Block4x4Encoding_RGB8A1_Transparent is used when all pixels in the 4x4 block are transparent +Block4x4Encoding_RGB8A1 is used when there is a mixture of alphas in the 4x4 block + +*/ + +#include "EtcConfig.h" +#include "EtcBlock4x4Encoding_RGB8A1.h" + +#include "EtcBlock4x4.h" +#include "EtcBlock4x4EncodingBits.h" +#include "EtcBlock4x4Encoding_RGB8.h" + +#include +#include +#include + +namespace Etc +{ + + // #################################################################################################### + // Block4x4Encoding_RGB8A1 + // #################################################################################################### + + float Block4x4Encoding_RGB8A1::s_aafCwOpaqueUnsetTable[CW_RANGES][SELECTORS] = + { + { 0.0f / 255.0f, 8.0f / 255.0f, 0.0f / 255.0f, -8.0f / 255.0f }, + { 0.0f / 255.0f, 17.0f / 255.0f, 0.0f / 255.0f, -17.0f / 255.0f }, + { 0.0f / 255.0f, 29.0f / 255.0f, 0.0f / 255.0f, -29.0f / 255.0f }, + { 0.0f / 255.0f, 42.0f / 255.0f, 0.0f / 255.0f, -42.0f / 255.0f }, + { 0.0f / 255.0f, 60.0f / 255.0f, 0.0f / 255.0f, -60.0f / 255.0f }, + { 0.0f / 255.0f, 80.0f / 255.0f, 0.0f / 255.0f, -80.0f / 255.0f }, + { 0.0f / 255.0f, 106.0f / 255.0f, 0.0f / 255.0f, -106.0f / 255.0f }, + { 0.0f / 255.0f, 183.0f / 255.0f, 0.0f / 255.0f, -183.0f / 255.0f } + }; + + // ---------------------------------------------------------------------------------------------------- + // + Block4x4Encoding_RGB8A1::Block4x4Encoding_RGB8A1(void) + { + m_pencodingbitsRGB8 = nullptr; + m_boolOpaque = false; + m_boolTransparent = false; + m_boolPunchThroughPixels = true; + + } + Block4x4Encoding_RGB8A1::~Block4x4Encoding_RGB8A1(void) {} + // ---------------------------------------------------------------------------------------------------- + // initialization prior to encoding + // a_pblockParent points to the block associated with this encoding + // a_errormetric is used to choose the best encoding + // a_pafrgbaSource points to a 4x4 block subset of the source image + // a_paucEncodingBits points to the final encoding bits + // + void Block4x4Encoding_RGB8A1::InitFromSource(Block4x4 *a_pblockParent, + ColorFloatRGBA *a_pafrgbaSource, + unsigned char *a_paucEncodingBits, + ErrorMetric a_errormetric) + { + + Block4x4Encoding_RGB8::InitFromSource(a_pblockParent, + a_pafrgbaSource, + a_paucEncodingBits, + a_errormetric); + + m_boolOpaque = a_pblockParent->GetSourceAlphaMix() == Block4x4::SourceAlphaMix::OPAQUE; + m_boolTransparent = a_pblockParent->GetSourceAlphaMix() == Block4x4::SourceAlphaMix::TRANSPARENT; + m_boolPunchThroughPixels = a_pblockParent->HasPunchThroughPixels(); + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + if (m_pafrgbaSource[uiPixel].fA >= 0.5f) + { + m_afDecodedAlphas[uiPixel] = 1.0f; + } + else + { + m_afDecodedAlphas[uiPixel] = 0.0f; + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // initialization from the encoding bits of a previous encoding + // a_pblockParent points to the block associated with this encoding + // a_errormetric is used to choose the best encoding + // a_pafrgbaSource points to a 4x4 block subset of the source image + // a_paucEncodingBits points to the final encoding bits of a previous encoding + // + void Block4x4Encoding_RGB8A1::InitFromEncodingBits(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + ErrorMetric a_errormetric) + { + + + InitFromEncodingBits_ETC1(a_pblockParent, + a_paucEncodingBits, + a_pafrgbaSource, + a_errormetric); + + m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits; + + // detect if there is a T, H or Planar mode present + int iRed1 = m_pencodingbitsRGB8->differential.red1; + int iDRed2 = m_pencodingbitsRGB8->differential.dred2; + int iRed2 = iRed1 + iDRed2; + + int iGreen1 = m_pencodingbitsRGB8->differential.green1; + int iDGreen2 = m_pencodingbitsRGB8->differential.dgreen2; + int iGreen2 = iGreen1 + iDGreen2; + + int iBlue1 = m_pencodingbitsRGB8->differential.blue1; + int iDBlue2 = m_pencodingbitsRGB8->differential.dblue2; + int iBlue2 = iBlue1 + iDBlue2; + + if (iRed2 < 0 || iRed2 > 31) + { + InitFromEncodingBits_T(); + } + else if (iGreen2 < 0 || iGreen2 > 31) + { + InitFromEncodingBits_H(); + } + else if (iBlue2 < 0 || iBlue2 > 31) + { + Block4x4Encoding_RGB8::InitFromEncodingBits_Planar(); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // initialization from the encoding bits of a previous encoding assuming the encoding is an ETC1 mode. + // if it isn't an ETC1 mode, this will be overwritten later + // + void Block4x4Encoding_RGB8A1::InitFromEncodingBits_ETC1(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + ErrorMetric a_errormetric) + { + Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource, + a_errormetric); + + m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits; + + m_mode = MODE_ETC1; + m_boolDiff = true; + m_boolFlip = m_pencodingbitsRGB8->differential.flip; + m_boolOpaque = m_pencodingbitsRGB8->differential.diff; + + int iR2 = m_pencodingbitsRGB8->differential.red1 + m_pencodingbitsRGB8->differential.dred2; + if (iR2 < 0) + { + iR2 = 0; + } + else if (iR2 > 31) + { + iR2 = 31; + } + + int iG2 = m_pencodingbitsRGB8->differential.green1 + m_pencodingbitsRGB8->differential.dgreen2; + if (iG2 < 0) + { + iG2 = 0; + } + else if (iG2 > 31) + { + iG2 = 31; + } + + int iB2 = m_pencodingbitsRGB8->differential.blue1 + m_pencodingbitsRGB8->differential.dblue2; + if (iB2 < 0) + { + iB2 = 0; + } + else if (iB2 > 31) + { + iB2 = 31; + } + + m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5(m_pencodingbitsRGB8->differential.red1, m_pencodingbitsRGB8->differential.green1, m_pencodingbitsRGB8->differential.blue1); + m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iR2, (unsigned char)iG2, (unsigned char)iB2); + + m_uiCW1 = m_pencodingbitsRGB8->differential.cw1; + m_uiCW2 = m_pencodingbitsRGB8->differential.cw2; + + Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors(); + + Decode_ETC1(); + + CalcBlockError(); + + } + + // ---------------------------------------------------------------------------------------------------- + // initialization from the encoding bits of a previous encoding if T mode is detected + // + void Block4x4Encoding_RGB8A1::InitFromEncodingBits_T(void) + { + m_mode = MODE_T; + + unsigned char ucRed1 = (unsigned char)((m_pencodingbitsRGB8->t.red1a << 2) + + m_pencodingbitsRGB8->t.red1b); + unsigned char ucGreen1 = m_pencodingbitsRGB8->t.green1; + unsigned char ucBlue1 = m_pencodingbitsRGB8->t.blue1; + + unsigned char ucRed2 = m_pencodingbitsRGB8->t.red2; + unsigned char ucGreen2 = m_pencodingbitsRGB8->t.green2; + unsigned char ucBlue2 = m_pencodingbitsRGB8->t.blue2; + + m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1); + m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2); + + m_uiCW1 = (m_pencodingbitsRGB8->t.da << 1) + m_pencodingbitsRGB8->t.db; + + Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors(); + + DecodePixels_T(); + + CalcBlockError(); + } + + // ---------------------------------------------------------------------------------------------------- + // initialization from the encoding bits of a previous encoding if H mode is detected + // + void Block4x4Encoding_RGB8A1::InitFromEncodingBits_H(void) + { + m_mode = MODE_H; + + unsigned char ucRed1 = m_pencodingbitsRGB8->h.red1; + unsigned char ucGreen1 = (unsigned char)((m_pencodingbitsRGB8->h.green1a << 1) + + m_pencodingbitsRGB8->h.green1b); + unsigned char ucBlue1 = (unsigned char)((m_pencodingbitsRGB8->h.blue1a << 3) + + (m_pencodingbitsRGB8->h.blue1b << 1) + + m_pencodingbitsRGB8->h.blue1c); + + unsigned char ucRed2 = m_pencodingbitsRGB8->h.red2; + unsigned char ucGreen2 = (unsigned char)((m_pencodingbitsRGB8->h.green2a << 1) + + m_pencodingbitsRGB8->h.green2b); + unsigned char ucBlue2 = m_pencodingbitsRGB8->h.blue2; + + m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1); + m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2); + + // used to determine the LSB of the CW + unsigned int uiRGB1 = (unsigned int)(((int)ucRed1 << 16) + ((int)ucGreen1 << 8) + (int)ucBlue1); + unsigned int uiRGB2 = (unsigned int)(((int)ucRed2 << 16) + ((int)ucGreen2 << 8) + (int)ucBlue2); + + m_uiCW1 = (m_pencodingbitsRGB8->h.da << 2) + (m_pencodingbitsRGB8->h.db << 1); + if (uiRGB1 >= uiRGB2) + { + m_uiCW1++; + } + + Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors(); + + DecodePixels_H(); + + CalcBlockError(); + } + + // ---------------------------------------------------------------------------------------------------- + // for ETC1 modes, set the decoded colors and decoded alpha based on the encoding state + // + void Block4x4Encoding_RGB8A1::Decode_ETC1(void) + { + + const unsigned int *pauiPixelOrder = m_boolFlip ? s_auiPixelOrderFlip1 : s_auiPixelOrderFlip0; + + for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS; uiPixelOrder++) + { + ColorFloatRGBA *pfrgbaCenter = uiPixelOrder < 8 ? &m_frgbaColor1 : &m_frgbaColor2; + unsigned int uiCW = uiPixelOrder < 8 ? m_uiCW1 : m_uiCW2; + + unsigned int uiPixel = pauiPixelOrder[uiPixelOrder]; + + float fDelta; + if (m_boolOpaque) + fDelta = Block4x4Encoding_ETC1::s_aafCwTable[uiCW][m_auiSelectors[uiPixel]]; + else + fDelta = s_aafCwOpaqueUnsetTable[uiCW][m_auiSelectors[uiPixel]]; + + if (m_boolOpaque == false && m_auiSelectors[uiPixel] == TRANSPARENT_SELECTOR) + { + m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(); + m_afDecodedAlphas[uiPixel] = 0.0f; + } + else + { + m_afrgbaDecodedColors[uiPixel] = (*pfrgbaCenter + fDelta).ClampRGB(); + m_afDecodedAlphas[uiPixel] = 1.0f; + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // for T mode, set the decoded colors and decoded alpha based on the encoding state + // + void Block4x4Encoding_RGB8A1::DecodePixels_T(void) + { + + float fDistance = s_afTHDistanceTable[m_uiCW1]; + ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f); + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + switch (m_auiSelectors[uiPixel]) + { + case 0: + m_afrgbaDecodedColors[uiPixel] = m_frgbaColor1; + m_afDecodedAlphas[uiPixel] = 1.0f; + break; + + case 1: + m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB(); + m_afDecodedAlphas[uiPixel] = 1.0f; + break; + + case 2: + if (m_boolOpaque == false) + { + m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(); + m_afDecodedAlphas[uiPixel] = 0.0f; + } + else + { + m_afrgbaDecodedColors[uiPixel] = m_frgbaColor2; + m_afDecodedAlphas[uiPixel] = 1.0f; + } + break; + + case 3: + m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB(); + m_afDecodedAlphas[uiPixel] = 1.0f; + break; + } + + } + + } + + // ---------------------------------------------------------------------------------------------------- + // for H mode, set the decoded colors and decoded alpha based on the encoding state + // + void Block4x4Encoding_RGB8A1::DecodePixels_H(void) + { + + float fDistance = s_afTHDistanceTable[m_uiCW1]; + ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f); + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + switch (m_auiSelectors[uiPixel]) + { + case 0: + m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 + frgbaDistance).ClampRGB(); + m_afDecodedAlphas[uiPixel] = 1.0f; + break; + + case 1: + m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 - frgbaDistance).ClampRGB(); + m_afDecodedAlphas[uiPixel] = 1.0f; + break; + + case 2: + if (m_boolOpaque == false) + { + m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(); + m_afDecodedAlphas[uiPixel] = 0.0f; + } + else + { + m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB(); + m_afDecodedAlphas[uiPixel] = 1.0f; + } + break; + + case 3: + m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB(); + m_afDecodedAlphas[uiPixel] = 1.0f; + break; + } + + } + + } + + + // ---------------------------------------------------------------------------------------------------- + // perform a single encoding iteration + // replace the encoding if a better encoding was found + // subsequent iterations generally take longer for each iteration + // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort + // + // RGB8A1 can't use individual mode + // RGB8A1 with transparent pixels can't use planar mode + // + void Block4x4Encoding_RGB8A1::PerformIteration(float a_fEffort) + { + assert(!m_boolOpaque); + assert(!m_boolTransparent); + assert(!m_boolDone); + + switch (m_uiEncodingIterations) + { + case 0: + PerformFirstIteration(); + break; + + case 1: + TryDifferential(m_boolMostLikelyFlip, 1, 0, 0); + break; + + case 2: + TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0); + if (a_fEffort <= 39.5f) + { + m_boolDone = true; + } + break; + + case 3: + Block4x4Encoding_RGB8::CalculateBaseColorsForTAndH(); + TryT(1); + TryH(1); + if (a_fEffort <= 49.5f) + { + m_boolDone = true; + } + break; + + case 4: + TryDegenerates1(); + if (a_fEffort <= 59.5f) + { + m_boolDone = true; + } + break; + + case 5: + TryDegenerates2(); + if (a_fEffort <= 69.5f) + { + m_boolDone = true; + } + break; + + case 6: + TryDegenerates3(); + if (a_fEffort <= 79.5f) + { + m_boolDone = true; + } + break; + + case 7: + TryDegenerates4(); + m_boolDone = true; + break; + + default: + assert(0); + break; + } + + m_uiEncodingIterations++; + + SetDoneIfPerfect(); + + } + + // ---------------------------------------------------------------------------------------------------- + // find best initial encoding to ensure block has a valid encoding + // + void Block4x4Encoding_RGB8A1::PerformFirstIteration(void) + { + Block4x4Encoding_ETC1::CalculateMostLikelyFlip(); + + m_fError = FLT_MAX; + + TryDifferential(m_boolMostLikelyFlip, 0, 0, 0); + SetDoneIfPerfect(); + if (m_boolDone) + { + return; + } + TryDifferential(!m_boolMostLikelyFlip, 0, 0, 0); + SetDoneIfPerfect(); + + } + + // ---------------------------------------------------------------------------------------------------- + // mostly copied from ETC1 + // differences: + // Block4x4Encoding_RGB8A1 encodingTry = *this; + // + void Block4x4Encoding_RGB8A1::TryDifferential(bool a_boolFlip, unsigned int a_uiRadius, + int a_iGrayOffset1, int a_iGrayOffset2) + { + + ColorFloatRGBA frgbaColor1; + ColorFloatRGBA frgbaColor2; + + const unsigned int *pauiPixelMapping1; + const unsigned int *pauiPixelMapping2; + + if (a_boolFlip) + { + frgbaColor1 = m_frgbaSourceAverageTop; + frgbaColor2 = m_frgbaSourceAverageBottom; + + pauiPixelMapping1 = s_auiTopPixelMapping; + pauiPixelMapping2 = s_auiBottomPixelMapping; + } + else + { + frgbaColor1 = m_frgbaSourceAverageLeft; + frgbaColor2 = m_frgbaSourceAverageRight; + + pauiPixelMapping1 = s_auiLeftPixelMapping; + pauiPixelMapping2 = s_auiRightPixelMapping; + } + + DifferentialTrys trys(frgbaColor1, frgbaColor2, pauiPixelMapping1, pauiPixelMapping2, + a_uiRadius, a_iGrayOffset1, a_iGrayOffset2); + + Block4x4Encoding_RGB8A1 encodingTry = *this; + encodingTry.m_boolFlip = a_boolFlip; + + encodingTry.TryDifferentialHalf(&trys.m_half1); + encodingTry.TryDifferentialHalf(&trys.m_half2); + + // find best halves that are within differential range + DifferentialTrys::Try *ptryBest1 = nullptr; + DifferentialTrys::Try *ptryBest2 = nullptr; + encodingTry.m_fError = FLT_MAX; + + // see if the best of each half are in differential range + int iDRed = trys.m_half2.m_ptryBest->m_iRed - trys.m_half1.m_ptryBest->m_iRed; + int iDGreen = trys.m_half2.m_ptryBest->m_iGreen - trys.m_half1.m_ptryBest->m_iGreen; + int iDBlue = trys.m_half2.m_ptryBest->m_iBlue - trys.m_half1.m_ptryBest->m_iBlue; + if (iDRed >= -4 && iDRed <= 3 && iDGreen >= -4 && iDGreen <= 3 && iDBlue >= -4 && iDBlue <= 3) + { + ptryBest1 = trys.m_half1.m_ptryBest; + ptryBest2 = trys.m_half2.m_ptryBest; + encodingTry.m_fError = trys.m_half1.m_ptryBest->m_fError + trys.m_half2.m_ptryBest->m_fError; + } + else + { + // else, find the next best halves that are in differential range + for (DifferentialTrys::Try *ptry1 = &trys.m_half1.m_atry[0]; + ptry1 < &trys.m_half1.m_atry[trys.m_half1.m_uiTrys]; + ptry1++) + { + for (DifferentialTrys::Try *ptry2 = &trys.m_half2.m_atry[0]; + ptry2 < &trys.m_half2.m_atry[trys.m_half2.m_uiTrys]; + ptry2++) + { + iDRed = ptry2->m_iRed - ptry1->m_iRed; + bool boolValidRedDelta = iDRed <= 3 && iDRed >= -4; + iDGreen = ptry2->m_iGreen - ptry1->m_iGreen; + bool boolValidGreenDelta = iDGreen <= 3 && iDGreen >= -4; + iDBlue = ptry2->m_iBlue - ptry1->m_iBlue; + bool boolValidBlueDelta = iDBlue <= 3 && iDBlue >= -4; + + if (boolValidRedDelta && boolValidGreenDelta && boolValidBlueDelta) + { + float fError = ptry1->m_fError + ptry2->m_fError; + + if (fError < encodingTry.m_fError) + { + encodingTry.m_fError = fError; + + ptryBest1 = ptry1; + ptryBest2 = ptry2; + } + } + + } + } + assert(encodingTry.m_fError < FLT_MAX); + assert(ptryBest1 != nullptr); + assert(ptryBest2 != nullptr); + } + + if (encodingTry.m_fError < m_fError) + { + m_mode = MODE_ETC1; + m_boolDiff = true; + m_boolFlip = encodingTry.m_boolFlip; + m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest1->m_iRed, (unsigned char)ptryBest1->m_iGreen, (unsigned char)ptryBest1->m_iBlue); + m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest2->m_iRed, (unsigned char)ptryBest2->m_iGreen, (unsigned char)ptryBest2->m_iBlue); + m_uiCW1 = ptryBest1->m_uiCW; + m_uiCW2 = ptryBest2->m_uiCW; + + m_fError = 0.0f; + for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS / 2; uiPixelOrder++) + { + unsigned int uiPixel1 = pauiPixelMapping1[uiPixelOrder]; + unsigned int uiPixel2 = pauiPixelMapping2[uiPixelOrder]; + + unsigned int uiSelector1 = ptryBest1->m_auiSelectors[uiPixelOrder]; + unsigned int uiSelector2 = ptryBest2->m_auiSelectors[uiPixelOrder]; + + m_auiSelectors[uiPixel1] = uiSelector1; + m_auiSelectors[uiPixel2] = ptryBest2->m_auiSelectors[uiPixelOrder]; + + if (uiSelector1 == TRANSPARENT_SELECTOR) + { + m_afrgbaDecodedColors[uiPixel1] = ColorFloatRGBA(); + m_afDecodedAlphas[uiPixel1] = 0.0f; + } + else + { + float fDeltaRGB1 = s_aafCwOpaqueUnsetTable[m_uiCW1][uiSelector1]; + m_afrgbaDecodedColors[uiPixel1] = (m_frgbaColor1 + fDeltaRGB1).ClampRGB(); + m_afDecodedAlphas[uiPixel1] = 1.0f; + } + + if (uiSelector2 == TRANSPARENT_SELECTOR) + { + m_afrgbaDecodedColors[uiPixel2] = ColorFloatRGBA(); + m_afDecodedAlphas[uiPixel2] = 0.0f; + } + else + { + float fDeltaRGB2 = s_aafCwOpaqueUnsetTable[m_uiCW2][uiSelector2]; + m_afrgbaDecodedColors[uiPixel2] = (m_frgbaColor2 + fDeltaRGB2).ClampRGB(); + m_afDecodedAlphas[uiPixel2] = 1.0f; + } + + float fDeltaA1 = m_afDecodedAlphas[uiPixel1] - m_pafrgbaSource[uiPixel1].fA; + m_fError += fDeltaA1 * fDeltaA1; + float fDeltaA2 = m_afDecodedAlphas[uiPixel2] - m_pafrgbaSource[uiPixel2].fA; + m_fError += fDeltaA2 * fDeltaA2; + } + + m_fError1 = ptryBest1->m_fError; + m_fError2 = ptryBest2->m_fError; + m_boolSeverelyBentDifferentialColors = trys.m_boolSeverelyBentColors; + m_fError = m_fError1 + m_fError2; + + // sanity check + { + int iRed1 = m_frgbaColor1.IntRed(31.0f); + int iGreen1 = m_frgbaColor1.IntGreen(31.0f); + int iBlue1 = m_frgbaColor1.IntBlue(31.0f); + + int iRed2 = m_frgbaColor2.IntRed(31.0f); + int iGreen2 = m_frgbaColor2.IntGreen(31.0f); + int iBlue2 = m_frgbaColor2.IntBlue(31.0f); + + iDRed = iRed2 - iRed1; + iDGreen = iGreen2 - iGreen1; + iDBlue = iBlue2 - iBlue1; + + assert(iDRed >= -4 && iDRed < 4); + assert(iDGreen >= -4 && iDGreen < 4); + assert(iDBlue >= -4 && iDBlue < 4); + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // mostly copied from ETC1 + // differences: + // uses s_aafCwOpaqueUnsetTable + // color for selector set to 0,0,0,0 + // + void Block4x4Encoding_RGB8A1::TryDifferentialHalf(DifferentialTrys::Half *a_phalf) + { + + a_phalf->m_ptryBest = nullptr; + float fBestTryError = FLT_MAX; + + a_phalf->m_uiTrys = 0; + for (int iRed = a_phalf->m_iRed - (int)a_phalf->m_uiRadius; + iRed <= a_phalf->m_iRed + (int)a_phalf->m_uiRadius; + iRed++) + { + assert(iRed >= 0 && iRed <= 31); + + for (int iGreen = a_phalf->m_iGreen - (int)a_phalf->m_uiRadius; + iGreen <= a_phalf->m_iGreen + (int)a_phalf->m_uiRadius; + iGreen++) + { + assert(iGreen >= 0 && iGreen <= 31); + + for (int iBlue = a_phalf->m_iBlue - (int)a_phalf->m_uiRadius; + iBlue <= a_phalf->m_iBlue + (int)a_phalf->m_uiRadius; + iBlue++) + { + assert(iBlue >= 0 && iBlue <= 31); + + DifferentialTrys::Try *ptry = &a_phalf->m_atry[a_phalf->m_uiTrys]; + assert(ptry < &a_phalf->m_atry[DifferentialTrys::Half::MAX_TRYS]); + + ptry->m_iRed = iRed; + ptry->m_iGreen = iGreen; + ptry->m_iBlue = iBlue; + ptry->m_fError = FLT_MAX; + ColorFloatRGBA frgbaColor = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iRed, (unsigned char)iGreen, (unsigned char)iBlue); + + // try each CW + for (unsigned int uiCW = 0; uiCW < CW_RANGES; uiCW++) + { + unsigned int auiPixelSelectors[PIXELS / 2]; + ColorFloatRGBA afrgbaDecodedColors[PIXELS / 2]; + float afPixelErrors[PIXELS / 2] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, + FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; + + // pre-compute decoded pixels for each selector + ColorFloatRGBA afrgbaSelectors[SELECTORS]; + assert(SELECTORS == 4); + afrgbaSelectors[0] = (frgbaColor + s_aafCwOpaqueUnsetTable[uiCW][0]).ClampRGB(); + afrgbaSelectors[1] = (frgbaColor + s_aafCwOpaqueUnsetTable[uiCW][1]).ClampRGB(); + afrgbaSelectors[2] = ColorFloatRGBA(); + afrgbaSelectors[3] = (frgbaColor + s_aafCwOpaqueUnsetTable[uiCW][3]).ClampRGB(); + + for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) + { + ColorFloatRGBA *pfrgbaSourcePixel = &m_pafrgbaSource[a_phalf->m_pauiPixelMapping[uiPixel]]; + ColorFloatRGBA frgbaDecodedPixel; + + for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++) + { + if (pfrgbaSourcePixel->fA < 0.5f) + { + uiSelector = TRANSPARENT_SELECTOR; + } + else if (uiSelector == TRANSPARENT_SELECTOR) + { + continue; + } + + frgbaDecodedPixel = afrgbaSelectors[uiSelector]; + + float fPixelError; + + fPixelError = CalcPixelError(frgbaDecodedPixel, m_afDecodedAlphas[a_phalf->m_pauiPixelMapping[uiPixel]], + *pfrgbaSourcePixel); + + if (fPixelError < afPixelErrors[uiPixel]) + { + auiPixelSelectors[uiPixel] = uiSelector; + afrgbaDecodedColors[uiPixel] = frgbaDecodedPixel; + afPixelErrors[uiPixel] = fPixelError; + } + + if (uiSelector == TRANSPARENT_SELECTOR) + { + break; + } + } + } + + // add up all pixel errors + float fCWError = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) + { + fCWError += afPixelErrors[uiPixel]; + } + + // if best CW so far + if (fCWError < ptry->m_fError) + { + ptry->m_uiCW = uiCW; + for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) + { + ptry->m_auiSelectors[uiPixel] = auiPixelSelectors[uiPixel]; + } + ptry->m_fError = fCWError; + } + + } + + if (ptry->m_fError < fBestTryError) + { + a_phalf->m_ptryBest = ptry; + fBestTryError = ptry->m_fError; + } + + assert(ptry->m_fError < FLT_MAX); + + a_phalf->m_uiTrys++; + } + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // try encoding in T mode + // save this encoding if it improves the error + // + // since pixels that use base color1 don't use the distance table, color1 and color2 can be twiddled independently + // better encoding can be found if TWIDDLE_RADIUS is set to 2, but it will be much slower + // + void Block4x4Encoding_RGB8A1::TryT(unsigned int a_uiRadius) + { + Block4x4Encoding_RGB8A1 encodingTry = *this; + + // init "try" + { + encodingTry.m_mode = MODE_T; + encodingTry.m_boolDiff = true; + encodingTry.m_boolFlip = false; + encodingTry.m_fError = FLT_MAX; + } + + int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f); + int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f); + int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f); + + int iMinRed1 = iColor1Red - (int)a_uiRadius; + if (iMinRed1 < 0) + { + iMinRed1 = 0; + } + int iMaxRed1 = iColor1Red + (int)a_uiRadius; + if (iMaxRed1 > 15) + { + iMinRed1 = 15; + } + + int iMinGreen1 = iColor1Green - (int)a_uiRadius; + if (iMinGreen1 < 0) + { + iMinGreen1 = 0; + } + int iMaxGreen1 = iColor1Green + (int)a_uiRadius; + if (iMaxGreen1 > 15) + { + iMinGreen1 = 15; + } + + int iMinBlue1 = iColor1Blue - (int)a_uiRadius; + if (iMinBlue1 < 0) + { + iMinBlue1 = 0; + } + int iMaxBlue1 = iColor1Blue + (int)a_uiRadius; + if (iMaxBlue1 > 15) + { + iMinBlue1 = 15; + } + + int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f); + int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f); + int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f); + + int iMinRed2 = iColor2Red - (int)a_uiRadius; + if (iMinRed2 < 0) + { + iMinRed2 = 0; + } + int iMaxRed2 = iColor2Red + (int)a_uiRadius; + if (iMaxRed2 > 15) + { + iMinRed2 = 15; + } + + int iMinGreen2 = iColor2Green - (int)a_uiRadius; + if (iMinGreen2 < 0) + { + iMinGreen2 = 0; + } + int iMaxGreen2 = iColor2Green + (int)a_uiRadius; + if (iMaxGreen2 > 15) + { + iMinGreen2 = 15; + } + + int iMinBlue2 = iColor2Blue - (int)a_uiRadius; + if (iMinBlue2 < 0) + { + iMinBlue2 = 0; + } + int iMaxBlue2 = iColor2Blue + (int)a_uiRadius; + if (iMaxBlue2 > 15) + { + iMinBlue2 = 15; + } + + for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++) + { + encodingTry.m_uiCW1 = uiDistance; + + // twiddle m_frgbaOriginalColor2_TAndH + // twiddle color2 first, since it affects 3 selectors, while color1 only affects one selector + // + for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++) + { + for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++) + { + for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++) + { + for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++) + { + if (uiBaseColorSwaps == 0) + { + encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH; + encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2); + } + else + { + encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2); + encodingTry.m_frgbaColor2 = m_frgbaOriginalColor1_TAndH; + } + + encodingTry.TryT_BestSelectorCombination(); + + if (encodingTry.m_fError < m_fError) + { + m_mode = encodingTry.m_mode; + m_boolDiff = encodingTry.m_boolDiff; + m_boolFlip = encodingTry.m_boolFlip; + + m_frgbaColor1 = encodingTry.m_frgbaColor1; + m_frgbaColor2 = encodingTry.m_frgbaColor2; + m_uiCW1 = encodingTry.m_uiCW1; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel]; + m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; + } + + m_fError = encodingTry.m_fError; + } + } + } + } + } + + // twiddle m_frgbaOriginalColor1_TAndH + for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++) + { + for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++) + { + for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++) + { + for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++) + { + if (uiBaseColorSwaps == 0) + { + encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1); + encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH; + } + else + { + encodingTry.m_frgbaColor1 = m_frgbaOriginalColor2_TAndH; + encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1); + } + + encodingTry.TryT_BestSelectorCombination(); + + if (encodingTry.m_fError < m_fError) + { + m_mode = encodingTry.m_mode; + m_boolDiff = encodingTry.m_boolDiff; + m_boolFlip = encodingTry.m_boolFlip; + + m_frgbaColor1 = encodingTry.m_frgbaColor1; + m_frgbaColor2 = encodingTry.m_frgbaColor2; + m_uiCW1 = encodingTry.m_uiCW1; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel]; + m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; + } + + m_fError = encodingTry.m_fError; + } + } + } + } + } + + } + + } + + // ---------------------------------------------------------------------------------------------------- + // find best selector combination for TryT + // called on an encodingTry + // + void Block4x4Encoding_RGB8A1::TryT_BestSelectorCombination(void) + { + + float fDistance = s_afTHDistanceTable[m_uiCW1]; + + unsigned int auiBestPixelSelectors[PIXELS]; + float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, + FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; + ColorFloatRGBA afrgbaBestDecodedPixels[PIXELS]; + ColorFloatRGBA afrgbaDecodedPixel[SELECTORS]; + + assert(SELECTORS == 4); + afrgbaDecodedPixel[0] = m_frgbaColor1; + afrgbaDecodedPixel[1] = (m_frgbaColor2 + fDistance).ClampRGB(); + afrgbaDecodedPixel[2] = ColorFloatRGBA(); + afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB(); + + // try each selector + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + unsigned int uiMinSelector = 0; + unsigned int uiMaxSelector = SELECTORS - 1; + + if (m_pafrgbaSource[uiPixel].fA < 0.5f) + { + uiMinSelector = 2; + uiMaxSelector = 2; + } + + for (unsigned int uiSelector = uiMinSelector; uiSelector <= uiMaxSelector; uiSelector++) + { + float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel], + m_pafrgbaSource[uiPixel]); + + if (fPixelError < afBestPixelErrors[uiPixel]) + { + afBestPixelErrors[uiPixel] = fPixelError; + auiBestPixelSelectors[uiPixel] = uiSelector; + afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector]; + } + } + } + + + // add up all of the pixel errors + float fBlockError = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + fBlockError += afBestPixelErrors[uiPixel]; + } + + if (fBlockError < m_fError) + { + m_fError = fBlockError; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel]; + m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel]; + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // try encoding in H mode + // save this encoding if it improves the error + // + // since all pixels use the distance table, color1 and color2 can NOT be twiddled independently + // TWIDDLE_RADIUS of 2 is WAY too slow + // + void Block4x4Encoding_RGB8A1::TryH(unsigned int a_uiRadius) + { + Block4x4Encoding_RGB8A1 encodingTry = *this; + + // init "try" + { + encodingTry.m_mode = MODE_H; + encodingTry.m_boolDiff = true; + encodingTry.m_boolFlip = false; + encodingTry.m_fError = FLT_MAX; + } + + int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f); + int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f); + int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f); + + int iMinRed1 = iColor1Red - (int)a_uiRadius; + if (iMinRed1 < 0) + { + iMinRed1 = 0; + } + int iMaxRed1 = iColor1Red + (int)a_uiRadius; + if (iMaxRed1 > 15) + { + iMinRed1 = 15; + } + + int iMinGreen1 = iColor1Green - (int)a_uiRadius; + if (iMinGreen1 < 0) + { + iMinGreen1 = 0; + } + int iMaxGreen1 = iColor1Green + (int)a_uiRadius; + if (iMaxGreen1 > 15) + { + iMinGreen1 = 15; + } + + int iMinBlue1 = iColor1Blue - (int)a_uiRadius; + if (iMinBlue1 < 0) + { + iMinBlue1 = 0; + } + int iMaxBlue1 = iColor1Blue + (int)a_uiRadius; + if (iMaxBlue1 > 15) + { + iMinBlue1 = 15; + } + + int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f); + int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f); + int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f); + + int iMinRed2 = iColor2Red - (int)a_uiRadius; + if (iMinRed2 < 0) + { + iMinRed2 = 0; + } + int iMaxRed2 = iColor2Red + (int)a_uiRadius; + if (iMaxRed2 > 15) + { + iMinRed2 = 15; + } + + int iMinGreen2 = iColor2Green - (int)a_uiRadius; + if (iMinGreen2 < 0) + { + iMinGreen2 = 0; + } + int iMaxGreen2 = iColor2Green + (int)a_uiRadius; + if (iMaxGreen2 > 15) + { + iMinGreen2 = 15; + } + + int iMinBlue2 = iColor2Blue - (int)a_uiRadius; + if (iMinBlue2 < 0) + { + iMinBlue2 = 0; + } + int iMaxBlue2 = iColor2Blue + (int)a_uiRadius; + if (iMaxBlue2 > 15) + { + iMinBlue2 = 15; + } + + for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++) + { + encodingTry.m_uiCW1 = uiDistance; + + // twiddle m_frgbaOriginalColor1_TAndH + for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++) + { + for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++) + { + for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++) + { + encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1); + encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH; + + // if color1 == color2, H encoding issues can pop up, so abort + if (iRed1 == iColor2Red && iGreen1 == iColor2Green && iBlue1 == iColor2Blue) + { + continue; + } + + encodingTry.TryH_BestSelectorCombination(); + + if (encodingTry.m_fError < m_fError) + { + m_mode = encodingTry.m_mode; + m_boolDiff = encodingTry.m_boolDiff; + m_boolFlip = encodingTry.m_boolFlip; + + m_frgbaColor1 = encodingTry.m_frgbaColor1; + m_frgbaColor2 = encodingTry.m_frgbaColor2; + m_uiCW1 = encodingTry.m_uiCW1; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel]; + m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; + } + + m_fError = encodingTry.m_fError; + } + } + } + } + + // twiddle m_frgbaOriginalColor2_TAndH + for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++) + { + for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++) + { + for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++) + { + encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH; + encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2); + + // if color1 == color2, H encoding issues can pop up, so abort + if (iRed2 == iColor1Red && iGreen2 == iColor1Green && iBlue2 == iColor1Blue) + { + continue; + } + + encodingTry.TryH_BestSelectorCombination(); + + if (encodingTry.m_fError < m_fError) + { + m_mode = encodingTry.m_mode; + m_boolDiff = encodingTry.m_boolDiff; + m_boolFlip = encodingTry.m_boolFlip; + + m_frgbaColor1 = encodingTry.m_frgbaColor1; + m_frgbaColor2 = encodingTry.m_frgbaColor2; + m_uiCW1 = encodingTry.m_uiCW1; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel]; + m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; + } + + m_fError = encodingTry.m_fError; + } + } + } + } + + } + + } + + // ---------------------------------------------------------------------------------------------------- + // find best selector combination for TryH + // called on an encodingTry + // + void Block4x4Encoding_RGB8A1::TryH_BestSelectorCombination(void) + { + + // abort if colors and CW will pose an encoding problem + { + unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(255.0f); + unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(255.0f); + unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(255.0f); + unsigned int uiColorValue1 = (uiRed1 << 16) + (uiGreen1 << 8) + uiBlue1; + + unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(255.0f); + unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(255.0f); + unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(255.0f); + unsigned int uiColorValue2 = (uiRed2 << 16) + (uiGreen2 << 8) + uiBlue2; + + unsigned int uiCWLsb = m_uiCW1 & 1; + + if ((uiColorValue1 >= (uiColorValue2 & uiCWLsb)) == 0 || + (uiColorValue1 < (uiColorValue2 & uiCWLsb)) == 1) + { + return; + } + } + + float fDistance = s_afTHDistanceTable[m_uiCW1]; + + unsigned int auiBestPixelSelectors[PIXELS]; + float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, + FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; + ColorFloatRGBA afrgbaBestDecodedPixels[PIXELS]; + ColorFloatRGBA afrgbaDecodedPixel[SELECTORS]; + + assert(SELECTORS == 4); + afrgbaDecodedPixel[0] = (m_frgbaColor1 + fDistance).ClampRGB(); + afrgbaDecodedPixel[1] = (m_frgbaColor1 - fDistance).ClampRGB(); + afrgbaDecodedPixel[2] = ColorFloatRGBA();; + afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB(); + + + // try each selector + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + unsigned int uiMinSelector = 0; + unsigned int uiMaxSelector = SELECTORS - 1; + + if (m_pafrgbaSource[uiPixel].fA < 0.5f) + { + uiMinSelector = 2; + uiMaxSelector = 2; + } + + for (unsigned int uiSelector = uiMinSelector; uiSelector <= uiMaxSelector; uiSelector++) + { + float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel], + m_pafrgbaSource[uiPixel]); + + if (fPixelError < afBestPixelErrors[uiPixel]) + { + afBestPixelErrors[uiPixel] = fPixelError; + auiBestPixelSelectors[uiPixel] = uiSelector; + afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector]; + } + } + } + + + // add up all of the pixel errors + float fBlockError = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + fBlockError += afBestPixelErrors[uiPixel]; + } + + if (fBlockError < m_fError) + { + m_fError = fBlockError; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel]; + m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel]; + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // try version 1 of the degenerate search + // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings + // each subsequent version of the degenerate search uses more basecolor movement and is less likely to + // be successfull + // + void Block4x4Encoding_RGB8A1::TryDegenerates1(void) + { + + TryDifferential(m_boolMostLikelyFlip, 1, -2, 0); + TryDifferential(m_boolMostLikelyFlip, 1, 2, 0); + TryDifferential(m_boolMostLikelyFlip, 1, 0, 2); + TryDifferential(m_boolMostLikelyFlip, 1, 0, -2); + + } + + // ---------------------------------------------------------------------------------------------------- + // try version 2 of the degenerate search + // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings + // each subsequent version of the degenerate search uses more basecolor movement and is less likely to + // be successfull + // + void Block4x4Encoding_RGB8A1::TryDegenerates2(void) + { + + TryDifferential(!m_boolMostLikelyFlip, 1, -2, 0); + TryDifferential(!m_boolMostLikelyFlip, 1, 2, 0); + TryDifferential(!m_boolMostLikelyFlip, 1, 0, 2); + TryDifferential(!m_boolMostLikelyFlip, 1, 0, -2); + + } + + // ---------------------------------------------------------------------------------------------------- + // try version 3 of the degenerate search + // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings + // each subsequent version of the degenerate search uses more basecolor movement and is less likely to + // be successfull + // + void Block4x4Encoding_RGB8A1::TryDegenerates3(void) + { + + TryDifferential(m_boolMostLikelyFlip, 1, -2, -2); + TryDifferential(m_boolMostLikelyFlip, 1, -2, 2); + TryDifferential(m_boolMostLikelyFlip, 1, 2, -2); + TryDifferential(m_boolMostLikelyFlip, 1, 2, 2); + + } + + // ---------------------------------------------------------------------------------------------------- + // try version 4 of the degenerate search + // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings + // each subsequent version of the degenerate search uses more basecolor movement and is less likely to + // be successfull + // + void Block4x4Encoding_RGB8A1::TryDegenerates4(void) + { + + TryDifferential(m_boolMostLikelyFlip, 1, -4, 0); + TryDifferential(m_boolMostLikelyFlip, 1, 4, 0); + TryDifferential(m_boolMostLikelyFlip, 1, 0, 4); + TryDifferential(m_boolMostLikelyFlip, 1, 0, -4); + + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state + // + void Block4x4Encoding_RGB8A1::SetEncodingBits(void) + { + switch (m_mode) + { + case MODE_ETC1: + SetEncodingBits_ETC1(); + break; + + case MODE_T: + SetEncodingBits_T(); + break; + + case MODE_H: + SetEncodingBits_H(); + break; + + case MODE_PLANAR: + Block4x4Encoding_RGB8::SetEncodingBits_Planar(); + break; + + default: + assert(false); + } + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state if ETC1 mode + // + void Block4x4Encoding_RGB8A1::SetEncodingBits_ETC1(void) + { + + // there is no individual mode in RGB8A1 + assert(m_boolDiff); + + int iRed1 = m_frgbaColor1.IntRed(31.0f); + int iGreen1 = m_frgbaColor1.IntGreen(31.0f); + int iBlue1 = m_frgbaColor1.IntBlue(31.0f); + + int iRed2 = m_frgbaColor2.IntRed(31.0f); + int iGreen2 = m_frgbaColor2.IntGreen(31.0f); + int iBlue2 = m_frgbaColor2.IntBlue(31.0f); + + int iDRed2 = iRed2 - iRed1; + int iDGreen2 = iGreen2 - iGreen1; + int iDBlue2 = iBlue2 - iBlue1; + + assert(iDRed2 >= -4 && iDRed2 < 4); + assert(iDGreen2 >= -4 && iDGreen2 < 4); + assert(iDBlue2 >= -4 && iDBlue2 < 4); + + m_pencodingbitsRGB8->differential.red1 = iRed1; + m_pencodingbitsRGB8->differential.green1 = iGreen1; + m_pencodingbitsRGB8->differential.blue1 = iBlue1; + + m_pencodingbitsRGB8->differential.dred2 = iDRed2; + m_pencodingbitsRGB8->differential.dgreen2 = iDGreen2; + m_pencodingbitsRGB8->differential.dblue2 = iDBlue2; + + m_pencodingbitsRGB8->individual.cw1 = m_uiCW1; + m_pencodingbitsRGB8->individual.cw2 = m_uiCW2; + + SetEncodingBits_Selectors(); + + // in RGB8A1 encoding bits, opaque replaces differential + m_pencodingbitsRGB8->differential.diff = !m_boolPunchThroughPixels; + + m_pencodingbitsRGB8->individual.flip = m_boolFlip; + + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state if T mode + // + void Block4x4Encoding_RGB8A1::SetEncodingBits_T(void) + { + static const bool SANITY_CHECK = true; + + assert(m_mode == MODE_T); + assert(m_boolDiff == true); + + unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f); + unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f); + unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f); + + unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f); + unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f); + unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f); + + m_pencodingbitsRGB8->t.red1a = uiRed1 >> 2; + m_pencodingbitsRGB8->t.red1b = uiRed1; + m_pencodingbitsRGB8->t.green1 = uiGreen1; + m_pencodingbitsRGB8->t.blue1 = uiBlue1; + + m_pencodingbitsRGB8->t.red2 = uiRed2; + m_pencodingbitsRGB8->t.green2 = uiGreen2; + m_pencodingbitsRGB8->t.blue2 = uiBlue2; + + m_pencodingbitsRGB8->t.da = m_uiCW1 >> 1; + m_pencodingbitsRGB8->t.db = m_uiCW1; + + // in RGB8A1 encoding bits, opaque replaces differential + m_pencodingbitsRGB8->differential.diff = !m_boolPunchThroughPixels; + + Block4x4Encoding_ETC1::SetEncodingBits_Selectors(); + + // create an invalid R differential to trigger T mode + m_pencodingbitsRGB8->t.detect1 = 0; + m_pencodingbitsRGB8->t.detect2 = 0; + int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; + if (iRed2 >= 4) + { + m_pencodingbitsRGB8->t.detect1 = 7; + m_pencodingbitsRGB8->t.detect2 = 0; + } + else + { + m_pencodingbitsRGB8->t.detect1 = 0; + m_pencodingbitsRGB8->t.detect2 = 1; + } + + if (SANITY_CHECK) + { + iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; + + // make sure red overflows + assert(iRed2 < 0 || iRed2 > 31); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state if H mode + // + // colors and selectors may need to swap in order to generate lsb of distance index + // + void Block4x4Encoding_RGB8A1::SetEncodingBits_H(void) + { + static const bool SANITY_CHECK = true; + + assert(m_mode == MODE_H); + assert(m_boolDiff == true); + + unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f); + unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f); + unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f); + + unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f); + unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f); + unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f); + + unsigned int uiColor1 = (uiRed1 << 16) + (uiGreen1 << 8) + uiBlue1; + unsigned int uiColor2 = (uiRed2 << 16) + (uiGreen2 << 8) + uiBlue2; + + bool boolOddDistance = m_uiCW1 & 1; + bool boolSwapColors = (uiColor1 < uiColor2) ^ !boolOddDistance; + + if (boolSwapColors) + { + m_pencodingbitsRGB8->h.red1 = uiRed2; + m_pencodingbitsRGB8->h.green1a = uiGreen2 >> 1; + m_pencodingbitsRGB8->h.green1b = uiGreen2; + m_pencodingbitsRGB8->h.blue1a = uiBlue2 >> 3; + m_pencodingbitsRGB8->h.blue1b = uiBlue2 >> 1; + m_pencodingbitsRGB8->h.blue1c = uiBlue2; + + m_pencodingbitsRGB8->h.red2 = uiRed1; + m_pencodingbitsRGB8->h.green2a = uiGreen1 >> 1; + m_pencodingbitsRGB8->h.green2b = uiGreen1; + m_pencodingbitsRGB8->h.blue2 = uiBlue1; + + m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2; + m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1; + } + else + { + m_pencodingbitsRGB8->h.red1 = uiRed1; + m_pencodingbitsRGB8->h.green1a = uiGreen1 >> 1; + m_pencodingbitsRGB8->h.green1b = uiGreen1; + m_pencodingbitsRGB8->h.blue1a = uiBlue1 >> 3; + m_pencodingbitsRGB8->h.blue1b = uiBlue1 >> 1; + m_pencodingbitsRGB8->h.blue1c = uiBlue1; + + m_pencodingbitsRGB8->h.red2 = uiRed2; + m_pencodingbitsRGB8->h.green2a = uiGreen2 >> 1; + m_pencodingbitsRGB8->h.green2b = uiGreen2; + m_pencodingbitsRGB8->h.blue2 = uiBlue2; + + m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2; + m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1; + } + + // in RGB8A1 encoding bits, opaque replaces differential + m_pencodingbitsRGB8->differential.diff = !m_boolPunchThroughPixels; + + Block4x4Encoding_ETC1::SetEncodingBits_Selectors(); + + if (boolSwapColors) + { + m_pencodingbitsRGB8->h.selectors ^= 0x0000FFFF; + } + + // create an invalid R differential to trigger T mode + m_pencodingbitsRGB8->h.detect1 = 0; + m_pencodingbitsRGB8->h.detect2 = 0; + m_pencodingbitsRGB8->h.detect3 = 0; + int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; + int iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2; + if (iRed2 < 0 || iRed2 > 31) + { + m_pencodingbitsRGB8->h.detect1 = 1; + } + if (iGreen2 >= 4) + { + m_pencodingbitsRGB8->h.detect2 = 7; + m_pencodingbitsRGB8->h.detect3 = 0; + } + else + { + m_pencodingbitsRGB8->h.detect2 = 0; + m_pencodingbitsRGB8->h.detect3 = 1; + } + + if (SANITY_CHECK) + { + iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; + iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2; + + // make sure red doesn't overflow and green does + assert(iRed2 >= 0 && iRed2 <= 31); + assert(iGreen2 < 0 || iGreen2 > 31); + } + + } + + // #################################################################################################### + // Block4x4Encoding_RGB8A1_Opaque + // #################################################################################################### + + // ---------------------------------------------------------------------------------------------------- + // perform a single encoding iteration + // replace the encoding if a better encoding was found + // subsequent iterations generally take longer for each iteration + // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort + // + void Block4x4Encoding_RGB8A1_Opaque::PerformIteration(float a_fEffort) + { + assert(!m_boolPunchThroughPixels); + assert(!m_boolTransparent); + assert(!m_boolDone); + + switch (m_uiEncodingIterations) + { + case 0: + PerformFirstIteration(); + break; + + case 1: + Block4x4Encoding_ETC1::TryDifferential(m_boolMostLikelyFlip, 1, 0, 0); + break; + + case 2: + Block4x4Encoding_ETC1::TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0); + break; + + case 3: + Block4x4Encoding_RGB8::TryPlanar(1); + break; + + case 4: + Block4x4Encoding_RGB8::TryTAndH(1); + if (a_fEffort <= 49.5f) + { + m_boolDone = true; + } + break; + + case 5: + Block4x4Encoding_ETC1::TryDegenerates1(); + if (a_fEffort <= 59.5f) + { + m_boolDone = true; + } + break; + + case 6: + Block4x4Encoding_ETC1::TryDegenerates2(); + if (a_fEffort <= 69.5f) + { + m_boolDone = true; + } + break; + + case 7: + Block4x4Encoding_ETC1::TryDegenerates3(); + if (a_fEffort <= 79.5f) + { + m_boolDone = true; + } + break; + + case 8: + Block4x4Encoding_ETC1::TryDegenerates4(); + m_boolDone = true; + break; + + default: + assert(0); + break; + } + + m_uiEncodingIterations++; + SetDoneIfPerfect(); + } + + // ---------------------------------------------------------------------------------------------------- + // find best initial encoding to ensure block has a valid encoding + // + void Block4x4Encoding_RGB8A1_Opaque::PerformFirstIteration(void) + { + + // set decoded alphas + // calculate alpha error + m_fError = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_afDecodedAlphas[uiPixel] = 1.0f; + + float fDeltaA = 1.0f - m_pafrgbaSource[uiPixel].fA; + m_fError += fDeltaA * fDeltaA; + } + + CalculateMostLikelyFlip(); + + m_fError = FLT_MAX; + + Block4x4Encoding_ETC1::TryDifferential(m_boolMostLikelyFlip, 0, 0, 0); + SetDoneIfPerfect(); + if (m_boolDone) + { + return; + } + Block4x4Encoding_ETC1::TryDifferential(!m_boolMostLikelyFlip, 0, 0, 0); + SetDoneIfPerfect(); + if (m_boolDone) + { + return; + } + Block4x4Encoding_RGB8::TryPlanar(0); + SetDoneIfPerfect(); + if (m_boolDone) + { + return; + } + Block4x4Encoding_RGB8::TryTAndH(0); + SetDoneIfPerfect(); + } + + // #################################################################################################### + // Block4x4Encoding_RGB8A1_Transparent + // #################################################################################################### + + // ---------------------------------------------------------------------------------------------------- + // perform a single encoding iteration + // replace the encoding if a better encoding was found + // subsequent iterations generally take longer for each iteration + // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort + // + void Block4x4Encoding_RGB8A1_Transparent::PerformIteration(float ) + { + assert(!m_boolOpaque); + assert(m_boolTransparent); + assert(!m_boolDone); + assert(m_uiEncodingIterations == 0); + + m_mode = MODE_ETC1; + m_boolDiff = true; + m_boolFlip = false; + + m_uiCW1 = 0; + m_uiCW2 = 0; + + m_frgbaColor1 = ColorFloatRGBA(); + m_frgbaColor2 = ColorFloatRGBA(); + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiSelectors[uiPixel] = TRANSPARENT_SELECTOR; + + m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(); + m_afDecodedAlphas[uiPixel] = 0.0f; + } + + CalcBlockError(); + + m_boolDone = true; + m_uiEncodingIterations++; + + } + + // ---------------------------------------------------------------------------------------------------- + // +} diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGB8A1.h b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGB8A1.h new file mode 100644 index 0000000000..ff26e462f8 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGB8A1.h @@ -0,0 +1,129 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcBlock4x4Encoding_RGB8.h" +#include "EtcErrorMetric.h" +#include "EtcBlock4x4EncodingBits.h" + +namespace Etc +{ + + // ################################################################################ + // Block4x4Encoding_RGB8A1 + // RGB8A1 if not completely opaque or transparent + // ################################################################################ + + class Block4x4Encoding_RGB8A1 : public Block4x4Encoding_RGB8 + { + public: + + static const unsigned int TRANSPARENT_SELECTOR = 2; + + Block4x4Encoding_RGB8A1(void); + virtual ~Block4x4Encoding_RGB8A1(void); + + virtual void InitFromSource(Block4x4 *a_pblockParent, + ColorFloatRGBA *a_pafrgbaSource, + unsigned char *a_paucEncodingBits, + ErrorMetric a_errormetric); + + virtual void InitFromEncodingBits(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + ErrorMetric a_errormetric); + + virtual void PerformIteration(float a_fEffort); + + virtual void SetEncodingBits(void); + + void InitFromEncodingBits_ETC1(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + ErrorMetric a_errormetric); + + void InitFromEncodingBits_T(void); + void InitFromEncodingBits_H(void); + + void PerformFirstIteration(void); + + void Decode_ETC1(void); + void DecodePixels_T(void); + void DecodePixels_H(void); + void SetEncodingBits_ETC1(void); + void SetEncodingBits_T(void); + void SetEncodingBits_H(void); + + protected: + + bool m_boolOpaque; // all source pixels have alpha >= 0.5 + bool m_boolTransparent; // all source pixels have alpha < 0.5 + bool m_boolPunchThroughPixels; // some source pixels have alpha < 0.5 + + static float s_aafCwOpaqueUnsetTable[CW_RANGES][SELECTORS]; + + private: + + void TryDifferential(bool a_boolFlip, unsigned int a_uiRadius, + int a_iGrayOffset1, int a_iGrayOffset2); + void TryDifferentialHalf(DifferentialTrys::Half *a_phalf); + + void TryT(unsigned int a_uiRadius); + void TryT_BestSelectorCombination(void); + void TryH(unsigned int a_uiRadius); + void TryH_BestSelectorCombination(void); + + void TryDegenerates1(void); + void TryDegenerates2(void); + void TryDegenerates3(void); + void TryDegenerates4(void); + + }; + + // ################################################################################ + // Block4x4Encoding_RGB8A1_Opaque + // RGB8A1 if all pixels have alpha==1 + // ################################################################################ + + class Block4x4Encoding_RGB8A1_Opaque : public Block4x4Encoding_RGB8A1 + { + public: + + virtual void PerformIteration(float a_fEffort); + + void PerformFirstIteration(void); + + private: + + }; + + // ################################################################################ + // Block4x4Encoding_RGB8A1_Transparent + // RGB8A1 if all pixels have alpha==0 + // ################################################################################ + + class Block4x4Encoding_RGB8A1_Transparent : public Block4x4Encoding_RGB8A1 + { + public: + + virtual void PerformIteration(float a_fEffort); + + private: + + }; + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGBA8.cpp b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGBA8.cpp new file mode 100644 index 0000000000..600c7ab405 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGBA8.cpp @@ -0,0 +1,474 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +EtcBlock4x4Encoding_RGBA8.cpp contains: + Block4x4Encoding_RGBA8 + Block4x4Encoding_RGBA8_Opaque + Block4x4Encoding_RGBA8_Transparent + +These encoders are used when targetting file format RGBA8. + +Block4x4Encoding_RGBA8_Opaque is used when all pixels in the 4x4 block are opaque +Block4x4Encoding_RGBA8_Transparent is used when all pixels in the 4x4 block are transparent +Block4x4Encoding_RGBA8 is used when there is a mixture of alphas in the 4x4 block + +*/ + +#include "EtcConfig.h" +#include "EtcBlock4x4Encoding_RGBA8.h" + +#include "EtcBlock4x4EncodingBits.h" +#include "EtcBlock4x4.h" + +#include +#include +#include +#include +#include + +namespace Etc +{ + + // #################################################################################################### + // Block4x4Encoding_RGBA8 + // #################################################################################################### + + float Block4x4Encoding_RGBA8::s_aafModifierTable[MODIFIER_TABLE_ENTRYS][ALPHA_SELECTORS] + { + { -3.0f / 255.0f, -6.0f / 255.0f, -9.0f / 255.0f, -15.0f / 255.0f, 2.0f / 255.0f, 5.0f / 255.0f, 8.0f / 255.0f, 14.0f / 255.0f }, + { -3.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, -13.0f / 255.0f, 2.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f, 12.0f / 255.0f }, + { -2.0f / 255.0f, -5.0f / 255.0f, -8.0f / 255.0f, -13.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 12.0f / 255.0f }, + { -2.0f / 255.0f, -4.0f / 255.0f, -6.0f / 255.0f, -13.0f / 255.0f, 1.0f / 255.0f, 3.0f / 255.0f, 5.0f / 255.0f, 12.0f / 255.0f }, + + { -3.0f / 255.0f, -6.0f / 255.0f, -8.0f / 255.0f, -12.0f / 255.0f, 2.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 11.0f / 255.0f }, + { -3.0f / 255.0f, -7.0f / 255.0f, -9.0f / 255.0f, -11.0f / 255.0f, 2.0f / 255.0f, 6.0f / 255.0f, 8.0f / 255.0f, 10.0f / 255.0f }, + { -4.0f / 255.0f, -7.0f / 255.0f, -8.0f / 255.0f, -11.0f / 255.0f, 3.0f / 255.0f, 6.0f / 255.0f, 7.0f / 255.0f, 10.0f / 255.0f }, + { -3.0f / 255.0f, -5.0f / 255.0f, -8.0f / 255.0f, -11.0f / 255.0f, 2.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 10.0f / 255.0f }, + + { -2.0f / 255.0f, -6.0f / 255.0f, -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 9.0f / 255.0f }, + { -2.0f / 255.0f, -5.0f / 255.0f, -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 9.0f / 255.0f }, + { -2.0f / 255.0f, -4.0f / 255.0f, -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 3.0f / 255.0f, 7.0f / 255.0f, 9.0f / 255.0f }, + { -2.0f / 255.0f, -5.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f }, + + { -3.0f / 255.0f, -4.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, 2.0f / 255.0f, 3.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f }, + { -1.0f / 255.0f, -2.0f / 255.0f, -3.0f / 255.0f, -10.0f / 255.0f, 0.0f / 255.0f, 1.0f / 255.0f, 2.0f / 255.0f, 9.0f / 255.0f }, + { -4.0f / 255.0f, -6.0f / 255.0f, -8.0f / 255.0f, -9.0f / 255.0f, 3.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 8.0f / 255.0f }, + { -3.0f / 255.0f, -5.0f / 255.0f, -7.0f / 255.0f, -9.0f / 255.0f, 2.0f / 255.0f, 4.0f / 255.0f, 6.0f / 255.0f, 8.0f / 255.0f } + }; + + // ---------------------------------------------------------------------------------------------------- + // + Block4x4Encoding_RGBA8::Block4x4Encoding_RGBA8(void) + { + + m_pencodingbitsA8 = nullptr; + + } + Block4x4Encoding_RGBA8::~Block4x4Encoding_RGBA8(void) {} + // ---------------------------------------------------------------------------------------------------- + // initialization prior to encoding + // a_pblockParent points to the block associated with this encoding + // a_errormetric is used to choose the best encoding + // a_pafrgbaSource points to a 4x4 block subset of the source image + // a_paucEncodingBits points to the final encoding bits + // + void Block4x4Encoding_RGBA8::InitFromSource(Block4x4 *a_pblockParent, + ColorFloatRGBA *a_pafrgbaSource, + unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric) + { + Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric); + + m_pencodingbitsA8 = (Block4x4EncodingBits_A8 *)a_paucEncodingBits; + m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)(a_paucEncodingBits + sizeof(Block4x4EncodingBits_A8)); + + } + + // ---------------------------------------------------------------------------------------------------- + // initialization from the encoding bits of a previous encoding + // a_pblockParent points to the block associated with this encoding + // a_errormetric is used to choose the best encoding + // a_pafrgbaSource points to a 4x4 block subset of the source image + // a_paucEncodingBits points to the final encoding bits of a previous encoding + // + void Block4x4Encoding_RGBA8::InitFromEncodingBits(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + ErrorMetric a_errormetric) + { + + m_pencodingbitsA8 = (Block4x4EncodingBits_A8 *)a_paucEncodingBits; + m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)(a_paucEncodingBits + sizeof(Block4x4EncodingBits_A8)); + + // init RGB portion + Block4x4Encoding_RGB8::InitFromEncodingBits(a_pblockParent, + (unsigned char *) m_pencodingbitsRGB8, + a_pafrgbaSource, + a_errormetric); + + // init A8 portion + // has to be done after InitFromEncodingBits() + { + m_fBase = m_pencodingbitsA8->data.base / 255.0f; + m_fMultiplier = (float)m_pencodingbitsA8->data.multiplier; + m_uiModifierTableIndex = m_pencodingbitsA8->data.table; + + unsigned long long int ulliSelectorBits = 0; + ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors0 << 40; + ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors1 << 32; + ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors2 << 24; + ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors3 << 16; + ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors4 << 8; + ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors5; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + unsigned int uiShift = 45 - (3 * uiPixel); + m_auiAlphaSelectors[uiPixel] = (ulliSelectorBits >> uiShift) & (ALPHA_SELECTORS - 1); + } + + // decode the alphas + // calc alpha error + m_fError = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_afDecodedAlphas[uiPixel] = DecodePixelAlpha(m_fBase, m_fMultiplier, + m_uiModifierTableIndex, + m_auiAlphaSelectors[uiPixel]); + + float fDeltaAlpha = m_afDecodedAlphas[uiPixel] - m_pafrgbaSource[uiPixel].fA; + m_fError += fDeltaAlpha * fDeltaAlpha; + } + } + + // redo error calc to include alpha + CalcBlockError(); + + } + + // ---------------------------------------------------------------------------------------------------- + // perform a single encoding iteration + // replace the encoding if a better encoding was found + // subsequent iterations generally take longer for each iteration + // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort + // + // similar to Block4x4Encoding_RGB8_Base::Encode_RGB8(), but with alpha added + // + void Block4x4Encoding_RGBA8::PerformIteration(float a_fEffort) + { + assert(!m_boolDone); + + if (m_uiEncodingIterations == 0) + { + if (a_fEffort < 24.9f) + { + CalculateA8(0.0f); + } + else if (a_fEffort < 49.9f) + { + CalculateA8(1.0f); + } + else + { + CalculateA8(2.0f); + } + } + + Block4x4Encoding_RGB8::PerformIteration(a_fEffort); + + } + + // ---------------------------------------------------------------------------------------------------- + // find the best combination of base alpga, multiplier and selectors + // + // a_fRadius limits the range of base alpha to try + // + void Block4x4Encoding_RGBA8::CalculateA8(float a_fRadius) + { + + // find min/max alpha + float fMinAlpha = 1.0f; + float fMaxAlpha = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + float fAlpha = m_pafrgbaSource[uiPixel].fA; + + // ignore border pixels + if (isnan(fAlpha)) + { + continue; + } + + if (fAlpha < fMinAlpha) + { + fMinAlpha = fAlpha; + } + if (fAlpha > fMaxAlpha) + { + fMaxAlpha = fAlpha; + } + } + assert(fMinAlpha <= fMaxAlpha); + + float fAlphaRange = fMaxAlpha - fMinAlpha; + + // try each modifier table entry + m_fError = FLT_MAX; // artificially high value + for (unsigned int uiTableEntry = 0; uiTableEntry < MODIFIER_TABLE_ENTRYS; uiTableEntry++) + { + static const unsigned int MIN_VALUE_SELECTOR = 3; + static const unsigned int MAX_VALUE_SELECTOR = 7; + + float fTableEntryCenter = -s_aafModifierTable[uiTableEntry][MIN_VALUE_SELECTOR]; + + float fTableEntryRange = s_aafModifierTable[uiTableEntry][MAX_VALUE_SELECTOR] - + s_aafModifierTable[uiTableEntry][MIN_VALUE_SELECTOR]; + + float fCenterRatio = fTableEntryCenter / fTableEntryRange; + + float fCenter = fMinAlpha + fCenterRatio*fAlphaRange; + fCenter = roundf(255.0f * fCenter) / 255.0f; + + float fMinBase = fCenter - (a_fRadius / 255.0f); + if (fMinBase < 0.0f) + { + fMinBase = 0.0f; + } + + float fMaxBase = fCenter + (a_fRadius / 255.0f); + if (fMaxBase > 1.0f) + { + fMaxBase = 1.0f; + } + + for (float fBase = fMinBase; fBase <= fMaxBase; fBase += (0.999999f / 255.0f)) + { + + float fRangeMultiplier = roundf(fAlphaRange / fTableEntryRange); + + float fMinMultiplier = fRangeMultiplier - a_fRadius; + if (fMinMultiplier < 1.0f) + { + fMinMultiplier = 1.0f; + } + else if (fMinMultiplier > 15.0f) + { + fMinMultiplier = 15.0f; + } + + float fMaxMultiplier = fRangeMultiplier + a_fRadius; + if (fMaxMultiplier < 1.0f) + { + fMaxMultiplier = 1.0f; + } + else if (fMaxMultiplier > 15.0f) + { + fMaxMultiplier = 15.0f; + } + + for (float fMultiplier = fMinMultiplier; fMultiplier <= fMaxMultiplier; fMultiplier += 1.0f) + { + // find best selector for each pixel + unsigned int auiBestSelectors[PIXELS]; + float afBestAlphaError[PIXELS]; + float afBestDecodedAlphas[PIXELS]; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + float fBestPixelAlphaError = FLT_MAX; + for (unsigned int uiSelector = 0; uiSelector < ALPHA_SELECTORS; uiSelector++) + { + float fDecodedAlpha = DecodePixelAlpha(fBase, fMultiplier, uiTableEntry, uiSelector); + + // border pixels (NAN) should have zero error + float fPixelDeltaAlpha = isnan(m_pafrgbaSource[uiPixel].fA) ? + 0.0f : + fDecodedAlpha - m_pafrgbaSource[uiPixel].fA; + + float fPixelAlphaError = fPixelDeltaAlpha * fPixelDeltaAlpha; + + if (fPixelAlphaError < fBestPixelAlphaError) + { + fBestPixelAlphaError = fPixelAlphaError; + auiBestSelectors[uiPixel] = uiSelector; + afBestAlphaError[uiPixel] = fBestPixelAlphaError; + afBestDecodedAlphas[uiPixel] = fDecodedAlpha; + } + } + } + + float fBlockError = 0.0f; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + fBlockError += afBestAlphaError[uiPixel]; + } + + if (fBlockError < m_fError) + { + m_fError = fBlockError; + + m_fBase = fBase; + m_fMultiplier = fMultiplier; + m_uiModifierTableIndex = uiTableEntry; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_auiAlphaSelectors[uiPixel] = auiBestSelectors[uiPixel]; + m_afDecodedAlphas[uiPixel] = afBestDecodedAlphas[uiPixel]; + } + } + } + } + + } + + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state + // + void Block4x4Encoding_RGBA8::SetEncodingBits(void) + { + + // set the RGB8 portion + Block4x4Encoding_RGB8::SetEncodingBits(); + + // set the A8 portion + { + m_pencodingbitsA8->data.base = (unsigned char)roundf(255.0f * m_fBase); + m_pencodingbitsA8->data.table = m_uiModifierTableIndex; + m_pencodingbitsA8->data.multiplier = (unsigned char)roundf(m_fMultiplier); + + unsigned long long int ulliSelectorBits = 0; + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + unsigned int uiShift = 45 - (3 * uiPixel); + ulliSelectorBits |= ((unsigned long long int)m_auiAlphaSelectors[uiPixel]) << uiShift; + } + + m_pencodingbitsA8->data.selectors0 = ulliSelectorBits >> 40; + m_pencodingbitsA8->data.selectors1 = ulliSelectorBits >> 32; + m_pencodingbitsA8->data.selectors2 = ulliSelectorBits >> 24; + m_pencodingbitsA8->data.selectors3 = ulliSelectorBits >> 16; + m_pencodingbitsA8->data.selectors4 = ulliSelectorBits >> 8; + m_pencodingbitsA8->data.selectors5 = ulliSelectorBits; + } + + } + + // #################################################################################################### + // Block4x4Encoding_RGBA8_Opaque + // #################################################################################################### + + // ---------------------------------------------------------------------------------------------------- + // perform a single encoding iteration + // replace the encoding if a better encoding was found + // subsequent iterations generally take longer for each iteration + // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort + // + void Block4x4Encoding_RGBA8_Opaque::PerformIteration(float a_fEffort) + { + assert(!m_boolDone); + + if (m_uiEncodingIterations == 0) + { + m_fError = 0.0f; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_afDecodedAlphas[uiPixel] = 1.0f; + } + } + + Block4x4Encoding_RGB8::PerformIteration(a_fEffort); + + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state + // + void Block4x4Encoding_RGBA8_Opaque::SetEncodingBits(void) + { + + // set the RGB8 portion + Block4x4Encoding_RGB8::SetEncodingBits(); + + // set the A8 portion + m_pencodingbitsA8->data.base = 255; + m_pencodingbitsA8->data.table = 15; + m_pencodingbitsA8->data.multiplier = 15; + m_pencodingbitsA8->data.selectors0 = 0xFF; + m_pencodingbitsA8->data.selectors1 = 0xFF; + m_pencodingbitsA8->data.selectors2 = 0xFF; + m_pencodingbitsA8->data.selectors3 = 0xFF; + m_pencodingbitsA8->data.selectors4 = 0xFF; + m_pencodingbitsA8->data.selectors5 = 0xFF; + + } + + // #################################################################################################### + // Block4x4Encoding_RGBA8_Transparent + // #################################################################################################### + + // ---------------------------------------------------------------------------------------------------- + // perform a single encoding iteration + // replace the encoding if a better encoding was found + // subsequent iterations generally take longer for each iteration + // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort + // + void Block4x4Encoding_RGBA8_Transparent::PerformIteration(float ) + { + assert(!m_boolDone); + assert(m_uiEncodingIterations == 0); + + m_mode = MODE_ETC1; + m_boolDiff = true; + m_boolFlip = false; + + for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) + { + m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(); + m_afDecodedAlphas[uiPixel] = 0.0f; + } + + m_fError = 0.0f; + + m_boolDone = true; + m_uiEncodingIterations++; + + } + + // ---------------------------------------------------------------------------------------------------- + // set the encoding bits based on encoding state + // + void Block4x4Encoding_RGBA8_Transparent::SetEncodingBits(void) + { + + Block4x4Encoding_RGB8::SetEncodingBits(); + + // set the A8 portion + m_pencodingbitsA8->data.base = 0; + m_pencodingbitsA8->data.table = 0; + m_pencodingbitsA8->data.multiplier = 1; + m_pencodingbitsA8->data.selectors0 = 0; + m_pencodingbitsA8->data.selectors1 = 0; + m_pencodingbitsA8->data.selectors2 = 0; + m_pencodingbitsA8->data.selectors3 = 0; + m_pencodingbitsA8->data.selectors4 = 0; + m_pencodingbitsA8->data.selectors5 = 0; + + } + + // ---------------------------------------------------------------------------------------------------- + // +} diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGBA8.h b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGBA8.h new file mode 100644 index 0000000000..5765d36b90 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcBlock4x4Encoding_RGBA8.h @@ -0,0 +1,121 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcBlock4x4Encoding_RGB8.h" + +namespace Etc +{ + class Block4x4EncodingBits_A8; + + // ################################################################################ + // Block4x4Encoding_RGBA8 + // RGBA8 if not completely opaque or transparent + // ################################################################################ + + class Block4x4Encoding_RGBA8 : public Block4x4Encoding_RGB8 + { + public: + + Block4x4Encoding_RGBA8(void); + virtual ~Block4x4Encoding_RGBA8(void); + + virtual void InitFromSource(Block4x4 *a_pblockParent, + ColorFloatRGBA *a_pafrgbaSource, + unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric); + + virtual void InitFromEncodingBits(Block4x4 *a_pblockParent, + unsigned char *a_paucEncodingBits, + ColorFloatRGBA *a_pafrgbaSource, + ErrorMetric a_errormetric); + + virtual void PerformIteration(float a_fEffort); + + virtual void SetEncodingBits(void); + + protected: + + static const unsigned int MODIFIER_TABLE_ENTRYS = 16; + static const unsigned int ALPHA_SELECTOR_BITS = 3; + static const unsigned int ALPHA_SELECTORS = 1 << ALPHA_SELECTOR_BITS; + + static float s_aafModifierTable[MODIFIER_TABLE_ENTRYS][ALPHA_SELECTORS]; + + void CalculateA8(float a_fRadius); + + Block4x4EncodingBits_A8 *m_pencodingbitsA8; // A8 portion of Block4x4EncodingBits_RGBA8 + + float m_fBase; + float m_fMultiplier; + unsigned int m_uiModifierTableIndex; + unsigned int m_auiAlphaSelectors[PIXELS]; + + private: + + inline float DecodePixelAlpha(float a_fBase, float a_fMultiplier, + unsigned int a_uiTableIndex, unsigned int a_uiSelector) + { + float fPixelAlpha = a_fBase + + a_fMultiplier*s_aafModifierTable[a_uiTableIndex][a_uiSelector]; + if (fPixelAlpha < 0.0f) + { + fPixelAlpha = 0.0f; + } + else if (fPixelAlpha > 1.0f) + { + fPixelAlpha = 1.0f; + } + + return fPixelAlpha; + } + + }; + + // ################################################################################ + // Block4x4Encoding_RGBA8_Opaque + // RGBA8 if all pixels have alpha==1 + // ################################################################################ + + class Block4x4Encoding_RGBA8_Opaque : public Block4x4Encoding_RGBA8 + { + public: + + virtual void PerformIteration(float a_fEffort); + + virtual void SetEncodingBits(void); + + }; + + // ################################################################################ + // Block4x4Encoding_RGBA8_Transparent + // RGBA8 if all pixels have alpha==0 + // ################################################################################ + + class Block4x4Encoding_RGBA8_Transparent : public Block4x4Encoding_RGBA8 + { + public: + + virtual void PerformIteration(float a_fEffort); + + virtual void SetEncodingBits(void); + + }; + + // ---------------------------------------------------------------------------------------------------- + // + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcDifferentialTrys.cpp b/deps/etc2comp/EtcLib/EtcCodec/EtcDifferentialTrys.cpp new file mode 100644 index 0000000000..ef4cd103d9 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcDifferentialTrys.cpp @@ -0,0 +1,173 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +EtcDifferentialTrys.cpp + +Gathers the results of the various encoding trys for both halves of a 4x4 block for Differential mode + +*/ + +#include "EtcConfig.h" +#include "EtcDifferentialTrys.h" + +#include + +namespace Etc +{ + + // ---------------------------------------------------------------------------------------------------- + // construct a list of trys (encoding attempts) + // + // a_frgbaColor1 is the basecolor for the first half + // a_frgbaColor2 is the basecolor for the second half + // a_pauiPixelMapping1 is the pixel order for the first half + // a_pauiPixelMapping2 is the pixel order for the second half + // a_uiRadius is the amount to vary the base colors + // + DifferentialTrys::DifferentialTrys(ColorFloatRGBA a_frgbaColor1, ColorFloatRGBA a_frgbaColor2, + const unsigned int *a_pauiPixelMapping1, + const unsigned int *a_pauiPixelMapping2, + unsigned int a_uiRadius, + int a_iGrayOffset1, int a_iGrayOffset2) + { + assert(a_uiRadius <= MAX_RADIUS); + + m_boolSeverelyBentColors = false; + + ColorFloatRGBA frgbaQuantizedColor1 = a_frgbaColor1.QuantizeR5G5B5(); + ColorFloatRGBA frgbaQuantizedColor2 = a_frgbaColor2.QuantizeR5G5B5(); + + // quantize base colors + // ensure that trys with a_uiRadius don't overflow + int iRed1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntRed(31.0f)+a_iGrayOffset1, a_uiRadius); + int iGreen1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntGreen(31.0f) + a_iGrayOffset1, a_uiRadius); + int iBlue1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntBlue(31.0f) + a_iGrayOffset1, a_uiRadius); + int iRed2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntRed(31.0f) + a_iGrayOffset2, a_uiRadius); + int iGreen2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntGreen(31.0f) + a_iGrayOffset2, a_uiRadius); + int iBlue2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntBlue(31.0f) + a_iGrayOffset2, a_uiRadius); + + int iDeltaRed = iRed2 - iRed1; + int iDeltaGreen = iGreen2 - iGreen1; + int iDeltaBlue = iBlue2 - iBlue1; + + // make sure components are within range + { + if (iDeltaRed > 3) + { + if (iDeltaRed > 7) + { + m_boolSeverelyBentColors = true; + } + + iRed1 += (iDeltaRed - 3) / 2; + iRed2 = iRed1 + 3; + iDeltaRed = 3; + } + else if (iDeltaRed < -4) + { + if (iDeltaRed < -8) + { + m_boolSeverelyBentColors = true; + } + + iRed1 += (iDeltaRed + 4) / 2; + iRed2 = iRed1 - 4; + iDeltaRed = -4; + } + assert(iRed1 >= (signed)(0 + a_uiRadius) && iRed1 <= (signed)(31 - a_uiRadius)); + assert(iRed2 >= (signed)(0 + a_uiRadius) && iRed2 <= (signed)(31 - a_uiRadius)); + assert(iDeltaRed >= -4 && iDeltaRed <= 3); + + if (iDeltaGreen > 3) + { + if (iDeltaGreen > 7) + { + m_boolSeverelyBentColors = true; + } + + iGreen1 += (iDeltaGreen - 3) / 2; + iGreen2 = iGreen1 + 3; + iDeltaGreen = 3; + } + else if (iDeltaGreen < -4) + { + if (iDeltaGreen < -8) + { + m_boolSeverelyBentColors = true; + } + + iGreen1 += (iDeltaGreen + 4) / 2; + iGreen2 = iGreen1 - 4; + iDeltaGreen = -4; + } + assert(iGreen1 >= (signed)(0 + a_uiRadius) && iGreen1 <= (signed)(31 - a_uiRadius)); + assert(iGreen2 >= (signed)(0 + a_uiRadius) && iGreen2 <= (signed)(31 - a_uiRadius)); + assert(iDeltaGreen >= -4 && iDeltaGreen <= 3); + + if (iDeltaBlue > 3) + { + if (iDeltaBlue > 7) + { + m_boolSeverelyBentColors = true; + } + + iBlue1 += (iDeltaBlue - 3) / 2; + iBlue2 = iBlue1 + 3; + iDeltaBlue = 3; + } + else if (iDeltaBlue < -4) + { + if (iDeltaBlue < -8) + { + m_boolSeverelyBentColors = true; + } + + iBlue1 += (iDeltaBlue + 4) / 2; + iBlue2 = iBlue1 - 4; + iDeltaBlue = -4; + } + assert(iBlue1 >= (signed)(0+a_uiRadius) && iBlue1 <= (signed)(31 - a_uiRadius)); + assert(iBlue2 >= (signed)(0 + a_uiRadius) && iBlue2 <= (signed)(31 - a_uiRadius)); + assert(iDeltaBlue >= -4 && iDeltaBlue <= 3); + } + + m_half1.Init(iRed1, iGreen1, iBlue1, a_pauiPixelMapping1, a_uiRadius); + m_half2.Init(iRed2, iGreen2, iBlue2, a_pauiPixelMapping2, a_uiRadius); + + } + + // ---------------------------------------------------------------------------------------------------- + // + void DifferentialTrys::Half::Init(int a_iRed, int a_iGreen, int a_iBlue, + const unsigned int *a_pauiPixelMapping, unsigned int a_uiRadius) + { + + m_iRed = a_iRed; + m_iGreen = a_iGreen; + m_iBlue = a_iBlue; + + m_pauiPixelMapping = a_pauiPixelMapping; + m_uiRadius = a_uiRadius; + + m_uiTrys = 0; + + } + + // ---------------------------------------------------------------------------------------------------- + // + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcDifferentialTrys.h b/deps/etc2comp/EtcLib/EtcCodec/EtcDifferentialTrys.h new file mode 100644 index 0000000000..71860908ff --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcDifferentialTrys.h @@ -0,0 +1,97 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcColorFloatRGBA.h" + +namespace Etc +{ + + class DifferentialTrys + { + public: + + static const unsigned int MAX_RADIUS = 2; + + DifferentialTrys(ColorFloatRGBA a_frgbaColor1, + ColorFloatRGBA a_frgbaColor2, + const unsigned int *a_pauiPixelMapping1, + const unsigned int *a_pauiPixelMapping2, + unsigned int a_uiRadius, + int a_iGrayOffset1, int a_iGrayOffset2); + + inline static int MoveAwayFromEdge(int a_i, int a_iDistance) + { + if (a_i < (0+ a_iDistance)) + { + return (0 + a_iDistance); + } + else if (a_i > (31- a_iDistance)) + { + return (31 - a_iDistance); + } + + return a_i; + } + + class Try + { + public : + static const unsigned int SELECTORS = 8; // per half + + int m_iRed; + int m_iGreen; + int m_iBlue; + unsigned int m_uiCW; + unsigned int m_auiSelectors[SELECTORS]; + float m_fError; + }; + + class Half + { + public: + + static const unsigned int MAX_TRYS = 125; + + void Init(int a_iRed, int a_iGreen, int a_iBlue, + const unsigned int *a_pauiPixelMapping, + unsigned int a_uiRadius); + + // center of trys + int m_iRed; + int m_iGreen; + int m_iBlue; + + const unsigned int *m_pauiPixelMapping; + unsigned int m_uiRadius; + + unsigned int m_uiTrys; + Try m_atry[MAX_TRYS]; + + Try *m_ptryBest; + }; + + Half m_half1; + Half m_half2; + + bool m_boolSeverelyBentColors; + }; + + // ---------------------------------------------------------------------------------------------------- + // + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcErrorMetric.h b/deps/etc2comp/EtcLib/EtcCodec/EtcErrorMetric.h new file mode 100644 index 0000000000..df4dcab4fb --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcErrorMetric.h @@ -0,0 +1,54 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace Etc +{ + + enum ErrorMetric + { + RGBA, + RGBX, + REC709, + NUMERIC, + NORMALXYZ, + // + ERROR_METRICS, + // + BT709 = REC709 + }; + + inline const char *ErrorMetricToString(ErrorMetric errorMetric) + { + switch (errorMetric) + { + case RGBA: + return "RGBA"; + case RGBX: + return "RGBX"; + case REC709: + return "REC709"; + case NUMERIC: + return "NUMERIC"; + case NORMALXYZ: + return "NORMALXYZ"; + case ERROR_METRICS: + default: + return "UNKNOWN"; + } + } +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcIndividualTrys.cpp b/deps/etc2comp/EtcLib/EtcCodec/EtcIndividualTrys.cpp new file mode 100644 index 0000000000..56ff4c65ec --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcIndividualTrys.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +EtcIndividualTrys.cpp + +Gathers the results of the various encoding trys for both halves of a 4x4 block for Individual mode + +*/ + +#include "EtcConfig.h" +#include "EtcIndividualTrys.h" + +#include + +namespace Etc +{ + + // ---------------------------------------------------------------------------------------------------- + // construct a list of trys (encoding attempts) + // + // a_frgbaColor1 is the basecolor for the first half + // a_frgbaColor2 is the basecolor for the second half + // a_pauiPixelMapping1 is the pixel order for the first half + // a_pauiPixelMapping2 is the pixel order for the second half + // a_uiRadius is the amount to vary the base colors + // + IndividualTrys::IndividualTrys(ColorFloatRGBA a_frgbaColor1, ColorFloatRGBA a_frgbaColor2, + const unsigned int *a_pauiPixelMapping1, + const unsigned int *a_pauiPixelMapping2, + unsigned int a_uiRadius) + { + assert(a_uiRadius <= MAX_RADIUS); + + ColorFloatRGBA frgbaQuantizedColor1 = a_frgbaColor1.QuantizeR4G4B4(); + ColorFloatRGBA frgbaQuantizedColor2 = a_frgbaColor2.QuantizeR4G4B4(); + + // quantize base colors + // ensure that trys with a_uiRadius don't overflow + int iRed1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntRed(15.0f), a_uiRadius); + int iGreen1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntGreen(15.0f), a_uiRadius); + int iBlue1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntBlue(15.0f), a_uiRadius); + int iRed2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntRed(15.0f), a_uiRadius); + int iGreen2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntGreen(15.0f), a_uiRadius); + int iBlue2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntBlue(15.0f), a_uiRadius); + + m_half1.Init(iRed1, iGreen1, iBlue1, a_pauiPixelMapping1, a_uiRadius); + m_half2.Init(iRed2, iGreen2, iBlue2, a_pauiPixelMapping2, a_uiRadius); + + } + + // ---------------------------------------------------------------------------------------------------- + // + void IndividualTrys::Half::Init(int a_iRed, int a_iGreen, int a_iBlue, + const unsigned int *a_pauiPixelMapping, unsigned int a_uiRadius) + { + + m_iRed = a_iRed; + m_iGreen = a_iGreen; + m_iBlue = a_iBlue; + + m_pauiPixelMapping = a_pauiPixelMapping; + m_uiRadius = a_uiRadius; + + m_uiTrys = 0; + + } + + // ---------------------------------------------------------------------------------------------------- + // + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcIndividualTrys.h b/deps/etc2comp/EtcLib/EtcCodec/EtcIndividualTrys.h new file mode 100644 index 0000000000..5fb12fbcf4 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcIndividualTrys.h @@ -0,0 +1,95 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcColorFloatRGBA.h" + +namespace Etc +{ + + class IndividualTrys + { + public: + + static const unsigned int MAX_RADIUS = 1; + + IndividualTrys(ColorFloatRGBA a_frgbaColor1, + ColorFloatRGBA a_frgbaColor2, + const unsigned int *a_pauiPixelMapping1, + const unsigned int *a_pauiPixelMapping2, + unsigned int a_uiRadius); + + inline static int MoveAwayFromEdge(int a_i, int a_iDistance) + { + if (a_i < (0+ a_iDistance)) + { + return (0 + a_iDistance); + } + else if (a_i > (15- a_iDistance)) + { + return (15 - a_iDistance); + } + + return a_i; + } + + class Try + { + public : + static const unsigned int SELECTORS = 8; // per half + + int m_iRed; + int m_iGreen; + int m_iBlue; + unsigned int m_uiCW; + unsigned int m_auiSelectors[SELECTORS]; + float m_fError; + }; + + class Half + { + public: + + static const unsigned int MAX_TRYS = 27; + + void Init(int a_iRed, int a_iGreen, int a_iBlue, + const unsigned int *a_pauiPixelMapping, + unsigned int a_uiRadius); + + // center of trys + int m_iRed; + int m_iGreen; + int m_iBlue; + + const unsigned int *m_pauiPixelMapping; + unsigned int m_uiRadius; + + unsigned int m_uiTrys; + Try m_atry[MAX_TRYS]; + + Try *m_ptryBest; + }; + + Half m_half1; + Half m_half2; + + }; + + // ---------------------------------------------------------------------------------------------------- + // + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcSortedBlockList.cpp b/deps/etc2comp/EtcLib/EtcCodec/EtcSortedBlockList.cpp new file mode 100644 index 0000000000..bfa6b7b3fa --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcSortedBlockList.cpp @@ -0,0 +1,228 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +EtcSortedBlockList.cpp + +SortedBlockList is a list of 4x4 blocks that can be used by the "effort" system to prioritize +the encoding of the 4x4 blocks. + +The sorting is done with buckets, where each bucket is an indication of how much error each 4x4 block has + +*/ + +#include "EtcConfig.h" +#include "EtcSortedBlockList.h" + +#include "EtcBlock4x4.h" + +#include +#include +#include + +namespace Etc +{ + + // ---------------------------------------------------------------------------------------------------- + // construct an empty list + // + // allocate enough memory to add all of the image's 4x4 blocks later + // allocate enough buckets to sort the blocks + // + SortedBlockList::SortedBlockList(unsigned int a_uiImageBlocks, unsigned int a_uiBuckets) + { + m_uiImageBlocks = a_uiImageBlocks; + m_iBuckets = (int)a_uiBuckets; + + m_uiAddedBlocks = 0; + m_uiSortedBlocks = 0; + m_palinkPool = new Link[m_uiImageBlocks]; + m_pabucket = new Bucket[m_iBuckets]; + m_fMaxError = 0.0f; + + InitBuckets(); + + } + + // ---------------------------------------------------------------------------------------------------- + // + SortedBlockList::~SortedBlockList(void) + { + delete[] m_palinkPool; + delete[] m_pabucket; + } + + // ---------------------------------------------------------------------------------------------------- + // add a 4x4 block to the list + // the 4x4 block will be sorted later + // + void SortedBlockList::AddBlock(Block4x4 *a_pblock) + { + assert(m_uiAddedBlocks < m_uiImageBlocks); + Link *plink = &m_palinkPool[m_uiAddedBlocks++]; + plink->Init(a_pblock); + } + + // ---------------------------------------------------------------------------------------------------- + // sort all of the 4x4 blocks that have been added to the list + // + // first, determine the maximum error, then assign an error range to each bucket + // next, determine which bucket each 4x4 block belongs to based on the 4x4 block's error + // add the 4x4 block to the appropriate bucket + // lastly, walk thru the buckets and add each bucket to a sorted linked list + // + // the resultant sorting is an approximate sorting from most to least error + // + void SortedBlockList::Sort(void) + { + assert(m_uiAddedBlocks == m_uiImageBlocks); + InitBuckets(); + + // find max block error + m_fMaxError = -1.0f; + + for (unsigned int uiLink = 0; uiLink < m_uiAddedBlocks; uiLink++) + { + Link *plinkBlock = &m_palinkPool[uiLink]; + + float fBlockError = plinkBlock->GetBlock()->GetError(); + if (fBlockError > m_fMaxError) + { + m_fMaxError = fBlockError; + } + } + // prevent divide by zero or divide by negative + if (m_fMaxError <= 0.0f) + { + m_fMaxError = 1.0f; + } + //used for debugging + //int numDone = 0; + // put all of the blocks with unfinished encodings into the appropriate bucket + m_uiSortedBlocks = 0; + for (unsigned int uiLink = 0; uiLink < m_uiAddedBlocks; uiLink++) + { + Link *plinkBlock = &m_palinkPool[uiLink]; + + // if the encoding is done, don't add it to the list + if (plinkBlock->GetBlock()->GetEncoding()->IsDone()) + { + //numDone++; + continue; + } + + // calculate the appropriate sort bucket + float fBlockError = plinkBlock->GetBlock()->GetError(); + int iBucket = (int) floorf(m_iBuckets * fBlockError / m_fMaxError); + // clamp to bucket index + iBucket = iBucket < 0 ? 0 : iBucket >= m_iBuckets ? m_iBuckets - 1 : iBucket; + + // add block to bucket + { + Bucket *pbucket = &m_pabucket[iBucket]; + if (pbucket->plinkLast) + { + pbucket->plinkLast->SetNext(plinkBlock); + pbucket->plinkLast = plinkBlock; + } + else + { + pbucket->plinkFirst = pbucket->plinkLast = plinkBlock; + } + plinkBlock->SetNext(nullptr); + } + + m_uiSortedBlocks++; + + if (0) + { + printf("%u: e=%.3f\n", uiLink, fBlockError); + Print(); + printf("\n\n\n"); + } + } + //printf("num blocks already done: %d\n",numDone); + //link the blocks together across buckets + m_plinkFirst = nullptr; + m_plinkLast = nullptr; + for (int iBucket = m_iBuckets - 1; iBucket >= 0; iBucket--) + { + Bucket *pbucket = &m_pabucket[iBucket]; + + if (pbucket->plinkFirst) + { + if (m_plinkFirst == nullptr) + { + m_plinkFirst = pbucket->plinkFirst; + } + else + { + assert(pbucket->plinkLast->GetNext() == nullptr); + m_plinkLast->SetNext(pbucket->plinkFirst); + } + + m_plinkLast = pbucket->plinkLast; + } + } + + + } + + // ---------------------------------------------------------------------------------------------------- + // clear all of the buckets. normally done in preparation for a sort + // + void SortedBlockList::InitBuckets(void) + { + for (int iBucket = 0; iBucket < m_iBuckets; iBucket++) + { + Bucket *pbucket = &m_pabucket[iBucket]; + + pbucket->plinkFirst = 0; + pbucket->plinkLast = 0; + } + } + + // ---------------------------------------------------------------------------------------------------- + // print out the list of sorted 4x4 blocks + // normally used for debugging + // + void SortedBlockList::Print(void) + { + for (int iBucket = m_iBuckets-1; iBucket >= 0; iBucket--) + { + Bucket *pbucket = &m_pabucket[iBucket]; + + unsigned int uiBlocks = 0; + for (Link *plink = pbucket->plinkFirst; plink != nullptr; plink = plink->GetNext() ) + { + uiBlocks++; + + if (plink == pbucket->plinkLast) + { + break; + } + } + + float fBucketError = m_fMaxError * iBucket / m_iBuckets; + float fBucketRMS = sqrtf(fBucketError / (4.0f*16.0f) ); + printf("%3d: e=%.3f rms=%.6f %u\n", iBucket, fBucketError, fBucketRMS, uiBlocks); + } + } + + // ---------------------------------------------------------------------------------------------------- + // + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/EtcCodec/EtcSortedBlockList.h b/deps/etc2comp/EtcLib/EtcCodec/EtcSortedBlockList.h new file mode 100644 index 0000000000..960e8adc34 --- /dev/null +++ b/deps/etc2comp/EtcLib/EtcCodec/EtcSortedBlockList.h @@ -0,0 +1,124 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace Etc +{ + class Block4x4; + + class SortedBlockList + { + public: + + class Link + { + public: + + inline void Init(Block4x4 *a_pblock) + { + m_pblock = a_pblock; + m_plinkNext = nullptr; + } + + inline Block4x4 * GetBlock(void) + { + return m_pblock; + } + + inline void SetNext(Link *a_plinkNext) + { + m_plinkNext = a_plinkNext; + } + + inline Link * GetNext(void) + { + return m_plinkNext; + } + + inline Link * Advance(unsigned int a_uiSteps = 1) + { + Link *plink = this; + + for (unsigned int uiStep = 0; uiStep < a_uiSteps; uiStep++) + { + if (plink == nullptr) + { + break; + } + + plink = plink->m_plinkNext; + } + + return plink; + } + + private: + + Block4x4 *m_pblock; + Link *m_plinkNext; + }; + + SortedBlockList(unsigned int a_uiImageBlocks, unsigned int a_uiBuckets); + ~SortedBlockList(void); + + void AddBlock(Block4x4 *a_pblock); + + void Sort(void); + + inline Link * GetLinkToFirstBlock(void) + { + return m_plinkFirst; + } + + inline unsigned int GetNumberOfAddedBlocks(void) + { + return m_uiAddedBlocks; + } + + inline unsigned int GetNumberOfSortedBlocks(void) + { + return m_uiSortedBlocks; + } + + void Print(void); + + private: + + void InitBuckets(void); + + class Bucket + { + public: + Link *plinkFirst; + Link *plinkLast; + }; + + unsigned int m_uiImageBlocks; + int m_iBuckets; + + unsigned int m_uiAddedBlocks; + unsigned int m_uiSortedBlocks; + Link *m_palinkPool; + Bucket *m_pabucket; + float m_fMaxError; + + Link *m_plinkFirst; + Link *m_plinkLast; + + }; + +} // namespace Etc diff --git a/deps/etc2comp/EtcLib/etc.epj b/deps/etc2comp/EtcLib/etc.epj new file mode 100644 index 0000000000..c85f611769 --- /dev/null +++ b/deps/etc2comp/EtcLib/etc.epj @@ -0,0 +1,122 @@ +{ + "Version" : 0.2, + "ModuleName" : "etc", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "EtcCodec", + "Etc" + ], + "TargetType" : "SharedLibrary", + "TargetFileName" : "etc" + }, + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "Libraries" : [ + "pthread", + "m", + "stdc++" + ], + "LibraryDirs" : [ + "../deps/etc2comp/EtcLib/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)" + ] + } + } + ], + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + }, + { + "Name" : "Release", + "Options" : { + "Debug" : true, + "Optimization" : "Speed", + "Libraries" : [ + + ], + "FastMath" : false + } + }, + { + "Name" : "Android", + "Options" : { + "Optimization" : "Speed" + }, + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "Libraries" : [ + "pthread", + "m", + "gnustl_static" + ] + } + } + ] + } + ], + "Files" : [ + { + "Folder" : "src", + "Files" : [ + "Etc/EtcConfig.h", + "Etc/EtcMath.cpp", + "Etc/EtcFilter.cpp", + "Etc/EtcColor.h", + "Etc/Etc.h", + "Etc/EtcColorFloatRGBA.h", + "Etc/Etc.cpp", + "Etc/EtcImage.h", + "Etc/EtcFilter.h", + "Etc/EtcImage.cpp", + "Etc/EtcMath.h" + ] + }, + { + "Folder" : "codecs", + "Files" : [ + "EtcCodec/EtcBlock4x4Encoding.cpp", + "EtcCodec/EtcDifferentialTrys.cpp", + "EtcCodec/EtcBlock4x4Encoding_RG11.cpp", + "EtcCodec/EtcBlock4x4Encoding_ETC1.h", + "EtcCodec/EtcBlock4x4Encoding_RGB8.h", + "EtcCodec/EtcBlock4x4Encoding_R11.h", + "EtcCodec/EtcBlock4x4Encoding_RGB8A1.cpp", + "EtcCodec/EtcBlock4x4.h", + "EtcCodec/EtcSortedBlockList.cpp", + "EtcCodec/EtcBlock4x4Encoding_RGBA8.cpp", + "EtcCodec/EtcErrorMetric.h", + "EtcCodec/EtcBlock4x4Encoding_R11.cpp", + "EtcCodec/EtcBlock4x4EncodingBits.h", + "EtcCodec/EtcBlock4x4Encoding_RGB8.cpp", + "EtcCodec/EtcBlock4x4Encoding_ETC1.cpp", + "EtcCodec/EtcBlock4x4Encoding_RGB8A1.h", + "EtcCodec/EtcIndividualTrys.h", + "EtcCodec/EtcBlock4x4Encoding_RG11.h", + "EtcCodec/EtcSortedBlockList.h", + "EtcCodec/EtcBlock4x4Encoding.h", + "EtcCodec/EtcBlock4x4.cpp", + "EtcCodec/EtcDifferentialTrys.h", + "EtcCodec/EtcIndividualTrys.cpp", + "EtcCodec/EtcBlock4x4Encoding_RGBA8.h" + ] + }, + "../etcAPI.cpp" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/deps/etc2comp/EtcTool/Args.txt b/deps/etc2comp/EtcTool/Args.txt new file mode 100644 index 0000000000..1f831d8283 --- /dev/null +++ b/deps/etc2comp/EtcTool/Args.txt @@ -0,0 +1,7 @@ +C:\Users\BSI\Desktop\etc2comp\googleTest.png +-format RGB8 +-errormetric rgba +-output ../../EncodedImages/googleTest.ktx +-analyze ../../Analysis/googleTest +-verbose +-effort 0 diff --git a/deps/etc2comp/EtcTool/CMakeLists.txt b/deps/etc2comp/EtcTool/CMakeLists.txt new file mode 100644 index 0000000000..71aed4e7ba --- /dev/null +++ b/deps/etc2comp/EtcTool/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright 2015 The Etc2Comp Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +project(EtcTool) +include_directories(../EtcLib/Etc) +include_directories(../EtcLib/EtcCodec) +include_directories(../third_party/lodepng) + +file(GLOB SOURCES + ${PROJECT_SOURCE_DIR}/*.h + ${PROJECT_SOURCE_DIR}/*.cpp + ../third_party/lodepng/*.h + ../third_party/lodepng/*.cpp) +add_executable(EtcTool ${SOURCES}) + +target_link_libraries (EtcTool EtcLib) + diff --git a/deps/etc2comp/EtcTool/EtcAnalysis.cpp b/deps/etc2comp/EtcTool/EtcAnalysis.cpp new file mode 100644 index 0000000000..51c0747d11 --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcAnalysis.cpp @@ -0,0 +1,410 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS (1) +#endif + +#include "EtcAnalysis.h" + +#include "EtcTool.h" +#include "EtcComparison.h" +#include "Etc.h" +#include "EtcFile.h" +#include "EtcMath.h" +#include "EtcImage.h" +#include "EtcBlock4x4.h" + +#include "lodepng.h" +#include +#include //sqrt fn() + +namespace Etc +{ + + // ---------------------------------------------------------------------------------------------------- + // + Analysis::Analysis(Image *a_pimage, const char *a_pstrOutputFolder) + { + + m_pimage = a_pimage; + m_pstrOutputFolder = a_pstrOutputFolder; + m_uiComparisons = 0; + + CreateNewDir(a_pstrOutputFolder); + + // write log file + { + char strFilename[200]; + sprintf(strFilename, "%s%cAnalysis.txt", m_pstrOutputFolder, ETC_PATH_SLASH); + + FILE *pfileTxt = fopen(strFilename, "wt"); + if (pfileTxt == nullptr) + { + printf("Error: couldn't create analysis log file (%s)\n", strFilename); + exit(1); + } + + float fImageError = m_pimage->GetError(); + unsigned int uiImagePixels = m_pimage->GetSourceWidth() * m_pimage->GetSourceHeight(); + + // output stats to both stdout and the analysis file + int numOutputs = 1; + FILE *apfile[2]; + apfile[0] = pfileTxt; + + if (m_pimage->m_bVerboseOutput) + { + apfile[1] = stdout; + numOutputs++; + } + + for (int i = 0; i < numOutputs; i++) + { + + if (a_pimage->GetFormat() == Image::Format::R11 || a_pimage->GetFormat() == Image::Format::SIGNED_R11) + { + fprintf(apfile[i], "PSNR(r) = %.4f\n", ConvertErrorToPSNR(fImageError, 1 * uiImagePixels)); + } + else if (a_pimage->GetFormat() == Image::Format::RG11 || a_pimage->GetFormat() == Image::Format::SIGNED_RG11) + { + fprintf(apfile[i], "PSNR(rg) = %.4f\n", ConvertErrorToPSNR(fImageError, 2 * uiImagePixels)); + } + else + { + int iComponents=3; + if (a_pimage->GetErrorMetric() == ErrorMetric::REC709) + { + iComponents = (int)Block4x4Encoding::LUMA_WEIGHT + 2; + } + fprintf(apfile[i], "PSNR(rgb) = %.4f\n", ConvertErrorToPSNR(fImageError, iComponents * uiImagePixels)); + fprintf(apfile[i], "PSNR(rgba) = %.4f\n", ConvertErrorToPSNR(fImageError, (iComponents+1) * uiImagePixels)); + } + + fprintf(apfile[i], "EncodeTime = %.3f seconds\n", (float)m_pimage->GetEncodingTimeMs() / 1000.0f); + } + + + fclose(pfileTxt); + } + + // scale == 1 + DrawImage(m_pimage, m_pstrOutputFolder, false); + + // scale == 2, with modes + DrawImage(m_pimage, m_pstrOutputFolder, true); + + } + + // ---------------------------------------------------------------------------------------------------- + // + void Analysis::Compare(const char *a_pstrFilename, int a_iPixelX, int a_iPixelY) + { + if (m_uiComparisons >= MAX_COMPARISONS) + { + printf("Error: too many comparisons\n"); + exit(1); + } + + m_apcomparison[m_uiComparisons] = new Comparison(this, m_uiComparisons, a_pstrFilename, a_iPixelX, a_iPixelY); + + m_uiComparisons++; + + } + + // ---------------------------------------------------------------------------------------------------- + // draw 2x image + // optionally display encoding modes + void Analysis::DrawImage(Image *a_pimage, const char *a_pstrOutputFolder, bool a_boolDrawModes) + { + unsigned int uiPngWidth = a_pimage->GetExtendedWidth(); + unsigned int uiPngHeight = a_pimage->GetExtendedHeight(); + if (a_boolDrawModes) + { + uiPngWidth *= 2; + uiPngHeight *= 2; + } + + unsigned char* paucPngPixels = new unsigned char[uiPngWidth * uiPngHeight * 4]; + assert(paucPngPixels); + + ColorR8G8B8A8 *pargba8PngPixels = (ColorR8G8B8A8 *)paucPngPixels; + + for (unsigned int uiBlock = 0; uiBlock < a_pimage->GetNumberOfBlocks(); uiBlock++) + { + Block4x4 *pblock = &a_pimage->GetBlocks()[uiBlock]; + if (a_boolDrawModes) + { + if (a_pimage->GetFormat() == Image::Format::R11 || a_pimage->GetFormat() == Image::Format::SIGNED_R11) + { + DrawBlockPixels(pblock, pargba8PngPixels, uiPngWidth, true, 2); + } + else + { + DrawBlockPixels(pblock, pargba8PngPixels, uiPngWidth, false, 2); + } + DrawBlockMode2x(pblock, pargba8PngPixels, uiPngWidth); + } + else + { + if (a_pimage->GetFormat() == Image::Format::R11 || a_pimage->GetFormat() == Image::Format::SIGNED_R11) + { + DrawBlockPixels(pblock, pargba8PngPixels, uiPngWidth, true); + } + else + { + DrawBlockPixels(pblock, pargba8PngPixels, uiPngWidth, false); + } + } + } + + char strFilename[200]; + if (a_boolDrawModes) + { + sprintf(strFilename, "%s%cModes.png", a_pstrOutputFolder, ETC_PATH_SLASH); + } + else + { + sprintf(strFilename, "%s%cDecoded.png", a_pstrOutputFolder, ETC_PATH_SLASH); + } + + unsigned iResult = lodepng_encode32_file(strFilename, paucPngPixels, uiPngWidth, uiPngHeight); + + if (iResult != 0) + { + if (a_boolDrawModes) + { + printf("Error couldn't write modes image (%s)\n", strFilename); + } + else + { + printf("Error couldn't write decoded image (%s)\n", strFilename); + } + + exit(1); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // + void Analysis::DrawBlockPixels(Block4x4 *a_pblock, + ColorR8G8B8A8 *a_pargba8Output, + unsigned int a_uiOutputWidth, + bool a_bGrayscale, + unsigned int a_uiScale) + { + + // output pixel coord of upper left corner of block + unsigned int uiBlockH = a_uiScale * a_pblock->GetSourceH(); + unsigned int uiBlockV = a_uiScale * a_pblock->GetSourceV(); + + ColorR8G8B8A8 *pargba8Block = &a_pargba8Output[uiBlockV*a_uiOutputWidth + uiBlockH]; + ColorFloatRGBA *pafrgbaDecodedColor = a_pblock->GetDecodedColors(); + float *pafDecodedAlpha = a_pblock->GetDecodedAlphas(); + + for (unsigned int uiPixel = 0; uiPixel < Block4x4::PIXELS; uiPixel++) + { + ColorFloatRGBA *pfrgba = &pafrgbaDecodedColor[uiPixel]; + int iR; + int iG; + int iB; + if (a_bGrayscale) + { + iR = pfrgba->IntRed(255.0f); + iG = pfrgba->IntRed(255.0f); + iB = pfrgba->IntRed(255.0f); + } + else + { + iR = pfrgba->IntRed(255.0f); + iG = pfrgba->IntGreen(255.0f); + iB = pfrgba->IntBlue(255.0f); + } + + int iA = (int) roundf((255.0f*pafDecodedAlpha[uiPixel])); + + ColorR8G8B8A8 *pargba8ScaledPixel = &pargba8Block[(a_uiScale * (uiPixel % 4))*a_uiOutputWidth + + a_uiScale * (uiPixel / 4)]; + + // draw scaled pixel + for (unsigned int uiV = 0; uiV < a_uiScale; uiV++) + { + for (unsigned int uiH = 0; uiH < a_uiScale; uiH++) + { + ColorR8G8B8A8 *prgba8 = &pargba8ScaledPixel[uiV*a_uiOutputWidth + uiH]; + + prgba8->ucR = (unsigned char)iR; + prgba8->ucG = (unsigned char)iG; + prgba8->ucB = (unsigned char)iB; + prgba8->ucA = (unsigned char)iA; + } + } + } + + } + + // ---------------------------------------------------------------------------------------------------- + // + void Analysis::DrawBlockMode2x(Block4x4 *a_pblock, + ColorR8G8B8A8 *a_pargba8Output, + unsigned int a_uiOutputWidth) + { + static const unsigned int SCALE = 2; + + typedef struct + { + int iH; + int iV; + } PixelCoord; + + static const PixelCoord s_apixelcoordOutline[] = { + { 0,0 },{ 1,0 },{ 2,0 },{ 3,0 },{ 4,0 },{ 5,0 },{ 6,0 },{ 7,0 }, + { 0,1 },{ 0,2 },{ 0,3 },{ 0,4 },{ 0,5 },{ 0,6 },{ 0,7 },{ -1,-1 } + }; + + static const PixelCoord s_apixelcoordFlip0[] = + { + { 4,2 },{ 4,4 },{ 4,6 },{ -1,-1 } + }; + + static const PixelCoord s_apixelcoordFlip1[] = + { + { 2,4 },{ 4,4 },{ 6,4 },{ -1,-1 } + }; + + static const PixelCoord s_apixelcoordBentDiffFlip0[] = + { + { 4,2 },{ 4,3 },{ 4,5 },{ 4,6 },{ -1,-1 } + }; + + static const PixelCoord s_apixelcoordBentDiffFlip1[] = + { + { 2,4 },{ 3,4 },{ 5,4 },{ 6,4 },{ -1,-1 } + }; + + static const PixelCoord s_apixelcoordIndividual[] = + { + { 1,1 },{ 1,7 },{ 7,1 },{ 7,7 },{ -1,-1 } + }; + + static const PixelCoord s_apixelcoordT[] = + { + { 3,2 },{ 5,2 }, + { 4,2 },{ 4,3 },{ 4,4 },{ 4,5 },{ 4,6 },{ -1,-1 } + }; + + static const PixelCoord s_apixelcoordH[] = + { + { 4,4 }, + { 3,2 },{ 3,3 },{ 3,4 },{ 3,5 },{ 3,6 }, + { 5,2 },{ 5,3 },{ 5,4 },{ 5,5 },{ 5,6 },{ -1,-1 } + }; + + static const PixelCoord s_apixelcoordPlanar[] = + { + { 4,2 },{ 5,2 },{ 5,3 },{ 5,4 },{ 4,4 }, + { 3,2 },{ 3,3 },{ 3,4 },{ 3,5 },{ 3,6 },{ -1,-1 } + }; + + static const PixelCoord s_apixelcoordUnknown[] = + { + { 3,2 },{ 4,2 },{ 5,2 },{ 5,3 },{ 5,4 },{ 4,4 },{ 4,6 },{ -1,-1 } + }; + + // output pixel coord of upper left corner of block + unsigned int uiBlockH = SCALE * a_pblock->GetSourceH(); + unsigned int uiBlockV = SCALE * a_pblock->GetSourceV(); + + ColorR8G8B8A8 *pargba8Block = &a_pargba8Output[uiBlockV*a_uiOutputWidth + uiBlockH]; + ColorR8G8B8A8 rgba8Gray; + rgba8Gray.ucR = 128; + rgba8Gray.ucG = 128; + rgba8Gray.ucB = 128; + rgba8Gray.ucA = 255; + + // outline + for (const PixelCoord *pcoord = s_apixelcoordOutline; pcoord->iH >= 0; pcoord++) + { + ColorR8G8B8A8 *prgba8 = &pargba8Block[pcoord->iV*a_uiOutputWidth + pcoord->iH]; + *prgba8 = rgba8Gray; + } + + const PixelCoord *pacoordMode = nullptr; + + switch (a_pblock->GetEncodingMode()) + { + case Block4x4Encoding::MODE_ETC1: + + // H/V split + pacoordMode = a_pblock->GetFlip() ? s_apixelcoordFlip1 : s_apixelcoordFlip0; + + // individial + if (a_pblock->IsDifferential() == false) + { + for (const PixelCoord *pcoord = s_apixelcoordIndividual; pcoord->iH >= 0; pcoord++) + { + ColorR8G8B8A8 *prgba8 = &pargba8Block[pcoord->iV*a_uiOutputWidth + pcoord->iH]; + *prgba8 = rgba8Gray; + } + } + else if (a_pblock->GetEncoding()->HasSeverelyBentDifferentialColors()) + { + pacoordMode = a_pblock->GetFlip() ? s_apixelcoordBentDiffFlip1 : s_apixelcoordBentDiffFlip0; + } + break; + + case Block4x4Encoding::MODE_T: + pacoordMode = s_apixelcoordT; + break; + + case Block4x4Encoding::MODE_H: + pacoordMode = s_apixelcoordH; + break; + + case Block4x4Encoding::MODE_PLANAR: + pacoordMode = s_apixelcoordPlanar; + break; + + default: + pacoordMode = s_apixelcoordUnknown; + break; + } + + // draw mode + for (const PixelCoord *pcoord = pacoordMode; pcoord->iH >= 0; pcoord++) + { + ColorR8G8B8A8 *prgba8 = &pargba8Block[pcoord->iV*a_uiOutputWidth + pcoord->iH]; + *prgba8 = rgba8Gray; + } + + } + + // ---------------------------------------------------------------------------------------------------- + // + float Analysis::ConvertErrorToPSNR(float a_fError, unsigned int a_uiTotalComponents) + { + + float fMSE = a_fError / (float)a_uiTotalComponents; + float fPSNR = ConvertMSEToPSNR(fMSE); + + return fPSNR; + } + + // ---------------------------------------------------------------------------------------------------- + // + +} diff --git a/deps/etc2comp/EtcTool/EtcAnalysis.h b/deps/etc2comp/EtcTool/EtcAnalysis.h new file mode 100644 index 0000000000..16bc975d4a --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcAnalysis.h @@ -0,0 +1,71 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcColor.h" + +namespace Etc +{ + + class Comparison; + class Image; + class Block4x4; + + class Analysis + { + public: + + static const unsigned int MAX_COMPARISONS = 4; + + Analysis(Image *a_pimage, const char *a_pstrOutputFolder); + + void Compare(const char *a_pstrFilename, int a_iPixelX = -1, int a_iPixelY = -1); + + inline const char * GetOutputFolder(void) + { + return m_pstrOutputFolder; + } + + static void DrawImage(Image *a_pimage, const char *a_pstrOutputFolder, + bool a_boolDrawModes); + + inline Image * GetImage(void) + { + return m_pimage; + } + + static void DrawBlockPixels(Block4x4 *a_pblock, + ColorR8G8B8A8 *a_pargba8Output, + unsigned int a_uiOutputWidth, + bool a_bGrayscale, + unsigned int a_uiScale = 1); + + static float ConvertErrorToPSNR(float a_fError, unsigned int a_uiTotalComponents); + + private: + + static void DrawBlockMode2x(Block4x4 *a_pblock, + ColorR8G8B8A8 *a_pargba8Output, + unsigned int a_uiOutputWidth); + + Image *m_pimage; + const char *m_pstrOutputFolder; + unsigned int m_uiComparisons; + Comparison *m_apcomparison[MAX_COMPARISONS]; + }; + +} diff --git a/deps/etc2comp/EtcTool/EtcComparison.cpp b/deps/etc2comp/EtcTool/EtcComparison.cpp new file mode 100644 index 0000000000..47251a3711 --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcComparison.cpp @@ -0,0 +1,637 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS (1) +#endif + +#include "EtcConfig.h" + +#include "EtcComparison.h" + +#include "EtcAnalysis.h" +#include "EtcTool.h" +#include "Etc.h" +#include "EtcFile.h" +#include "EtcMath.h" +#include "EtcImage.h" +#include "EtcBlock4x4.h" +#include "EtcBlock4x4Encoding_ETC1.h" +#include "EtcBlock4x4Encoding_RGB8.h" +#include "EtcBlock4x4Encoding_R11.h" +#include "EtcBlock4x4Encoding_RG11.h" + +#include "lodepng.h" + +#include +#include +#include +#include + +namespace Etc +{ + const float Comparison::ERROR_EPSILON = 0.000001f; + + // ---------------------------------------------------------------------------------------------------- + // + Comparison::Comparison(Analysis *a_panalysisParent, unsigned int a_uiIndex, const char *a_pstrFilename, int a_iPixelX, int a_iPixelY) + { + + m_panalysisParent = a_panalysisParent; + m_uiIndex = a_uiIndex; + m_pstrFilename = new char[strlen(a_pstrFilename)+1]; + strcpy(m_pstrFilename, a_pstrFilename); + + SetName(); + + m_pstrOutputFolder = new char[256]; + sprintf(m_pstrOutputFolder, "%s%cComparison_%s", + m_panalysisParent->GetOutputFolder(), ETC_PATH_SLASH, m_pstrName); + + CreateNewDir(m_pstrOutputFolder); + + // read etc file + Etc::File etcfile(a_pstrFilename, Etc::File::Format::INFER_FROM_FILE_EXTENSION); + + etcfile.UseSingleBlock(a_iPixelX, a_iPixelY); + + // construct image with encoding bits + m_pimage = new Image(etcfile.GetImageFormat(), + etcfile.GetSourceWidth(), + etcfile.GetSourceHeight(), + etcfile.GetEncodingBits(), + etcfile.GetEncodingBitsBytes(), + a_panalysisParent->GetImage(), + a_panalysisParent->GetImage()->GetErrorMetric()); + + if (m_pimage->GetExtendedWidth() != a_panalysisParent->GetImage()->GetExtendedWidth() || + m_pimage->GetExtendedHeight() != a_panalysisParent->GetImage()->GetExtendedHeight()) + { + printf("Error: comparison image (%s) has different width or height\n", m_pstrFilename); + exit(1); + } + + // scale = 1 + Analysis::DrawImage(m_pimage, m_pstrOutputFolder, false); + + // scale = 2, with modes + Analysis::DrawImage(m_pimage, m_pstrOutputFolder, true); + + DrawImageComparison(m_panalysisParent->GetImage()); + + WriteLogFile(); + + } + + // ---------------------------------------------------------------------------------------------------- + // + void Comparison::DrawImageComparison(Image *a_pimageUnderTest) + { + + assert(m_pimage->GetExtendedWidth() == a_pimageUnderTest->GetExtendedWidth() && + m_pimage->GetExtendedHeight() == a_pimageUnderTest->GetExtendedHeight()); + + unsigned int uiPngWidth = 2 * m_pimage->GetExtendedWidth(); + unsigned int uiPngHeight = 2 * m_pimage->GetExtendedHeight(); + + unsigned char* paucPngPixels = new unsigned char[uiPngWidth * uiPngHeight * 4]; + assert(paucPngPixels); + + ColorR8G8B8A8 *pargba8PngPixels = (ColorR8G8B8A8 *)paucPngPixels; + + for (unsigned int uiBlock = 0; uiBlock < m_pimage->GetNumberOfBlocks(); uiBlock++) + { + Block4x4 *pblock = &m_pimage->GetBlocks()[uiBlock]; + Block4x4 *pblockUnderTest = &a_pimageUnderTest->GetBlocks()[uiBlock]; + + if (a_pimageUnderTest->GetFormat() == Image::Format::R11 || a_pimageUnderTest->GetFormat() == Image::Format::SIGNED_R11) + { + Analysis::DrawBlockPixels(pblock, pargba8PngPixels, uiPngWidth, true, 2); + } + else + { + Analysis::DrawBlockPixels(pblock, pargba8PngPixels, uiPngWidth, false, 2); + } + + float fErrorBlockReference = pblock->GetError(); + float fErrorBlockUnderTest = pblockUnderTest->GetError(); + + DrawBlockComparison2x(pblockUnderTest, pargba8PngPixels, uiPngWidth, + fErrorBlockUnderTest, fErrorBlockReference); + } + + char strFilename[200]; + sprintf(strFilename, "%s%cComparison.png", m_pstrOutputFolder, ETC_PATH_SLASH); + + unsigned iResult = lodepng_encode32_file(strFilename, paucPngPixels, uiPngWidth, uiPngHeight); + + if (iResult != 0) + { + printf("Error couldn't write modes image (%s)\n", strFilename); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // + void Comparison::DrawBlockComparison2x(Block4x4 *a_pblockUnderTest, + ColorR8G8B8A8 *a_pargba8Output, + unsigned int a_uiOutputWidth, + float a_fErrorBlockUnderTest, + float a_fErrorBlockReference) + { + static const unsigned int SCALE = 2; + + typedef struct + { + int iH; + int iV; + } PixelCoord; + + static const PixelCoord s_apixelcoordDifference0[] = { + { 0,0 },{ 1,0 },{ 2,0 },{ 3,0 },{ 4,0 },{ 5,0 },{ 6,0 },{ 7,0 }, + { 0,1 },{ 0,2 },{ 0,3 },{ 0,4 },{ 0,5 },{ 0,6 },{ 0,7 }, + { 1,7 },{ 2,7 },{ 3,7 },{ 4,7 },{ 5,7 },{ 6,7 },{ 7,7 }, + { 7,1 },{ 7,2 },{ 7,3 },{ 7,4 },{ 7,5 },{ 7,6 },{ -1,-1 } + }; + + static const PixelCoord s_apixelcoordDifference5[] = { + { 3,3 }, + { 0,0 },{ 1,0 },{ 2,0 },{ 3,0 },{ 4,0 },{ 5,0 },{ 6,0 },{ 7,0 }, + { 0,1 },{ 0,2 },{ 0,3 },{ 0,4 },{ 0,5 },{ 0,6 },{ 0,7 }, + { 1,7 },{ 2,7 },{ 3,7 },{ 4,7 },{ 5,7 },{ 6,7 },{ 7,7 }, + { 7,1 },{ 7,2 },{ 7,3 },{ 7,4 },{ 7,5 },{ 7,6 },{ -1,-1 } + }; + + static const PixelCoord s_apixelcoordDifference10[] = { + { 3,3 },{ 3,4 },{ 4,3 },{ 4,4 }, + { 0,0 },{ 1,0 },{ 2,0 },{ 3,0 },{ 4,0 },{ 5,0 },{ 6,0 },{ 7,0 }, + { 0,1 },{ 0,2 },{ 0,3 },{ 0,4 },{ 0,5 },{ 0,6 },{ 0,7 }, + { 1,7 },{ 2,7 },{ 3,7 },{ 4,7 },{ 5,7 },{ 6,7 },{ 7,7 }, + { 7,1 },{ 7,2 },{ 7,3 },{ 7,4 },{ 7,5 },{ 7,6 },{ -1,-1 } + }; + + static const PixelCoord s_apixelcoordDifference20[] = { + { 2,2 },{ 2,3 },{ 3,2 },{ 3,3 },{ 4,4 },{ 4,5 },{ 5,4 },{ 5,5 }, + { 0,0 },{ 1,0 },{ 2,0 },{ 3,0 },{ 4,0 },{ 5,0 },{ 6,0 },{ 7,0 }, + { 0,1 },{ 0,2 },{ 0,3 },{ 0,4 },{ 0,5 },{ 0,6 },{ 0,7 }, + { 1,7 },{ 2,7 },{ 3,7 },{ 4,7 },{ 5,7 },{ 6,7 },{ 7,7 }, + { 7,1 },{ 7,2 },{ 7,3 },{ 7,4 },{ 7,5 },{ 7,6 },{ -1,-1 } + }; + + static const PixelCoord s_apixelcoordCorners[] = { + { 0,0 },{ 7,0 },{ 0,7 },{ 7,7 },{ -1,-1 } + }; + + // output pixel coord of upper left corner of block + unsigned int uiBlockH = SCALE * a_pblockUnderTest->GetSourceH(); + unsigned int uiBlockV = SCALE * a_pblockUnderTest->GetSourceV(); + + ColorR8G8B8A8 *pargba8Block = &a_pargba8Output[uiBlockV*a_uiOutputWidth + uiBlockH]; + + float fRelativeError = 0.0f; + if (a_fErrorBlockUnderTest != a_fErrorBlockReference) + { + fRelativeError = fabs((a_fErrorBlockUnderTest - a_fErrorBlockReference) / + (a_fErrorBlockUnderTest + a_fErrorBlockReference)); + + if (fabsf(a_fErrorBlockUnderTest - a_fErrorBlockReference) < ERROR_EPSILON) + { + fRelativeError = 0.0f; + } + } + + ColorR8G8B8A8 rgb8Draw; + rgb8Draw.ucA = 255; + + // equal + if (fRelativeError == 0.0f) + { + rgb8Draw.ucR = 128; + rgb8Draw.ucG = 128; + rgb8Draw.ucB = 128; + } + // better tthan reference + else if (a_fErrorBlockUnderTest < a_fErrorBlockReference) + { + rgb8Draw.ucR = 0; + rgb8Draw.ucG = 255; + rgb8Draw.ucB = 0; + } + // worse than reference + else if (a_fErrorBlockUnderTest > a_fErrorBlockReference) + { + rgb8Draw.ucR = 255; + rgb8Draw.ucG = 0; + rgb8Draw.ucB = 0; + } + + const PixelCoord *papixelcoordDraw = s_apixelcoordCorners; + + // if 20% worse + if (fRelativeError >= 1.44f) + { + papixelcoordDraw = s_apixelcoordDifference20; + } + // if 10% worse + else if (fRelativeError >= 1.21f) + { + papixelcoordDraw = s_apixelcoordDifference10; + } + // if 5% worse + else if (fRelativeError >= 1.1025f) + { + papixelcoordDraw = s_apixelcoordDifference5; + } + else if (fRelativeError > 0.0f) + { + papixelcoordDraw = s_apixelcoordDifference0; + } + + // outline + for (const PixelCoord *pcoord = papixelcoordDraw; pcoord->iH >= 0; pcoord++) + { + ColorR8G8B8A8 *prgba8 = &pargba8Block[pcoord->iV*a_uiOutputWidth + pcoord->iH]; + + *prgba8 = rgb8Draw; + } + + } + + // ---------------------------------------------------------------------------------------------------- + // + void Comparison::SetName(void) + { + // alloc memory for name + unsigned int uiNameBytes = (unsigned int)strlen(m_pstrFilename) + 1; + if (uiNameBytes < 4) + { + uiNameBytes = 4; + } + m_pstrName = new char[uiNameBytes]; + m_pstrName[0] = 0; + + // first, try to find folder name of comparison image + { + char *pstr = new char[strlen(m_pstrFilename) + 1]; + strcpy(pstr, m_pstrFilename); + + int iLastSlash; + int iPenultimateSlash; + + // find last slash + for (iLastSlash = (int)strlen(pstr) - 1; iLastSlash >= 0; iLastSlash--) + { + if (pstr[iLastSlash] == ETC_PATH_SLASH) + { + break; + } + } + + // find penultimate slash + for (iPenultimateSlash = iLastSlash - 1; iPenultimateSlash >= 0; iPenultimateSlash--) + { + if (pstr[iPenultimateSlash] == ETC_PATH_SLASH) + { + break; + } + } + + if (iLastSlash > 0) + { + pstr[iLastSlash] = 0; + strcpy(m_pstrName, &pstr[iPenultimateSlash] + 1); + assert(strlen(m_pstrName) > 0); + } + + delete[] pstr; + } + + // otherwise use index as name + if (m_pstrName[0] == 0) + { + sprintf(m_pstrName, "%u", m_uiIndex); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // + void Comparison::WriteLogFile(void) + { + + char strPath[200]; + sprintf(strPath, "%s%cComparison.txt", m_pstrOutputFolder, ETC_PATH_SLASH); + + FILE *pfile = fopen(strPath, "wt"); + if (pfile == nullptr) + { + printf("Error couldn't open comparison log file (%s)\n", strPath); + exit(1); + } + + float fImageError = m_panalysisParent->GetImage()->GetError(); + unsigned int uiImagePixels = m_panalysisParent->GetImage()->GetSourceWidth() * + m_panalysisParent->GetImage()->GetSourceHeight(); + + if (m_pimage->GetFormat() == Image::Format::R11 || m_pimage->GetFormat() == Image::Format::SIGNED_R11) + { + fprintf(pfile, "PSNR(r) = %.4f\n", Analysis::ConvertErrorToPSNR(fImageError, 1 * uiImagePixels)); + fprintf(pfile, "\n"); + } + else if (m_pimage->GetFormat() == Image::Format::RG11 || m_pimage->GetFormat() == Image::Format::SIGNED_RG11) + { + fprintf(pfile, "PSNR(rg) = %.4f\n", Analysis::ConvertErrorToPSNR(fImageError, 2 * uiImagePixels)); + fprintf(pfile, "\n"); + } + else + { + fprintf(pfile, "PSNR(rgb) = %.4f\n", Analysis::ConvertErrorToPSNR(fImageError, 3 * uiImagePixels)); + fprintf(pfile, "PSNR(rgba) = %.4f\n", Analysis::ConvertErrorToPSNR(fImageError, 4 * uiImagePixels)); + fprintf(pfile, "\n"); + } + float fReferenceImageError = m_pimage->GetError(); + + fprintf(pfile, "reference image %s\n", m_pstrFilename); + if (m_pimage->GetFormat() == Image::Format::R11 || m_pimage->GetFormat() == Image::Format::SIGNED_R11) + { + fprintf(pfile, "reference PSNR(r) = %.4f\n", + Analysis::ConvertErrorToPSNR(fReferenceImageError, 1 * uiImagePixels)); + } + else if (m_pimage->GetFormat() == Image::Format::RG11 || m_pimage->GetFormat() == Image::Format::SIGNED_RG11) + { + fprintf(pfile, "reference PSNR(rg) = %.4f\n", + Analysis::ConvertErrorToPSNR(fReferenceImageError, 2 * uiImagePixels)); + } + else + { + fprintf(pfile, "reference PSNR(rgb) = %.4f\n", + Analysis::ConvertErrorToPSNR(fReferenceImageError, 3 * uiImagePixels)); + fprintf(pfile, "reference PSNR(rgba) = %.4f\n", + Analysis::ConvertErrorToPSNR(fReferenceImageError, 4 * uiImagePixels)); + } + WriteBetterOrWorseBlocks(pfile, false); + WriteBetterOrWorseBlocks(pfile, true); + + fclose(pfile); + + } + + // ---------------------------------------------------------------------------------------------------- + // + void Comparison::WriteBetterOrWorseBlocks(FILE *a_pfile, bool a_boolWorse) + { + + fprintf(a_pfile, "\n"); + if (a_boolWorse) + { + fprintf(a_pfile, "Worse Blocks\n"); + } + else + { + fprintf(a_pfile, "Better Blocks\n"); + } + + for (unsigned int uiBlock = 0; uiBlock < m_pimage->GetNumberOfBlocks(); uiBlock++) + { + Block4x4 *pblockReference = &m_pimage->GetBlocks()[uiBlock]; + Block4x4 *pblockUnderTest = &m_panalysisParent->GetImage()->GetBlocks()[uiBlock]; + + float fErrorBlockReference = pblockReference->GetError(); + float fErrorBlockUnderTest = pblockUnderTest->GetError(); + + if (fabsf(fErrorBlockUnderTest - fErrorBlockReference) >= ERROR_EPSILON) + { + if ((a_boolWorse && (fErrorBlockUnderTest > fErrorBlockReference)) || + (!a_boolWorse && (fErrorBlockUnderTest < fErrorBlockReference))) + { + fprintf(a_pfile, "HV=%u,%u\n", + pblockUnderTest->GetSourceH(), pblockUnderTest->GetSourceV()); + + WriteBlockInfo(a_pfile, pblockUnderTest, "bsi"); + WriteBlockInfo(a_pfile, pblockReference, "ref"); + + } + } + + } + + } + + // ---------------------------------------------------------------------------------------------------- + // + void Comparison::WriteBlockInfo(FILE *a_pfile, Block4x4 *a_pblock, const char *a_pstrEncoder) + { + + Block4x4Encoding *pencoding = a_pblock->GetEncoding(); + + Block4x4Encoding_R11 *pencoding_r11 = static_cast(pencoding); + Block4x4Encoding_RG11 *pencoding_rg11 = static_cast(pencoding); + Block4x4Encoding_RGB8 *pencoding_rgb8 = static_cast(pencoding); + + fprintf(a_pfile, " %s:", a_pstrEncoder); + + float fPSNR = 0.0f; + if (m_pimage->GetFormat() == Image::Format::R11 || m_pimage->GetFormat() == Image::Format::SIGNED_R11) + { + fPSNR = Analysis::ConvertErrorToPSNR(a_pblock->GetError(), 1 * 16); + } + else if (m_pimage->GetFormat() == Image::Format::RG11 || m_pimage->GetFormat() == Image::Format::SIGNED_RG11) + { + fPSNR = Analysis::ConvertErrorToPSNR(a_pblock->GetError(), 2 * 16); + } + else + { + fPSNR = Analysis::ConvertErrorToPSNR(a_pblock->GetError(), 3 * 16); + } + if (a_pblock->GetEncodingMode() == Block4x4Encoding::MODE_RG11) + { + fprintf(a_pfile, "psnr=%6.3f\n", fPSNR); + } + else + { + fprintf(a_pfile, "psnr=%6.3f ", fPSNR); + } + bool boolWriteSelectors = false; + + if (m_pimage->GetFormat() == Image::Format::RGBA8) + { + fprintf(a_pfile, "RGBA8 "); + fprintf(a_pfile, "alpha base(%4.0f) ", pencoding_r11->GetRedBase()); + fprintf(a_pfile, "alpha multiplier(%3.0f) ", pencoding_r11->GetRedMultiplier()); + fprintf(a_pfile, "alpha table index(%2d) ", pencoding_r11->GetRedTableIndex()); + } + if (a_pblock->GetEncodingMode() == Block4x4Encoding::MODE_ETC1) + { + fprintf(a_pfile, "ETC1(%c,%c) ", + a_pblock->IsDifferential() ? 'D' : 'I', + a_pblock->GetFlip() ? 'V' : 'H' ); + + if (a_pblock->IsDifferential()) + { + fprintf(a_pfile, "color1(%2d,%2d,%2d) ", + pencoding_rgb8->GetColor1().IntRed(31.0f), + pencoding_rgb8->GetColor1().IntGreen(31.0f), + pencoding_rgb8->GetColor1().IntBlue(31.0f) ); + fprintf(a_pfile, "color2(%2d,%2d,%2d) ", + pencoding_rgb8->GetColor2().IntRed(31.0f), + pencoding_rgb8->GetColor2().IntGreen(31.0f), + pencoding_rgb8->GetColor2().IntBlue(31.0f)); + } + else + { + fprintf(a_pfile, "color1(%2d,%2d,%2d) ", + pencoding_rgb8->GetColor1().IntRed(15.0f), + pencoding_rgb8->GetColor1().IntGreen(15.0f), + pencoding_rgb8->GetColor1().IntBlue(15.0f)); + fprintf(a_pfile, "color2(%2d,%2d,%2d) ", + pencoding_rgb8->GetColor2().IntRed(15.0f), + pencoding_rgb8->GetColor2().IntGreen(15.0f), + pencoding_rgb8->GetColor2().IntBlue(15.0f)); + } + + fprintf(a_pfile, "cw1(%d) cw2(%d) ", pencoding_rgb8->GetCW1(), pencoding_rgb8->GetCW2()); + + boolWriteSelectors = true; + } + else if (a_pblock->GetEncodingMode() == Block4x4Encoding::MODE_T || + a_pblock->GetEncodingMode() == Block4x4Encoding::MODE_H) + { + fprintf(a_pfile, "%-9s ", a_pblock->GetEncodingModeName()); + + fprintf(a_pfile, "color1(%2d,%2d,%2d) ", + pencoding_rgb8->GetColor1().IntRed(15.0f), + pencoding_rgb8->GetColor1().IntGreen(15.0f), + pencoding_rgb8->GetColor1().IntBlue(15.0f)); + fprintf(a_pfile, "color2(%2d,%2d,%2d) ", + pencoding_rgb8->GetColor2().IntRed(15.0f), + pencoding_rgb8->GetColor2().IntGreen(15.0f), + pencoding_rgb8->GetColor2().IntBlue(15.0f)); + + fprintf(a_pfile, "cw1(%d) ", pencoding_rgb8->GetCW1() ); + + boolWriteSelectors = true; + } + else if (a_pblock->GetEncodingMode() == Block4x4Encoding::MODE_PLANAR) + { + fprintf(a_pfile, "%-9s ", a_pblock->GetEncodingModeName()); + + fprintf(a_pfile, "color1(%2d,%2d,%2d) ", + pencoding_rgb8->GetColor1().IntRed(63.0f), + pencoding_rgb8->GetColor1().IntGreen(127.0f), + pencoding_rgb8->GetColor1().IntBlue(63.0f)); + fprintf(a_pfile, "color2(%2d,%2d,%2d) ", + pencoding_rgb8->GetColor2().IntRed(63.0f), + pencoding_rgb8->GetColor2().IntGreen(127.0f), + pencoding_rgb8->GetColor2().IntBlue(63.0f)); + fprintf(a_pfile, "color3(%2d,%2d,%2d) ", + pencoding_rgb8->GetColor3().IntRed(63.0f), + pencoding_rgb8->GetColor3().IntGreen(127.0f), + pencoding_rgb8->GetColor3().IntBlue(63.0f)); + } + else if (a_pblock->GetEncodingMode() == Block4x4Encoding::MODE_R11) + { + fprintf(a_pfile, "R11 "); + fprintf(a_pfile, "base(%4.0f) ", pencoding_r11->GetRedBase()); + fprintf(a_pfile, "multiplier(%3.0f) ", pencoding_r11->GetRedMultiplier()); + fprintf(a_pfile, "table index(%2d) ", pencoding_r11->GetRedTableIndex()); + + boolWriteSelectors = true; + } + else if (a_pblock->GetEncodingMode() == Block4x4Encoding::MODE_RG11) + { + fprintf(a_pfile, "RG11 image: %s\n", a_pstrEncoder); + fprintf(a_pfile, "red base(%4.0f)\n", pencoding_rg11->GetRedBase()); + fprintf(a_pfile, "grn base(%4.0f)\n\n", pencoding_rg11->GetGrnBase()); + + fprintf(a_pfile, "red multiplier(%3.0f)\n", pencoding_rg11->GetRedMultiplier()); + fprintf(a_pfile, "grn multiplier(%3.0f)\n\n", pencoding_rg11->GetGrnMultiplier()); + + fprintf(a_pfile, "red table index(%2d)\n", pencoding_rg11->GetRedTableIndex()); + fprintf(a_pfile, "grn table index(%2d)\n\n", pencoding_rg11->GetGrnTableIndex()); + + boolWriteSelectors = true; + } + else + { + assert(0); + } + + if (boolWriteSelectors) + { + size_t selectorStringSize = Block4x4Encoding::PIXELS*4; + char *redSelectors = new char[selectorStringSize]; + char *grnSelectors = new char[selectorStringSize]; + memset(&redSelectors[0], 0, selectorStringSize); + memset(&grnSelectors[0], 0, selectorStringSize); + + if (a_pblock->GetEncodingMode() != Block4x4Encoding::MODE_RG11) + { + fprintf(a_pfile, "selectors("); + } + + for (unsigned int uiPixel = 0; uiPixel < Block4x4Encoding::PIXELS; uiPixel++) + { + if (a_pblock->GetEncodingMode() == Block4x4Encoding::MODE_R11) + { + fprintf(a_pfile, "%u", pencoding_r11->GetRedSelectors()[uiPixel]); + } + else if (a_pblock->GetEncodingMode() == Block4x4Encoding::MODE_RG11) + { + char temp[2]; + sprintf(temp,"%d",pencoding_rg11->GetRedSelectors()[uiPixel]); + strcat(redSelectors,temp); + sprintf(temp, "%d", pencoding_rg11->GetGrnSelectors()[uiPixel]); + strcat(grnSelectors, temp); + + strcat(redSelectors, ",\0"); + strcat(grnSelectors, ",\0"); + + fprintf(a_pfile, "pixel[%d]:(%f,%f,%f)\n", uiPixel, + pencoding->GetDecodedColors()[uiPixel].fR, + pencoding->GetDecodedColors()[uiPixel].fG, + pencoding->GetDecodedColors()[uiPixel].fB); + + } + else + { + fprintf(a_pfile, "%u", pencoding_rgb8->GetSelectors()[uiPixel]); + } + } + if (a_pblock->GetEncodingMode() == Block4x4Encoding::MODE_RG11) + { + redSelectors[selectorStringSize - 1] = '\0'; + grnSelectors[selectorStringSize - 1] = '\0'; + fprintf(a_pfile, "selectors(red: %s)\n", redSelectors); + fprintf(a_pfile, "selectors(grn: %s)\n,", grnSelectors); + delete[] redSelectors; + redSelectors = NULL; + delete[] grnSelectors; + grnSelectors = NULL; + } + else + { + fprintf(a_pfile, ")"); + } + } + + fprintf(a_pfile, "\n"); + + } + + // ---------------------------------------------------------------------------------------------------- + // +} diff --git a/deps/etc2comp/EtcTool/EtcComparison.h b/deps/etc2comp/EtcTool/EtcComparison.h new file mode 100644 index 0000000000..3e9f3ccd59 --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcComparison.h @@ -0,0 +1,61 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcColor.h" +#include + +namespace Etc +{ + class Analysis; + class Image; + class Block4x4; + + class Comparison + { + public: + + Comparison(Analysis *a_panalysisParent, unsigned int a_uiIndex, const char *a_pstrFilename, int a_iPixelX = -1, int a_iPixelY = -1); + + private: + + static const float ERROR_EPSILON; + + void SetName(void); + + void DrawImageComparison(Image *a_pimageUnderTest); + + void DrawBlockComparison2x(Block4x4 *a_pblockUnderTest, + ColorR8G8B8A8 *a_pargba8Output, + unsigned int a_uiOutputWidth, + float a_fErrorBlockUnderTest, + float a_fErrorBlockReference); + + void WriteLogFile(void); + void WriteBetterOrWorseBlocks(FILE *a_pfile, bool a_boolWorse); + void WriteBlockInfo(FILE *a_pfile, Block4x4 *a_pblock, const char *a_pstrEncoder); + + Analysis *m_panalysisParent; + unsigned int m_uiIndex; + char *m_pstrFilename; + Image *m_pimage; + char *m_pstrName; + char *m_pstrOutputFolder; + }; + + +} diff --git a/deps/etc2comp/EtcTool/EtcFile.cpp b/deps/etc2comp/EtcTool/EtcFile.cpp new file mode 100644 index 0000000000..831a3aac45 --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcFile.cpp @@ -0,0 +1,390 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS (1) +#endif + +#include "EtcConfig.h" + + +#include "EtcFile.h" + +#include "EtcFileHeader.h" +#include "EtcColor.h" +#include "Etc.h" +#include "EtcBlock4x4EncodingBits.h" + +#include +#include +#include +#include + +using namespace Etc; + +// ---------------------------------------------------------------------------------------------------- +// +File::File(const char *a_pstrFilename, Format a_fileformat, Image::Format a_imageformat, + unsigned char *a_paucEncodingBits, unsigned int a_uiEncodingBitsBytes, + unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight, + unsigned int a_uiExtendedWidth, unsigned int a_uiExtendedHeight) +{ + if (a_pstrFilename == nullptr) + { + m_pstrFilename = const_cast(""); + } + else + { + m_pstrFilename = new char[strlen(a_pstrFilename) + 1]; + strcpy(m_pstrFilename, a_pstrFilename); + } + + m_fileformat = a_fileformat; + if (m_fileformat == Format::INFER_FROM_FILE_EXTENSION) + { + // ***** TODO: add this later ***** + m_fileformat = Format::KTX; + } + + m_imageformat = a_imageformat; + + m_uiNumMipmaps = 1; + m_pMipmapImages = new RawImage[m_uiNumMipmaps]; + m_pMipmapImages[0].paucEncodingBits = std::shared_ptr(a_paucEncodingBits, [](unsigned char *p) { delete[] p; } ); + m_pMipmapImages[0].uiEncodingBitsBytes = a_uiEncodingBitsBytes; + m_pMipmapImages[0].uiExtendedWidth = a_uiExtendedWidth; + m_pMipmapImages[0].uiExtendedHeight = a_uiExtendedHeight; + + m_uiSourceWidth = a_uiSourceWidth; + m_uiSourceHeight = a_uiSourceHeight; + + switch (m_fileformat) + { + case Format::PKM: + m_pheader = new FileHeader_Pkm(this); + break; + + case Format::KTX: + m_pheader = new FileHeader_Ktx(this); + break; + + default: + assert(0); + break; + } + +} + +// ---------------------------------------------------------------------------------------------------- +// +File::File(const char *a_pstrFilename, Format a_fileformat, Image::Format a_imageformat, + unsigned int a_uiNumMipmaps, RawImage *a_pMipmapImages, + unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight) +{ + if (a_pstrFilename == nullptr) + { + m_pstrFilename = const_cast(""); + } + else + { + m_pstrFilename = new char[strlen(a_pstrFilename) + 1]; + strcpy(m_pstrFilename, a_pstrFilename); + } + + m_fileformat = a_fileformat; + if (m_fileformat == Format::INFER_FROM_FILE_EXTENSION) + { + // ***** TODO: add this later ***** + m_fileformat = Format::KTX; + } + + m_imageformat = a_imageformat; + + m_uiNumMipmaps = a_uiNumMipmaps; + m_pMipmapImages = new RawImage[m_uiNumMipmaps]; + + for(unsigned int mip = 0; mip < m_uiNumMipmaps; mip++) + { + m_pMipmapImages[mip] = a_pMipmapImages[mip]; + } + + m_uiSourceWidth = a_uiSourceWidth; + m_uiSourceHeight = a_uiSourceHeight; + + switch (m_fileformat) + { + case Format::PKM: + m_pheader = new FileHeader_Pkm(this); + break; + + case Format::KTX: + m_pheader = new FileHeader_Ktx(this); + break; + + default: + assert(0); + break; + } + +} + +// ---------------------------------------------------------------------------------------------------- +// +File::File(const char *a_pstrFilename, Format a_fileformat) +{ + if (a_pstrFilename == nullptr) + { + return; + } + else + { + m_pstrFilename = new char[strlen(a_pstrFilename) + 1]; + strcpy(m_pstrFilename, a_pstrFilename); + } + + m_fileformat = a_fileformat; + if (m_fileformat == Format::INFER_FROM_FILE_EXTENSION) + { + // ***** TODO: add this later ***** + m_fileformat = Format::KTX; + } + + FILE *pfile = fopen(m_pstrFilename, "rb"); + if (pfile == nullptr) + { + printf("ERROR: Couldn't open %s", m_pstrFilename); + exit(1); + } + fseek(pfile, 0, SEEK_END); + unsigned int fileSize = ftell(pfile); + fseek(pfile, 0, SEEK_SET); + size_t szResult; + + m_pheader = new FileHeader_Ktx(this); + szResult = fread( ((FileHeader_Ktx*)m_pheader)->GetData(), 1, sizeof(FileHeader_Ktx::Data), pfile); + assert(szResult > 0); + + m_uiNumMipmaps = 1; + m_pMipmapImages = new RawImage[m_uiNumMipmaps]; + + if (((FileHeader_Ktx*)m_pheader)->GetData()->m_u32BytesOfKeyValueData > 0) + fseek(pfile, ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32BytesOfKeyValueData, SEEK_CUR); + szResult = fread(&m_pMipmapImages->uiEncodingBitsBytes, 1, sizeof(unsigned int), pfile); + assert(szResult > 0); + + m_pMipmapImages->paucEncodingBits = std::shared_ptr(new unsigned char[m_pMipmapImages->uiEncodingBitsBytes], [](unsigned char *p) { delete[] p; } ); + assert(ftell(pfile) + m_pMipmapImages->uiEncodingBitsBytes <= fileSize); + szResult = fread(m_pMipmapImages->paucEncodingBits.get(), 1, m_pMipmapImages->uiEncodingBitsBytes, pfile); + assert(szResult == m_pMipmapImages->uiEncodingBitsBytes); + + uint32_t uiInternalFormat = ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32GlInternalFormat; + uint32_t uiBaseInternalFormat = ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32GlBaseInternalFormat; + + if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC1_RGB8 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC1_RGB8) + { + m_imageformat = Image::Format::ETC1; + } + else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_RGB8 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RGB8) + { + m_imageformat = Image::Format::RGB8; + } + else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_RGB8A1 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RGB8A1) + { + m_imageformat = Image::Format::RGB8A1; + } + else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_RGBA8 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RGBA8) + { + m_imageformat = Image::Format::RGBA8; + } + else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_R11 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_R11) + { + m_imageformat = Image::Format::R11; + } + else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_SIGNED_R11 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_R11) + { + m_imageformat = Image::Format::SIGNED_R11; + } + else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_RG11 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RG11) + { + m_imageformat = Image::Format::RG11; + } + else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_SIGNED_RG11 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RG11) + { + m_imageformat = Image::Format::SIGNED_RG11; + } + else + { + m_imageformat = Image::Format::UNKNOWN; + } + + m_uiSourceWidth = ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32PixelWidth; + m_uiSourceHeight = ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32PixelHeight; + m_pMipmapImages->uiExtendedWidth = Image::CalcExtendedDimension((unsigned short)m_uiSourceWidth); + m_pMipmapImages->uiExtendedHeight = Image::CalcExtendedDimension((unsigned short)m_uiSourceHeight); + + unsigned int uiBlocks = m_pMipmapImages->uiExtendedWidth * m_pMipmapImages->uiExtendedHeight / 16; + Block4x4EncodingBits::Format encodingbitsformat = Image::DetermineEncodingBitsFormat(m_imageformat); + unsigned int expectedbytes = uiBlocks * Block4x4EncodingBits::GetBytesPerBlock(encodingbitsformat); + assert(expectedbytes == m_pMipmapImages->uiEncodingBitsBytes); + + fclose(pfile); +} + +File::~File() +{ + if (m_pMipmapImages != nullptr) + { + delete [] m_pMipmapImages; + } + + if(m_pstrFilename != nullptr) + { + delete[] m_pstrFilename; + m_pstrFilename = nullptr; + } + if (m_pheader != nullptr) + { + delete m_pheader; + m_pheader = nullptr; + } +} + +void File::UseSingleBlock(int a_iPixelX, int a_iPixelY) +{ + if (a_iPixelX <= -1 || a_iPixelY <= -1) + return; + if (a_iPixelX >(int) m_uiSourceWidth) + { + //if we are using a ktx thats the size of a single block or less + //then make sure we use the 4x4 image as the single block + if (m_uiSourceWidth <= 4) + { + a_iPixelX = 0; + } + else + { + printf("blockAtHV: H coordinate out of range, capped to image width\n"); + a_iPixelX = m_uiSourceWidth - 1; + } + } + if (a_iPixelY >(int) m_uiSourceHeight) + { + //if we are using a ktx thats the size of a single block or less + //then make sure we use the 4x4 image as the single block + if (m_uiSourceHeight <= 4) + { + a_iPixelY= 0; + } + else + { + printf("blockAtHV: V coordinate out of range, capped to image height\n"); + a_iPixelY = m_uiSourceHeight - 1; + } + } + + unsigned int origWidth = m_uiSourceWidth; + unsigned int origHeight = m_uiSourceHeight; + + m_uiSourceWidth = 4; + m_uiSourceHeight = 4; + + Block4x4EncodingBits::Format encodingbitsformat = Image::DetermineEncodingBitsFormat(m_imageformat); + unsigned int uiEncodingBitsBytesPerBlock = Block4x4EncodingBits::GetBytesPerBlock(encodingbitsformat); + + int numMipmaps = 1; + RawImage* pMipmapImages = new RawImage[numMipmaps]; + pMipmapImages[0].uiExtendedWidth = Image::CalcExtendedDimension((unsigned short)m_uiSourceWidth); + pMipmapImages[0].uiExtendedHeight = Image::CalcExtendedDimension((unsigned short)m_uiSourceHeight); + pMipmapImages[0].uiEncodingBitsBytes = 0; + pMipmapImages[0].paucEncodingBits = std::shared_ptr(new unsigned char[uiEncodingBitsBytesPerBlock], [](unsigned char *p) { delete[] p; }); + + //block position in pixels + // remove the bottom 2 bits to get the block coordinates + unsigned int iBlockPosX = (a_iPixelX & 0xFFFFFFFC); + unsigned int iBlockPosY = (a_iPixelY & 0xFFFFFFFC); + + int numXBlocks = (origWidth / 4); + int numYBlocks = (origHeight / 4); + + + // block location + //int iBlockX = (a_iPixelX % 4) == 0 ? a_iPixelX / 4.0f : (a_iPixelX / 4) + 1; + //int iBlockY = (a_iPixelY % 4) == 0 ? a_iPixelY / 4.0f : (a_iPixelY / 4) + 1; + //m_paucEncodingBits += ((iBlockY * numXBlocks) + iBlockX) * uiEncodingBitsBytesPerBlock; + + + unsigned int num = numXBlocks*numYBlocks; + unsigned int uiH = 0, uiV = 0; + unsigned char* pEncodingBits = m_pMipmapImages[0].paucEncodingBits.get(); + for (unsigned int uiBlock = 0; uiBlock < num; uiBlock++) + { + if (uiH == iBlockPosX && uiV == iBlockPosY) + { + memcpy(pMipmapImages[0].paucEncodingBits.get(),pEncodingBits, uiEncodingBitsBytesPerBlock); + break; + } + pEncodingBits += uiEncodingBitsBytesPerBlock; + uiH += 4; + + if (uiH >= origWidth) + { + uiH = 0; + uiV += 4; + } + } + + delete [] m_pMipmapImages; + m_pMipmapImages = pMipmapImages; +} +// ---------------------------------------------------------------------------------------------------- +// +void File::Write() +{ + + FILE *pfile = fopen(m_pstrFilename, "wb"); + if (pfile == nullptr) + { + printf("Error: couldn't open Etc file (%s)\n", m_pstrFilename); + exit(1); + } + + m_pheader->Write(pfile); + + for(unsigned int mip = 0; mip < m_uiNumMipmaps; mip++) + { + if(m_fileformat == Format::KTX) + { + // Write u32 image size + uint32_t u32ImageSize = m_pMipmapImages[mip].uiEncodingBitsBytes; + uint32_t szBytesWritten = fwrite(&u32ImageSize, 1, sizeof(u32ImageSize), pfile); + assert(szBytesWritten == sizeof(u32ImageSize)); + } + + unsigned int iResult = (int)fwrite(m_pMipmapImages[mip].paucEncodingBits.get(), 1, m_pMipmapImages[mip].uiEncodingBitsBytes, pfile); + if (iResult != m_pMipmapImages[mip].uiEncodingBitsBytes) + { + printf("Error: couldn't write Etc file (%s)\n", m_pstrFilename); + exit(1); + } + } + + fclose(pfile); + +} + +// ---------------------------------------------------------------------------------------------------- +// + diff --git a/deps/etc2comp/EtcTool/EtcFile.h b/deps/etc2comp/EtcTool/EtcFile.h new file mode 100644 index 0000000000..69bf3b2d3a --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcFile.h @@ -0,0 +1,136 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcColorFloatRGBA.h" +#include "EtcImage.h" +#include "Etc.h" + +namespace Etc +{ + class FileHeader; + class SourceImage; + + class File + { + public: + + enum class Format + { + INFER_FROM_FILE_EXTENSION, + PKM, + KTX, + }; + + File(const char *a_pstrFilename, Format a_fileformat, Image::Format a_imageformat, + unsigned char *a_paucEncodingBits, unsigned int a_uiEncodingBitsBytes, + unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight, + unsigned int a_uiExtendedWidth, unsigned int a_uiExtendedHeight); + + File(const char *a_pstrFilename, Format a_fileformat, Image::Format a_imageformat, + unsigned int a_uiNumMipmaps, RawImage *pMipmapImages, + unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight ); + + File(const char *a_pstrFilename, Format a_fileformat); + ~File(); + const char *GetFilename(void) { return m_pstrFilename; } + + void Read(const char *a_pstrFilename); + void Write(void); + + inline unsigned int GetSourceWidth(void) + { + return m_uiSourceWidth; + } + + inline unsigned int GetSourceHeight(void) + { + return m_uiSourceHeight; + } + + inline unsigned int GetExtendedWidth(unsigned int mipmapIndex = 0) + { + if (mipmapIndex < m_uiNumMipmaps) + { + return m_pMipmapImages[mipmapIndex].uiExtendedWidth; + } + else + { + return 0; + } + } + + inline unsigned int GetExtendedHeight(unsigned int mipmapIndex = 0) + { + if (mipmapIndex < m_uiNumMipmaps) + { + return m_pMipmapImages[mipmapIndex].uiExtendedHeight; + } + else + { + return 0; + } + } + + inline Image::Format GetImageFormat() + { + return m_imageformat; + } + + inline unsigned int GetEncodingBitsBytes(unsigned int mipmapIndex = 0) + { + if (mipmapIndex < m_uiNumMipmaps) + { + return m_pMipmapImages[mipmapIndex].uiEncodingBitsBytes; + } + else + { + return 0; + } + } + + inline unsigned char* GetEncodingBits(unsigned int mipmapIndex = 0) + { + if( mipmapIndex < m_uiNumMipmaps) + { + return m_pMipmapImages[mipmapIndex].paucEncodingBits.get(); + } + else + { + return nullptr; + } + } + + inline unsigned int GetNumMipmaps() + { + return m_uiNumMipmaps; + } + + void UseSingleBlock(int a_iPixelX = -1, int a_iPixelY = -1); + private: + + char *m_pstrFilename; // includes directory path and file extension + Format m_fileformat; + Image::Format m_imageformat; + FileHeader *m_pheader; + unsigned int m_uiNumMipmaps; + RawImage* m_pMipmapImages; + unsigned int m_uiSourceWidth; + unsigned int m_uiSourceHeight; + }; + +} diff --git a/deps/etc2comp/EtcTool/EtcFileHeader.cpp b/deps/etc2comp/EtcTool/EtcFileHeader.cpp new file mode 100644 index 0000000000..f02fcab011 --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcFileHeader.cpp @@ -0,0 +1,185 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EtcFileHeader.h" + +#include "EtcBlock4x4EncodingBits.h" + +#include + +namespace Etc +{ + + // ---------------------------------------------------------------------------------------------------- + // + FileHeader_Pkm::FileHeader_Pkm(File *a_pfile) + { + m_pfile = a_pfile; + + static const char s_acMagicNumberData[4] = { 'P', 'K', 'M', ' ' }; + static const char s_acVersionData[2] = { '1', '0' }; + + for (unsigned int ui = 0; ui < sizeof(s_acMagicNumberData); ui++) + { + m_data.m_acMagicNumber[ui] = s_acMagicNumberData[ui]; + } + + for (unsigned int ui = 0; ui < sizeof(s_acVersionData); ui++) + { + m_data.m_acVersion[ui] = s_acVersionData[ui]; + } + + m_data.m_ucDataType_msb = 0; // ETC1_RGB_NO_MIPMAPS + m_data.m_ucDataType_lsb = 0; + + m_data.m_ucOriginalWidth_msb = (unsigned char)(m_pfile->GetSourceWidth() >> 8); + m_data.m_ucOriginalWidth_lsb = m_pfile->GetSourceWidth() & 0xFF; + m_data.m_ucOriginalHeight_msb = (unsigned char)(m_pfile->GetSourceHeight() >> 8); + m_data.m_ucOriginalHeight_lsb = m_pfile->GetSourceHeight() & 0xFF; + + m_data.m_ucExtendedWidth_msb = (unsigned char)(m_pfile->GetExtendedWidth() >> 8); + m_data.m_ucExtendedWidth_lsb = m_pfile->GetExtendedWidth() & 0xFF; + m_data.m_ucExtendedHeight_msb = (unsigned char)(m_pfile->GetExtendedHeight() >> 8); + m_data.m_ucExtendedHeight_lsb = m_pfile->GetExtendedHeight() & 0xFF; + + } + + // ---------------------------------------------------------------------------------------------------- + // + void FileHeader_Pkm::Write(FILE *a_pfile) + { + + fwrite(&m_data, sizeof(Data), 1, a_pfile); + + } + + // ---------------------------------------------------------------------------------------------------- + // + FileHeader_Ktx::FileHeader_Ktx(File *a_pfile) + { + m_pfile = a_pfile; + + static const uint8_t s_au8Itentfier[12] = + { + 0xAB, 0x4B, 0x54, 0x58, // first four bytes of Byte[12] identifier + 0x20, 0x31, 0x31, 0xBB, // next four bytes of Byte[12] identifier + 0x0D, 0x0A, 0x1A, 0x0A // final four bytes of Byte[12] identifier + }; + + for (unsigned int ui = 0; ui < sizeof(s_au8Itentfier); ui++) + { + m_data.m_au8Identifier[ui] = s_au8Itentfier[ui]; + } + + m_data.m_u32Endianness = 0x04030201; + m_data.m_u32GlType = 0; + m_data.m_u32GlTypeSize = 1; + m_data.m_u32GlFormat = 0; + + switch (m_pfile->GetImageFormat()) + { + case Image::Format::RGB8: + case Image::Format::SRGB8: + m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_RGB8; + m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RGB8; + break; + + case Image::Format::RGBA8: + case Image::Format::SRGBA8: + m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_RGBA8; + m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RGBA8; + break; + + case Image::Format::RGB8A1: + case Image::Format::SRGB8A1: + m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_RGB8A1; + m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RGB8A1; + break; + + case Image::Format::R11: + m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_R11; + m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_R11; + break; + + case Image::Format::SIGNED_R11: + m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_SIGNED_R11; + m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_R11; + break; + + case Image::Format::RG11: + m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_RG11; + m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RG11; + break; + + case Image::Format::SIGNED_RG11: + m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_SIGNED_RG11; + m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RG11; + break; + + default: + m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC1_RGB8; + m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC1_RGB8; + break; + } + + m_data.m_u32PixelWidth = 0; + m_data.m_u32PixelHeight = 0; + m_data.m_u32PixelDepth = 0; + m_data.m_u32NumberOfArrayElements = 0; + m_data.m_u32NumberOfFaces = 0; + m_data.m_u32BytesOfKeyValueData = 0; + + m_pkeyvaluepair = nullptr; + + m_u32Images = 0; + m_u32KeyValuePairs = 0; + + m_data.m_u32PixelWidth = m_pfile->GetSourceWidth(); + m_data.m_u32PixelHeight = m_pfile->GetSourceHeight(); + m_data.m_u32PixelDepth = 0; + m_data.m_u32NumberOfArrayElements = 0; + m_data.m_u32NumberOfFaces = 1; + m_data.m_u32NumberOfMipmapLevels = m_pfile->GetNumMipmaps(); + + } + + // ---------------------------------------------------------------------------------------------------- + // + void FileHeader_Ktx::Write(FILE *a_pfile) + { + size_t szBytesWritten; + + // Write header + szBytesWritten = fwrite(&m_data, 1, sizeof(Data), a_pfile); + assert(szBytesWritten == sizeof(Data)); + + // Write KeyAndValuePairs + if (m_u32KeyValuePairs) + { + fwrite(m_pkeyvaluepair, m_pkeyvaluepair->u32KeyAndValueByteSize, 1, a_pfile); + } + } + + // ---------------------------------------------------------------------------------------------------- + // + FileHeader_Ktx::Data *FileHeader_Ktx::GetData() + { + return &m_data; + } + + // ---------------------------------------------------------------------------------------------------- + // +} // namespace Etc diff --git a/deps/etc2comp/EtcTool/EtcFileHeader.h b/deps/etc2comp/EtcTool/EtcFileHeader.h new file mode 100644 index 0000000000..55a9cb5d9d --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcFileHeader.h @@ -0,0 +1,146 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcFile.h" +#include +#include + +namespace Etc +{ + + class Image; + + class FileHeader + { + public: + + virtual void Write(FILE *a_pfile) = 0; + File GetFile(); + virtual ~FileHeader(void) {} + protected: + + File *m_pfile; + }; + + // ---------------------------------------------------------------------------------------------------- + // + class FileHeader_Pkm : public FileHeader + { + public: + + FileHeader_Pkm(File *a_pfile); + + virtual void Write(FILE *a_pfile); + virtual ~FileHeader_Pkm(void) {} + private: + + typedef struct + { + char m_acMagicNumber[4]; + char m_acVersion[2]; + unsigned char m_ucDataType_msb; // e.g. ETC1_RGB_NO_MIPMAPS + unsigned char m_ucDataType_lsb; + unsigned char m_ucExtendedWidth_msb; // padded to 4x4 blocks + unsigned char m_ucExtendedWidth_lsb; + unsigned char m_ucExtendedHeight_msb; // padded to 4x4 blocks + unsigned char m_ucExtendedHeight_lsb; + unsigned char m_ucOriginalWidth_msb; + unsigned char m_ucOriginalWidth_lsb; + unsigned char m_ucOriginalHeight_msb; + unsigned char m_ucOriginalHeight_lsb; + } Data; + + Data m_data; + }; + + // ---------------------------------------------------------------------------------------------------- + // + class FileHeader_Ktx : public FileHeader + { + public: + + typedef struct + { + uint32_t u32KeyAndValueByteSize; + } KeyValuePair; + + typedef struct + { + uint8_t m_au8Identifier[12]; + uint32_t m_u32Endianness; + uint32_t m_u32GlType; + uint32_t m_u32GlTypeSize; + uint32_t m_u32GlFormat; + uint32_t m_u32GlInternalFormat; + uint32_t m_u32GlBaseInternalFormat; + uint32_t m_u32PixelWidth; + uint32_t m_u32PixelHeight; + uint32_t m_u32PixelDepth; + uint32_t m_u32NumberOfArrayElements; + uint32_t m_u32NumberOfFaces; + uint32_t m_u32NumberOfMipmapLevels; + uint32_t m_u32BytesOfKeyValueData; + } Data; + + enum class InternalFormat + { + ETC1_RGB8 = 0x8D64, + ETC1_ALPHA8 = ETC1_RGB8, + // + ETC2_R11 = 0x9270, + ETC2_SIGNED_R11 = 0x9271, + ETC2_RG11 = 0x9272, + ETC2_SIGNED_RG11 = 0x9273, + ETC2_RGB8 = 0x9274, + ETC2_SRGB8 = 0x9275, + ETC2_RGB8A1 = 0x9276, + ETC2_SRGB8_PUNCHTHROUGH_ALPHA1 = 0x9277, + ETC2_RGBA8 = 0x9278 + }; + + enum class BaseInternalFormat + { + ETC2_R11 = 0x1903, + ETC2_RG11 = 0x8227, + ETC1_RGB8 = 0x1907, + ETC1_ALPHA8 = ETC1_RGB8, + // + ETC2_RGB8 = 0x1907, + ETC2_RGB8A1 = 0x1908, + ETC2_RGBA8 = 0x1908, + }; + + FileHeader_Ktx(File *a_pfile); + + virtual void Write(FILE *a_pfile); + virtual ~FileHeader_Ktx(void) {} + + void AddKeyAndValue(KeyValuePair *a_pkeyvaluepair); + + Data* GetData(); + + private: + + Data m_data; + KeyValuePair *m_pkeyvaluepair; + + uint32_t m_u32Images; + uint32_t m_u32KeyValuePairs; + }; + +} // namespace Etc diff --git a/deps/etc2comp/EtcTool/EtcMemTest.cpp b/deps/etc2comp/EtcTool/EtcMemTest.cpp new file mode 100644 index 0000000000..24bdb91843 --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcMemTest.cpp @@ -0,0 +1,258 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EtcMemTest.h" +#include +#include +#include +#include +#include +using namespace std; + +#if defined(_WIN32) +#include +#include +// Use to convert bytes to KB +#define DIV 1024 + +// Specify the width of the field in which to print the numbers. +// The asterisk in the format specifier "%*I64d" takes an integer +// argument and uses it to pad and right justify the number. +#define WIDTH 7 + +size_t GetMemoryUsageAmount() +{ + //MEMORYSTATUS status; // if 64 bit version isnt working, use 32bit version + //GlobalMemoryStatus(&status); + /*MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + return (size_t)(status.ullAvailPhys / DIV);*/ + + PROCESS_MEMORY_COUNTERS_EX pmc; + DWORD ret = GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)); + if (ret == 0) + { + printf("GetProcessMemoryInfo failed with code %d\n", GetLastError()); + } + + return pmc.PrivateUsage; +} + +void PrintWindowsMemUsage() +{ + MEMORYSTATUSEX statex; + + statex.dwLength = sizeof(statex); + + GlobalMemoryStatusEx(&statex); + + printf("There is %*ld percent of memory in use.\n", + WIDTH, statex.dwMemoryLoad); + printf("There are %*I64d total KB of physical memory.\n", + WIDTH, statex.ullTotalPhys / DIV); + printf("There are %*I64d free KB of physical memory.\n", + WIDTH, statex.ullAvailPhys / DIV); + printf("There are %*I64d total KB of paging file.\n", + WIDTH, statex.ullTotalPageFile / DIV); + printf("There are %*I64d free KB of paging file.\n", + WIDTH, statex.ullAvailPageFile / DIV); + printf("There are %*I64d total KB of virtual memory.\n", + WIDTH, statex.ullTotalVirtual / DIV); + printf("There are %*I64d free KB of virtual memory.\n", + WIDTH, statex.ullAvailVirtual / DIV); + + // Show the amount of extended memory available. + + printf("There are %*I64d free KB of extended memory.\n", + WIDTH, statex.ullAvailExtendedVirtual / DIV); +} + + + + +int RunMemTest(bool verboseOutput, size_t numTestIterations) +{ + WIN32_FIND_DATA ffd; + srand(static_cast(time(NULL))); + HANDLE hFind = INVALID_HANDLE_VALUE; + + + vector allImages; + + string imagesDir = "D:\\source\\etc2Sourcetree\\TestImages\\"; + string outputDir = "C:\\Users\\BSI\\Desktop\\etc2comp\\"; + + hFind = FindFirstFile((imagesDir+"*").c_str(), &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + printf ("image dir doesnt exist: %s\n", imagesDir.c_str()); + return -1; + } + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + continue; + } + else + { + string filename = ffd.cFileName; + size_t found = filename.find_last_of("."); + string ext = filename.substr(found + 1); + if(ext != "png") + continue; + + allImages.push_back(filename); + } + } while (FindNextFile(hFind, &ffd) != 0); + FindClose(hFind); + + size_t oldMemSize = 0; + size_t curMemSize = 0; + size_t fileIndex = 0; + int encodingType = 2;//0 = c , 1= both c and c++ encoding, 2 = c++ encoding + bool encodeRandomImage = true; + for (size_t i = 0; i < numTestIterations; i++) + { + encodingType = rand()%3; + + printf("--------------iteration %zu-----------\n",i+1); + int randomNumber = 1 + rand() % 11; + //Image::Format format = (Image::Format)randomNumber; + Image::Format format = Image::Format::RGBA8; + + randomNumber = rand() % 4; + ErrorMetric e_ErrMetric = (ErrorMetric)randomNumber; + + randomNumber = rand() % 100; + float curEffort = (float)randomNumber; + + int jobs = 1 + rand() % 8; + long memDiff = 0; + + if (verboseOutput) + { + printf(" effort = %.f\n", curEffort); + printf(" encoding = %s\n", Image::EncodingFormatToString(format)); + printf(" error metric: %s\n", ErrorMetricToString(e_ErrMetric)); + printf("jobs: %d\n", jobs); + } + + oldMemSize = curMemSize; + curMemSize = GetMemoryUsageAmount(); + memDiff = (long)curMemSize - (long)oldMemSize; + printf("mem diff: %ukb\n", memDiff); + + string curFile = imagesDir + allImages[fileIndex]; + string outFile = outputDir + allImages[fileIndex] +".ktx"; + printf("file: %s\n", curFile.c_str()); + + if (!encodeRandomImage) + { + if (fileIndex >= allImages.size() - 1) + fileIndex = 0; + else + fileIndex++; + } + else + { + fileIndex = rand()%allImages.size(); + } + SourceImage sourceimage(curFile.c_str(), -1, -1); + + unsigned int uiSourceWidth = sourceimage.GetWidth(); + unsigned int uiSourceHeight = sourceimage.GetHeight(); + //char *imgonnaleak = new char[1000]; + + unsigned char *paucEncodingBits; + unsigned int uiEncodingBitsBytes; + unsigned int uiExtendedWidth; + unsigned int uiExtendedHeight; + int iEncodingTime_ms; + //////////////C INTERFACE FIRST////////////////////// + if (encodingType < 2) + { + if (verboseOutput) + { + printf("start C interface Encoding:\n"); + } + + Etc::Encode((float *)sourceimage.GetPixels(), + uiSourceWidth, uiSourceHeight, + format, + e_ErrMetric, + curEffort, + jobs, + MAX_JOBS, + &paucEncodingBits, &uiEncodingBitsBytes, + &uiExtendedWidth, &uiExtendedHeight, + &iEncodingTime_ms, verboseOutput); + if (verboseOutput) + { + printf(" encode time = %dms\n", iEncodingTime_ms); + printf("EncodedImage: %s\n", outFile.c_str()); + } + Etc::File C_interfaceEtcfile(outFile.c_str(), Etc::File::Format::INFER_FROM_FILE_EXTENSION, + format, + paucEncodingBits, uiEncodingBitsBytes, + uiSourceWidth, uiSourceHeight, + uiExtendedWidth, uiExtendedHeight); + C_interfaceEtcfile.Write(); + + oldMemSize = curMemSize; + curMemSize = GetMemoryUsageAmount(); + memDiff = (long)curMemSize - (long)oldMemSize; + printf("mem diff again: %ukb\n", memDiff); + } + //////////////C++ INTERFACE /////////////////////////// + if (encodingType > 0) + { + if (verboseOutput) + { + printf("start C++ Encoding:\n"); + } + Etc::Image image((float *)sourceimage.GetPixels(), + uiSourceWidth, uiSourceHeight, + e_ErrMetric); + image.m_bVerboseOutput = verboseOutput; + + Etc::Image::EncodingStatus encStatus = Etc::Image::EncodingStatus::SUCCESS; + + encStatus = image.Encode(format, e_ErrMetric, curEffort, jobs, MAX_JOBS); + if (verboseOutput) + { + printf(" encode time = %dms\n", image.GetEncodingTimeMs()); + printf("EncodedImage: %s\n", outFile.c_str()); + printf("status bitfield: %u\n", encStatus); + } + Etc::File etcfile(outFile.c_str(), Etc::File::Format::INFER_FROM_FILE_EXTENSION, + format, + image.GetEncodingBits(), image.GetEncodingBitsBytes(), + image.GetSourceWidth(), image.GetSourceHeight(), + image.GetExtendedWidth(), image.GetExtendedHeight()); + + etcfile.Write(); + } + + } + + return 0; +} + +#endif diff --git a/deps/etc2comp/EtcTool/EtcMemTest.h b/deps/etc2comp/EtcTool/EtcMemTest.h new file mode 100644 index 0000000000..080849ac36 --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcMemTest.h @@ -0,0 +1,61 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcConfig.h" + +#include "Etc.h" + +#include "EtcSourceImage.h" +#include "EtcFile.h" +#include "EtcMath.h" +#include "EtcImage.h" +#include "EtcErrorMetric.h" +#include "EtcBlock4x4EncodingBits.h" +#include "EtcFileHeader.h" + +#include +#include +#include + +#define MAX_JOBS 1024 +using namespace Etc; + +int RunMemTest(bool verboseOutput, size_t numTestIterations); + + + + + +/*int getMem() +{ + +int tSize = 0, resident = 0, share = 0; +ifstream buffer("/proc/self/statm"); +buffer >> tSize >> resident >> share; +buffer.close(); + +long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages +double rss = resident * page_size_kb; +cout << "RSS - " << rss << " kB\n"; + +double shared_mem = share * page_size_kb; +cout << "Shared Memory - " << shared_mem << " kB\n"; + +cout << "Private Memory - " << rss - shared_mem << "kB\n"; +return 0; +}*/ diff --git a/deps/etc2comp/EtcTool/EtcSourceImage.cpp b/deps/etc2comp/EtcTool/EtcSourceImage.cpp new file mode 100644 index 0000000000..adef6877ac --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcSourceImage.cpp @@ -0,0 +1,292 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS (1) +#endif + +#include "EtcConfig.h" +#include "EtcSourceImage.h" +#include "Etc.h" + +#if USE_STB_IMAGE_LOAD +#include "stb_image.h" +#endif + +#include +#include +#include +#include + +#include "lodepng.h" + +namespace Etc +{ + + // ---------------------------------------------------------------------------------------------------- + // + SourceImage::SourceImage(const char *a_pstrFilename, int a_iPixelX, int a_iPixelY) + { + m_pstrFilename = nullptr; + m_pstrName = nullptr; + m_pstrFileExtension = nullptr; + m_uiWidth = 0; + m_uiHeight = 0; + m_pafrgbaPixels = nullptr; + + SetName(a_pstrFilename); + + Read(a_iPixelX, a_iPixelY); + } + + // ---------------------------------------------------------------------------------------------------- + // + SourceImage::SourceImage(ColorFloatRGBA *a_pafrgbaSource, + unsigned int a_uiSourceWidth, + unsigned int a_uiSourceHeight) + { + m_pstrFilename = nullptr; + m_pstrName = nullptr; + m_pstrFileExtension = nullptr; + m_uiWidth = a_uiSourceWidth; + m_uiHeight = a_uiSourceHeight; + m_pafrgbaPixels = a_pafrgbaSource; + + } + // ---------------------------------------------------------------------------------------------------- + // + SourceImage::~SourceImage() + { + if (m_pstrFilename != nullptr) + { + delete[] m_pstrFilename; + m_pstrFilename = nullptr; + } + + if (m_pstrName != nullptr) + { + delete[] m_pstrName; + m_pstrName = nullptr; + m_pstrFileExtension = nullptr; + } + + if (m_pafrgbaPixels != nullptr) + { + delete[] m_pafrgbaPixels; + m_pafrgbaPixels = nullptr; + } + m_uiWidth = 0; + m_uiHeight = 0; + } + // ---------------------------------------------------------------------------------------------------- + // + void SourceImage::Read(int a_iPixelX, int a_iPixelY) + { + unsigned char* paucPixels = nullptr; + + int iWidth = 0; + int iHeight = 0; + bool bool16BitImage = false; + +#if USE_STB_IMAGE_LOAD + int iBitsPerPixel; + //if stb_iamge is available, only use it to load files other than png + char *fileExt = strrchr(m_pstrFilename, '.'); + + if (strcmp(fileExt, ".png") != 0) + { + paucPixels = stbi_load(m_pstrFilename, &iWidth, &iHeight, &iBitsPerPixel, 4); + + if (paucPixels == nullptr) + { + printf("stb_image error %s\n", stbi_failure_reason()); + assert(0); + exit(1); + } + } +#endif + + if (paucPixels == nullptr) + { + //we can load 8 or 16 bit pngs + int iBitDepth = 16; + int error = lodepng_decode_file(&paucPixels, + (unsigned int*)&iWidth, (unsigned int*)&iHeight, + m_pstrFilename, + LCT_RGBA, iBitDepth); + + bool16BitImage = (iBitDepth == 16) ? true : false; + if (error) + { + printf("lodePNG error %u: %s\n", error, lodepng_error_text(error)); + assert(0); + exit(1); + } + } + + //the pixel cords for the top left corner of the block + int iBlockX = 0; + int iBlockY = 0; + if (a_iPixelX > -1 && a_iPixelY > -1) + { + // in 1 block mode, we basically will have an img thats 4x4 + m_uiWidth = 4; + m_uiHeight = 4; + + if(a_iPixelX > iWidth) + a_iPixelX = iWidth; + if (a_iPixelY > iHeight) + a_iPixelY = iHeight; + + // remove the bottom 2 bits to get the block coordinates + iBlockX = (a_iPixelX & 0xFFFFFFFC); + iBlockY = (a_iPixelY & 0xFFFFFFFC); + } + else + { + m_uiWidth = iWidth; + m_uiHeight = iHeight; + } + + m_pafrgbaPixels = new ColorFloatRGBA[m_uiWidth * m_uiHeight]; + assert(m_pafrgbaPixels); + + int iBytesPerPixel = bool16BitImage ? 8 : 4; + unsigned char *pucPixel; // = &paucPixels[(iBlockY * iWidth + iBlockX) * iBytesPerPixel]; + ColorFloatRGBA *pfrgbaPixel = m_pafrgbaPixels; + + // convert pixels from RGBA* to ColorFloatRGBA + for (unsigned int uiV = iBlockY; uiV < (iBlockY+m_uiHeight); ++uiV) + { + // reset coordinate for each row + pucPixel = &paucPixels[(uiV * iWidth + iBlockX) * iBytesPerPixel]; + + // read each row + for (unsigned int uiH = iBlockX; uiH < (iBlockX+m_uiWidth); ++uiH) + { + if (bool16BitImage) + { + unsigned short ushR = (pucPixel[0]<<8) + pucPixel[1]; + unsigned short ushG = (pucPixel[2]<<8) + pucPixel[3]; + unsigned short ushB = (pucPixel[4]<<8) + pucPixel[5]; + unsigned short ushA = (pucPixel[6]<<8) + pucPixel[7]; + + *pfrgbaPixel++ = ColorFloatRGBA((float)ushR / 65535.0f, + (float)ushG / 65535.0f, + (float)ushB / 65535.0f, + (float)ushA / 65535.0f); + } + else + { + *pfrgbaPixel++ = ColorFloatRGBA::ConvertFromRGBA8(pucPixel[0], pucPixel[1], + pucPixel[2], pucPixel[3]); + } + + pucPixel += iBytesPerPixel; + } + } + +#if USE_STB_IMAGE_LOAD + stbi_image_free(paucPixels); +#else + free(paucPixels); +#endif + } + + // ---------------------------------------------------------------------------------------------------- + // sets m_pstrFilename, m_pstrName and m_pstrFileExtension + // + void SourceImage::SetName(const char *a_pstrFilename) + { + if (a_pstrFilename == nullptr) + { + return; + } + + m_pstrFilename = new char[strlen(a_pstrFilename) + 1]; + strcpy(m_pstrFilename, a_pstrFilename); + + m_pstrName = new char[strlen(m_pstrFilename) + 1]; + + // ignore directory path + char *pcLastSlash = strrchr(m_pstrFilename, '/'); + char *pcLastBackSlash = strrchr(m_pstrFilename, '\\'); + if (pcLastSlash == nullptr && pcLastBackSlash == nullptr) + { + strcpy(m_pstrName, m_pstrFilename); + } + else if (pcLastSlash > pcLastBackSlash) + { + strcpy(m_pstrName, pcLastSlash + 1); + } + else + { + strcpy(m_pstrName, pcLastBackSlash + 1); + } + + // find file extension and remove it from image name + char *pcLastPeriod = strrchr(m_pstrName, '.'); + if (pcLastPeriod != nullptr) + { + m_pstrFileExtension = pcLastPeriod + 1; + *strrchr(m_pstrName, '.') = 0; + } + + } + + // ---------------------------------------------------------------------------------------------------- + // + void SourceImage::NormalizeXYZ(void) + { + int iPixels = m_uiWidth * m_uiHeight; + + ColorFloatRGBA *pfrgbaPixel = m_pafrgbaPixels; + for (int iPixel = 0; iPixel < iPixels; iPixel++) + { + float fX = 2.0f*pfrgbaPixel->fR - 1.0f; + float fY = 2.0f*pfrgbaPixel->fG - 1.0f; + float fZ = 2.0f*pfrgbaPixel->fB - 1.0f; + + float fLength2 = fX*fX + fY*fY + fZ*fZ; + + if (fLength2 == 0.0f) + { + pfrgbaPixel->fR = 1.0f; + pfrgbaPixel->fG = 0.0f; + pfrgbaPixel->fB = 0.0f; + } + else + { + float fLength = sqrtf(fLength2); + + float fNormalizedX = fX / fLength; + float fNormalizedY = fY / fLength; + float fNormalizedZ = fZ / fLength; + + pfrgbaPixel->fR = 0.5f * (fNormalizedX + 1.0f); + pfrgbaPixel->fG = 0.5f * (fNormalizedY + 1.0f); + pfrgbaPixel->fB = 0.5f * (fNormalizedZ + 1.0f); + } + + pfrgbaPixel++; + } + + } + + // ---------------------------------------------------------------------------------------------------- + // + +} // namespace Etc diff --git a/deps/etc2comp/EtcTool/EtcSourceImage.h b/deps/etc2comp/EtcTool/EtcSourceImage.h new file mode 100644 index 0000000000..b6fd2567d6 --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcSourceImage.h @@ -0,0 +1,93 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcColorFloatRGBA.h" + +namespace Etc +{ + + class SourceImage + { + public: + + SourceImage(const char *a_pstrFilename, int a_iPixelX = -1, int a_iPixelY = -1); + + SourceImage(ColorFloatRGBA *a_pafrgbaSource, + unsigned int a_uiSourceWidth, + unsigned int a_uiSourceHeight); + + ~SourceImage(); + + void SetName(const char *a_pstrFilename); + + void NormalizeXYZ(void); + + inline const char *GetFilename(void) + { + return m_pstrFilename; + } + + inline const char *GetName(void) + { + return m_pstrName; + } + + inline const char *GetFileExtension(void) + { + return m_pstrFileExtension; + } + + inline unsigned int GetWidth(void) + { + return m_uiWidth; + } + + inline unsigned int GetHeight(void) + { + return m_uiHeight; + } + + inline ColorFloatRGBA * GetPixels(void) + { + return m_pafrgbaPixels; + } + + inline ColorFloatRGBA * GetPixel(unsigned int a_uiColumn, unsigned int a_uiRow) + { + if (m_pafrgbaPixels == nullptr) + { + return nullptr; + } + + return &m_pafrgbaPixels[a_uiRow*m_uiWidth + a_uiColumn]; + } + + private: + + void Read(int a_iPixelX = -1, int a_iPixelY = -1); + + char *m_pstrFilename; // includes directory path and file extension + char *m_pstrName; // file name with directory path and file extension removed + char *m_pstrFileExtension; + unsigned int m_uiWidth; // not necessarily block aligned + unsigned int m_uiHeight; // not necessarily block aligned + ColorFloatRGBA *m_pafrgbaPixels; + + }; + +} // namespace Etc diff --git a/deps/etc2comp/EtcTool/EtcTool.cpp b/deps/etc2comp/EtcTool/EtcTool.cpp new file mode 100644 index 0000000000..9f1ef12ee3 --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcTool.cpp @@ -0,0 +1,833 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS (1) +#endif +/* +since this code will be used on a wide varity of platforms and configurations +its important to have some sort of sanity check for the amount of threads that can be used. +change this macro to suit your configuration. This will be the maximum amount of threads +that can be created. +*/ +#define MAX_JOBS 1024 + +#define RUN_MEM_TEST 0 + +#include "EtcConfig.h" + +#include "Etc.h" +#include "EtcFilter.h" + +#include "EtcTool.h" +#include "EtcSourceImage.h" +#include "EtcFile.h" +#include "EtcMath.h" +#include "EtcImage.h" +#include "EtcErrorMetric.h" +#include "EtcBlock4x4EncodingBits.h" + +#include "EtcAnalysis.h" + +#include +#include +#include + +using namespace Etc; + +#if ETC_WINDOWS +const char *ETC_MKDIR_COMMAND = "mkdir"; + +int strcasecmp(const char *s1, const char *s2) +{ + return _stricmp(s1, s2); +} +#else +const char *ETC_MKDIR_COMMAND = "mkdir -p"; +#endif + +#if RUN_MEM_TEST +#include "EtcMemTest.h" +#endif + +class Commands +{ +public: + + static const unsigned int MIN_JOBS = 8; + + Commands(void) + { + pstrSourceFilename = nullptr; + pstrOutputFilename = nullptr; + format = Image::Format::DEFAULT; + pstrAnalysisDirectory = nullptr; + uiComparisons = 0; + for (unsigned int uiComparison = 0; uiComparison < Analysis::MAX_COMPARISONS; uiComparison++) + { + apstrCompareFilename[uiComparison] = nullptr; + } + fEffort = ETCCOMP_DEFAULT_EFFORT_LEVEL; + //Rec. 709 or BT.709...the default + e_ErrMetric = ErrorMetric::BT709; + uiJobs = MIN_JOBS; + + //these are ignored if they are < 0 + i_hPixel = -1; + i_vPixel = -1; + verboseOutput = false; + boolNormalizeXYZ = false; + mipmaps = 1; + mipFilterFlags = Etc::FILTER_WRAP_NONE; + } + + bool ProcessCommandLineArguments(int a_iArgs, const char *a_apstrArgs[]); + void PrintUsageMessage(void); + static void FixSlashes(char *a_pstr); + + char *pstrSourceFilename; + char *pstrOutputFilename; + + Image::Format format; + char *pstrAnalysisDirectory; + char *formatType; + unsigned int uiComparisons; + char *apstrCompareFilename[Analysis::MAX_COMPARISONS]; + float fEffort; + ErrorMetric e_ErrMetric; + unsigned int uiJobs; // for threading + bool verboseOutput; + //when both of these are >= 0 then single block mode is on + int i_hPixel; + int i_vPixel; + bool boolNormalizeXYZ; + int mipmaps; + unsigned int mipFilterFlags; +}; + +#include "EtcFileHeader.h" + +// ---------------------------------------------------------------------------------------------------- +// +int main(int argc, const char * argv[]) +{ + + static const bool USE_C_INTERFACE = false; + + // this code tests for memory leaks +#if RUN_MEM_TEST + RunMemTest(true, 100); + printf("an extra line to see how the memory is free'd\n"); + printf("all done!\n"); + exit(0); +#endif + + Commands commands; + bool boolPrintUsage = commands.ProcessCommandLineArguments(argc, argv); + if (boolPrintUsage) + { + commands.PrintUsageMessage(); + exit(1); + } + + if (commands.verboseOutput) + { + printf("SourceImage: %s\n", commands.pstrSourceFilename); + } + SourceImage sourceimage(commands.pstrSourceFilename, commands.i_hPixel, commands.i_vPixel); + if (commands.boolNormalizeXYZ) + { + sourceimage.NormalizeXYZ(); + } + + unsigned int uiSourceWidth = sourceimage.GetWidth(); + unsigned int uiSourceHeight = sourceimage.GetHeight(); + + if(commands.mipmaps != 1) + { + int iEncodingTime_ms; + + // Calculate the maximum number of possible mipmaps + { + int dim = (uiSourceWidth < uiSourceHeight)?uiSourceWidth:uiSourceHeight; + int maxMips = 0; + while(dim >= 1) + { + maxMips++; + dim >>= 1; + } + if( commands.mipmaps == 0 || commands.mipmaps > maxMips) + { + commands.mipmaps = maxMips; + } + } + + Etc::RawImage *pMipmapImages = new Etc::RawImage[commands.mipmaps]; + + if (commands.verboseOutput) + { + printf("Encoding:\n"); + printf(" effort = %.f\n", commands.fEffort); + printf(" encoding = %s\n", Image::EncodingFormatToString(commands.format)); + printf(" error metric: %s\n", ErrorMetricToString(commands.e_ErrMetric)); + } + Etc::EncodeMipmaps((float *)sourceimage.GetPixels(), + uiSourceWidth, uiSourceHeight, + commands.format, + commands.e_ErrMetric, + commands.fEffort, + commands.uiJobs, + MAX_JOBS, + commands.mipmaps, + commands.mipFilterFlags, + pMipmapImages, + &iEncodingTime_ms); + if (commands.verboseOutput) + { + printf(" encode time = %dms\n", iEncodingTime_ms); + printf("EncodedImage: %s\n", commands.pstrOutputFilename); + } + Etc::File etcfile(commands.pstrOutputFilename, Etc::File::Format::INFER_FROM_FILE_EXTENSION, + commands.format, + commands.mipmaps, + pMipmapImages, + uiSourceWidth, uiSourceHeight ); + etcfile.Write(); + + delete [] pMipmapImages; + } + else if (USE_C_INTERFACE) + { + unsigned char *paucEncodingBits; + unsigned int uiEncodingBitsBytes; + unsigned int uiExtendedWidth; + unsigned int uiExtendedHeight; + int iEncodingTime_ms; + + if (commands.verboseOutput) + { + printf("Encoding:\n"); + printf(" effort = %.f\n", commands.fEffort); + printf(" encoding = %s\n", Image::EncodingFormatToString(commands.format)); + printf(" error metric: %s\n", ErrorMetricToString(commands.e_ErrMetric)); + } + Etc::Encode((float *)sourceimage.GetPixels(), + uiSourceWidth, uiSourceHeight, + commands.format, + commands.e_ErrMetric, + commands.fEffort, + commands.uiJobs, + MAX_JOBS, + &paucEncodingBits, &uiEncodingBitsBytes, + &uiExtendedWidth, &uiExtendedHeight, + &iEncodingTime_ms); + if (commands.verboseOutput) + { + printf(" encode time = %dms\n", iEncodingTime_ms); + printf("EncodedImage: %s\n", commands.pstrOutputFilename); + } + Etc::File etcfile(commands.pstrOutputFilename, Etc::File::Format::INFER_FROM_FILE_EXTENSION, + commands.format, + paucEncodingBits, uiEncodingBitsBytes, + uiSourceWidth, uiSourceHeight, + uiExtendedWidth, uiExtendedHeight); + etcfile.Write(); + } + else + { + if (commands.verboseOutput) + { + printf("Encoding:\n"); + printf(" effort = %.f%%\n", commands.fEffort); + printf(" encoding = %s\n", Image::EncodingFormatToString(commands.format)); + printf(" error metric: %s\n", ErrorMetricToString(commands.e_ErrMetric)); + } + Etc::Image image((float *)sourceimage.GetPixels(), + uiSourceWidth, uiSourceHeight, + commands.e_ErrMetric); + image.m_bVerboseOutput = commands.verboseOutput; + Etc::Image::EncodingStatus encStatus = Etc::Image::EncodingStatus::SUCCESS; + + encStatus = image.Encode(commands.format, commands.e_ErrMetric, commands.fEffort, commands.uiJobs,MAX_JOBS); + if (commands.verboseOutput) + { + printf(" encode time = %dms\n", image.GetEncodingTimeMs()); + printf("EncodedImage: %s\n", commands.pstrOutputFilename); + printf("status bitfield: %u\n", encStatus); + } + Etc::File etcfile(commands.pstrOutputFilename, Etc::File::Format::INFER_FROM_FILE_EXTENSION, + commands.format, + image.GetEncodingBits(), image.GetEncodingBitsBytes(), + image.GetSourceWidth(), image.GetSourceHeight(), + image.GetExtendedWidth(), image.GetExtendedHeight()); + + etcfile.Write(); + + if (commands.pstrAnalysisDirectory) + { + if (commands.verboseOutput) + { + printf("Analysis: %s\n", commands.pstrAnalysisDirectory); + } + Analysis analysis(&image, commands.pstrAnalysisDirectory); + + for (unsigned int uiComparison = 0; uiComparison < commands.uiComparisons; uiComparison++) + { + analysis.Compare(commands.apstrCompareFilename[uiComparison], commands.i_hPixel, commands.i_vPixel); + } + } + + } + + return 0; +} + +// ---------------------------------------------------------------------------------------------------- +// return true if usage message should be printed +// +bool Commands::ProcessCommandLineArguments(int a_iArgs, const char *a_apstrArgs[]) +{ + static const bool DEBUG_PRINT = false; + + if (a_iArgs == 1) + { + printf("Error: missing arguments\n"); + return true; + } + + for (int iArg = 1; iArg < a_iArgs; iArg++) + { + if (DEBUG_PRINT) + { + printf("%s: %u %s\n", a_apstrArgs[0], iArg, a_apstrArgs[iArg]); + } + + if (strcmp(a_apstrArgs[iArg], "-analyze") == 0) + { + ++iArg; + + if (iArg >= (a_iArgs)) + { + printf("Error: missing folder parameter for -analyze\n"); + return true; + } + else + { + pstrAnalysisDirectory = new char[strlen(a_apstrArgs[iArg]) + 1]; + strcpy(pstrAnalysisDirectory, a_apstrArgs[iArg]); + FixSlashes(pstrAnalysisDirectory); + } + } + else if (strcmp(a_apstrArgs[iArg], "-argfile") == 0) + { + static const unsigned int MAX_LINE_CHARS = 1000; + static const unsigned int MAX_ARGFILE_ARGS = 100; + + ++iArg; + + if (iArg >= (a_iArgs)) + { + printf("Error: missing file parameter for -argfile\n"); + return true; + } + else + { + FILE *pfile = fopen(a_apstrArgs[iArg], "rt"); + if (pfile == nullptr) + { + printf("Error: couldn't open argfile (%s)\n", a_apstrArgs[iArg]); + return true; + } + + char **apstrArgs = new char *[MAX_ARGFILE_ARGS]; + assert(apstrArgs); + + // add null executable name + apstrArgs[0] = const_cast(""); + int iArgs = 1; + + // read in tokens + { + char *pcTokens = new char[MAX_LINE_CHARS + 1]; + assert(pcTokens); + char *pcToken = nullptr; + + // read in each line + while (fgets(pcTokens, MAX_LINE_CHARS, pfile)) + { + // skip over lines with '#' in first char + if (pcTokens[0] == '#') + { + continue; + } + + // abort remainder of argfile with '%' in first char + if (pcTokens[0] == '%') + { + break; + } + pcToken = strtok(pcTokens, " \n\r"); + + if (pcToken != nullptr) + { + apstrArgs[iArgs] = new char[strlen(pcToken) + 1]; + strcpy(apstrArgs[iArgs], pcToken); + iArgs++; + } + + while (pcToken != nullptr) + { + pcToken = strtok(nullptr, " \n"); + if (pcToken != nullptr) + { + apstrArgs[iArgs] = new char[strlen(pcToken) + 1]; + strcpy(apstrArgs[iArgs], pcToken); + iArgs++; + } + } + } + + delete[] pcTokens; + } + + fclose(pfile); + + bool boolErrors = ProcessCommandLineArguments(iArgs, const_cast(apstrArgs)); + + for (iArg = 1; iArg < iArgs; iArg++) + { + delete[] apstrArgs[iArg]; + } + delete[] apstrArgs; + + if (boolErrors) + { + return true; + } + } + } + //used for debugging...select a single block to encode + //supply the horiz and very pos of the block + else if (strcmp(a_apstrArgs[iArg], "-blockAtHV") == 0) + { + ++iArg; + + //make sure we have two more args after -block + if (iArg + 1 >= (a_iArgs)) + { + printf("Error: missing horiz and vert position of pixel for single block mode \n"); + return true; + } + i_hPixel = atoi(a_apstrArgs[iArg]); + ++iArg; + i_vPixel = atoi(a_apstrArgs[iArg]); + } + else if (strcmp(a_apstrArgs[iArg], "-compare") == 0) + { + ++iArg; + + if (iArg >= (a_iArgs)) + { + printf("Error: missing comprison_image parameter for -compare\n"); + return true; + } + else + { + if (uiComparisons >= Analysis::MAX_COMPARISONS) + { + printf("Error: too many comparisons\n"); + return true; + } + + char **ppstrCompareFilename = &apstrCompareFilename[uiComparisons++]; + + *ppstrCompareFilename = new char[strlen(a_apstrArgs[iArg]) + 1]; + strcpy(*ppstrCompareFilename, a_apstrArgs[iArg]); + FixSlashes(*ppstrCompareFilename); + } + } + else if (strcmp(a_apstrArgs[iArg], "-effort") == 0) + { + ++iArg; + + if (iArg >= (a_iArgs)) + { + printf("Error: missing amount parameter for -effort\n"); + return true; + } + else + { + float f; + int iScans = sscanf(a_apstrArgs[iArg], "%f", &f); + + if (iScans != 1) + { + printf("Error: couldn't parse amount for -effort (%s)\n", a_apstrArgs[iArg]); + return true; + } + else + { + fEffort = f; + } + } + } + else if (strcmp(a_apstrArgs[iArg], "-errormetric") == 0) + { + ++iArg; + + if (iArg >= (a_iArgs)) + { + printf("Error: missing error metric type %s\n", a_apstrArgs[iArg]); + return true; + } + else + { + if (strcmp(a_apstrArgs[iArg], "rgba") == 0) + { + e_ErrMetric = ErrorMetric::RGBA; + } + else if (strcmp(a_apstrArgs[iArg], "rgbx") == 0) + { + e_ErrMetric = ErrorMetric::RGBX; + } + else if (strcmp(a_apstrArgs[iArg], "rec709") == 0) + { + e_ErrMetric = ErrorMetric::REC709; + } + else if (strcmp(a_apstrArgs[iArg], "numeric") == 0) + { + e_ErrMetric = ErrorMetric::NUMERIC; + } + else if (strcmp(a_apstrArgs[iArg], "normalxyz") == 0 || + strcmp(a_apstrArgs[iArg], "normalXYZ") == 0) + { + e_ErrMetric = ErrorMetric::NORMALXYZ; + } + else + { + printf("unrecognized error metric (%s), using numeric\n", a_apstrArgs[iArg]); + e_ErrMetric = ErrorMetric::NUMERIC; + } + } + } + else if (strcmp(a_apstrArgs[iArg], "-format") == 0) + { + ++iArg; + + if (iArg >= (a_iArgs)) + { + printf("Error: missing etc_format parameter for -format\n"); + return true; + } + else + { + formatType = new char[strlen(a_apstrArgs[iArg])+1]; + strcpy(formatType,a_apstrArgs[iArg]); + if (strcmp(a_apstrArgs[iArg], "ETC1") == 0) + { + format = Image::Format::ETC1; + } + else if (strcmp(a_apstrArgs[iArg], "RGB8") == 0) + { + format = Image::Format::RGB8; + } + else if (strcmp(a_apstrArgs[iArg], "SRGB8") == 0) + { + format = Image::Format::SRGB8; + } + else if (strcmp(a_apstrArgs[iArg], "RGBA8") == 0) + { + format = Image::Format::RGBA8; + } + else if (strcmp(a_apstrArgs[iArg], "SRGBA8") == 0) + { + format = Image::Format::SRGBA8; + } + else if (strcmp(a_apstrArgs[iArg], "R11") == 0) + { + format = Image::Format::R11; + } + else if (strcmp(a_apstrArgs[iArg], "SIGNED_R11") == 0) + { + format = Image::Format::SIGNED_R11; + } + else if (strcmp(a_apstrArgs[iArg], "RG11") == 0) + { + format = Image::Format::RG11; + } + else if (strcmp(a_apstrArgs[iArg], "SIGNED_RG11") == 0) + { + format = Image::Format::SIGNED_RG11; + } + else if (strcmp(a_apstrArgs[iArg], "RGB8A1") == 0) + { + format = Image::Format::RGB8A1; + } + else if (strcmp(a_apstrArgs[iArg], "SRGB8A1") == 0) + { + format = Image::Format::SRGB8A1; + } + else + { + printf("Error: unknown etc_format parameter for -format\n"); + format = Image::Format::UNKNOWN; + return true; + } + } + } + else if (strcmp(a_apstrArgs[iArg], "-help") == 0) + { + return true; + } + else if (strcmp(a_apstrArgs[iArg], "-j") == 0 || + strcmp(a_apstrArgs[iArg], "-jobs") == 0) + { + ++iArg; + + if (iArg >= (a_iArgs)) + { + printf("Error: missing job count for %s\n", a_apstrArgs[iArg]); + return true; + } + else + { + unsigned int ui; + int iScans = sscanf(a_apstrArgs[iArg], "%u", &ui); + + if (iScans != 1) + { + printf("Error: couldn't parse job count for %s (%s)\n", a_apstrArgs[iArg-1], a_apstrArgs[iArg]); + return true; + } + else + { + if (ui < MIN_JOBS) + { + ui = MIN_JOBS; + } + + uiJobs = ui; + } + } + } + else if (strcmp(a_apstrArgs[iArg], "-normalizexyz") == 0 || + strcmp(a_apstrArgs[iArg], "-normalizeXYZ") == 0) + { + boolNormalizeXYZ = true; + } + else if (strcmp(a_apstrArgs[iArg], "-output") == 0) + { + ++iArg; + + if (iArg >= (a_iArgs)) + { + printf("Error: missing encoded_image parameter for -output\n"); + return true; + } + else + { + pstrOutputFilename = new char[strlen(a_apstrArgs[iArg]) + 1]; + strcpy(pstrOutputFilename, a_apstrArgs[iArg]); + //take the output file name and extract the directory path so we can create the directory if nescacary + char *ptrOutputDir = nullptr; + + FixSlashes(pstrOutputFilename); + for (int c = (int)strlen(pstrOutputFilename); c > 0; c--) + { + //find the last slash, to get the name of the directory + if (pstrOutputFilename[c] == ETC_PATH_SLASH) + { + c++; + ptrOutputDir = new char[c]; + strncpy(ptrOutputDir, pstrOutputFilename, c); + ptrOutputDir[c] = '\0'; + CreateNewDir(ptrOutputDir); + break; + } + } + + if (ptrOutputDir == nullptr) + { + printf("couldnt find a place to put converted images\n"); + exit(1); + } + } + } + else if (strcmp(a_apstrArgs[iArg], "-verbose") == 0 || + strcmp(a_apstrArgs[iArg], "-v") == 0) + { + verboseOutput = true; + } + else if (strcmp(a_apstrArgs[iArg], "-mipmaps") == 0 || + strcmp(a_apstrArgs[iArg], "-m") == 0) + { + ++iArg; + + if (iArg >= (a_iArgs)) + { + printf("Error: missing mipmap number parameter for -mipmaps\n"); + return true; + } + else + { + unsigned int ui; + int result = sscanf(a_apstrArgs[iArg], "%u", &ui); + if (result == 1) + { + mipmaps = ui; + } + else + { + printf("Error: -mipmaps argument needs to be a number\n"); + return true; + } + } + } + else if (strcmp(a_apstrArgs[iArg], "-mipwrap") == 0 || + strcmp(a_apstrArgs[iArg], "-w") == 0) + { + ++iArg; + + if (iArg >= (a_iArgs)) + { + printf("Error: missing parameter for -mipwrap\n"); + return true; + } + else + { + if ( 0 == strcmp(a_apstrArgs[iArg], "x") ) + { + mipFilterFlags = Etc::FILTER_WRAP_X; + } + else if (0 == strcmp(a_apstrArgs[iArg], "y")) + { + mipFilterFlags = Etc::FILTER_WRAP_Y; + } + else if (0 == strcmp(a_apstrArgs[iArg], "xy")) + { + mipFilterFlags = Etc::FILTER_WRAP_X | Etc::FILTER_WRAP_Y; + } + } + } + else if (a_apstrArgs[iArg][0] == '-') + { + printf("Error: unknown option (%s)\n", a_apstrArgs[iArg]); + return true; + } + else if (a_apstrArgs[iArg][0] == '\r') + { + continue; + } + else + { + if (pstrSourceFilename != nullptr) + { + printf("Error: only support one source_image (%s)\n", a_apstrArgs[iArg]); + return true; + } + + pstrSourceFilename = new char[strlen(a_apstrArgs[iArg])+1]; + strcpy(pstrSourceFilename, a_apstrArgs[iArg]); + } + } + + if (pstrSourceFilename == nullptr) + { + printf("Error: missing source_image\n"); + return true; + } + + if (pstrOutputFilename == nullptr) + { + printf("Error: missing -output encoded_image\n"); + return true; + } + + if (uiComparisons > 0 && pstrAnalysisDirectory == nullptr) + { + printf("Error: -compare is only valid with -analyze\n"); + return true; + } + + return false; +} + +// ---------------------------------------------------------------------------------------------------- +// +void Commands::FixSlashes(char *a_pstr) +{ + while (*a_pstr) + { + if (*a_pstr == ETC_BAD_PATH_SLASH) + { + *a_pstr = ETC_PATH_SLASH; + } + + a_pstr++; + } +} + +// ---------------------------------------------------------------------------------------------------- +// print usage message and exit +// +void Commands::PrintUsageMessage(void) +{ + printf("Usage: etctool.exe source_image [options ...] -output \n"); + printf("Options:\n"); + printf(" -analyze \n"); + printf(" -argfile additional command line arguments\n"); + printf(" -blockAtHV encodes a single block that contains the\n"); + printf(" pixel specified by the H V coordinates\n"); + printf(" -compare compares source_image to comparison_image\n"); + printf(" -effort number between 0 and 100\n"); + printf(" -errormetric specify the error metric, the options are\n"); + printf(" rgba, rgbx, rec709, numeric and normalxyz\n"); + printf(" -format ETC1, RGB8, SRGB8, RGBA8, SRGB8, RGB8A1,\n"); + printf(" SRGB8A1 or R11\n"); + printf(" -help prints this message\n"); + printf(" -jobs or -j specifies the number of threads (default=1)\n"); + printf(" -normalizexyz normalize RGB to have a length of 1\n"); + printf(" -verbose or -v shows status information during the encoding\n"); + printf(" process\n"); + printf(" -mipmaps or -m sets the maximum number of mipaps to generate (default=1)\n"); + printf(" -mipwrap or -w sets the mipmap filter wrap mode (default=clamp)\n"); + printf("\n"); + + exit(1); +} + + // ---------------------------------------------------------------------------------------------------- + // + void CreateNewDir(const char *path) + { + char strCommand[300]; + +#if ETC_WINDOWS + sprintf_s(strCommand, "if not exist %s %s %s", path, ETC_MKDIR_COMMAND, path); +#else + sprintf(strCommand, "%s %s", ETC_MKDIR_COMMAND, path); +#endif + int iResult = system(strCommand); + if (iResult != 0) + { + printf("Error: couldn't create directory (%s)\n", path); + exit(0); + } + + } + + // ---------------------------------------------------------------------------------------------------- + // diff --git a/deps/etc2comp/EtcTool/EtcTool.h b/deps/etc2comp/EtcTool/EtcTool.h new file mode 100644 index 0000000000..97b95a82aa --- /dev/null +++ b/deps/etc2comp/EtcTool/EtcTool.h @@ -0,0 +1,36 @@ +/* + * Copyright 2015 The Etc2Comp Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "EtcConfig.h" + +#if ETC_WINDOWS + const char ETC_PATH_SLASH = '\\'; + const char ETC_BAD_PATH_SLASH = '/'; + + extern const char *ETC_MKDIR_COMMAND; + extern const char *ETC_IF_DIR_NOT_EXIST_COMMAND; + + int strcasecmp(const char *s1, const char *s2); +#else + const char ETC_PATH_SLASH = '/'; + const char ETC_BAD_PATH_SLASH = '\\'; + + extern const char *ETC_MKDIR_COMMAND; +#endif + + void CreateNewDir(const char *path); diff --git a/deps/etc2comp/LICENSE b/deps/etc2comp/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/deps/etc2comp/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/deps/etc2comp/README.md b/deps/etc2comp/README.md new file mode 100644 index 0000000000..2f4363d042 --- /dev/null +++ b/deps/etc2comp/README.md @@ -0,0 +1,197 @@ +# Etc2Comp - Texture to ETC2 compressor + +Etc2Comp is a command line tool that converts textures (e.g. bitmaps) +into the [ETC2](https://en.wikipedia.org/wiki/Ericsson_Texture_Compression) +format. The tool is built with a focus on encoding performance +to reduce the amount of time required to compile asset heavy applications as +well as reduce overall application size. + +This repo provides source code that can be compiled into a binary. The +binary can then be used to convert textures to the ETC2 format. + +Important: This is not an official Google product. It is an experimental +library published as-is. Please see the CONTRIBUTORS.md file for information +about questions or issues. + +## Setup +This project uses [CMake](https://cmake.org/) to generate platform-specific +build files: + - Linux: make files + - OS X: Xcode workspace files + - Microsoft Windows: Visual Studio solution files + - Note: CMake supports other formats, but this doc only provides steps for + one of each platform for brevity. + +Refer to each platform's setup section to setup your environment and build +an Etc2Comp binary. Then skip to the usage section of this page for examples +of how to use the library. + +### Setup for OS X + build tested on this config: + OS X 10.9.5 i7 16GB RAM + Xcode 5.1.1 + cmake 3.2.3 + +Start by downloading and installing the following components if they are not +already installed on your development machine. + - *Xcode* version 5.1.1, or greater + - [CMake](https://cmake.org/download/) version 3.2.3, or greater + +To build the Etc2Comp binary: + 1. Open a *Terminal* window and navigate to the project directory. + 1. Run `mkdir build_xcode` + 1. Run `cd build_xcode` + 1. Run `cmake -G Xcode ../` + 1. Open *Xcode* and import the `build_xcode/EtcTest.xcodeproj` file. + 1. Open the Product menu and choose Build For -> Running. + 1. Once the build succeeds the binary located at `build_xcode/EtcTool/Debug/EtcTool` +can be executed. + +Optional +Xcode EtcTool ‘Run’ preferences +note: if the build_xcode/EtcTest.xcodeproj is manually deleted then some Xcode preferences +will need to be set by hand after cmake is run (these prefs are retained across +cmake updates if the .xcodeproj is not deleted/removed) + +1. Set the active scheme to ‘EtcTool’ +1. Edit the scheme +1. Select option ‘Run EtcTool’, then tab ‘Arguments’. +Add this launch argument: ‘-argfile ../../EtcTool/args.txt’ +1. Select tab ‘Options’ and set a custom working directory to: ‘$(SRCROOT)/Build_Xcode/EtcTool’ + +### SetUp for Windows + +1. Open a *Terminal* window and navigate to the project directory. +1. Run `mkdir build_vs` +1. Run `cd build_vs` +1. Run CMAKE, noting what build version you need, and pointing to the parent directory as the source root; + For VS 2013 : `cmake -G "Visual Studio 12 2013 Win64" ../` + For VS 2015 : `cmake -G "Visual Studio 14 2015 Win64" ../` + NOTE: To see what supported Visual Studio outputs there are, run `cmake -G` +1. open the 'EtcTest' solution +1. make the 'EtcTool' project the start up project +1. (optional) in the project properties, under 'Debugging ->command arguments' +add the argfile textfile thats included in the EtcTool directory. +example: -argfile C:\etc2\EtcTool\Args.txt + +### Setup For Linux +The Linux build was tested on this config: + Ubuntu desktop 14.04 + gcc/g++ 4.8 + cmake 2.8.12.2 + +1. Verify linux has cmake and C++-11 capable g++ installed +1. Open shell +1. Run `mkdir build_linux` +1. Run `cd build_linux` +1. Run `cmake ../` +1. Run `make` +1. navigate to the newly created EtcTool directory `cd EtcTool` +1. run the executable: `./EtcTool -argfile ../../EtcTool/args.txt` + +Skip to the Usage section for more information about using the +tool. + +## Usage + +### Command Line Usage +EtcTool can be run from the command line with the following usage: + etctool.exe source_image [options ...] -output encoded_image + +The encoder will use an array of RGBA floats read from the source_image to create +an ETC1 or ETC2 encoded image in encoded_image. The RGBA floats should be in the +range [0:1]. + +Options: + + -analyze + -argfile additional command line arguments read from a file + -blockAtHV encodes a single block that contains the + pixel specified by the H V coordinates + -compare compares source_image to comparison_image + -effort number between 0 and 100 to specify the encoding quality + (100 is the highest quality) + -errormetric specify the error metric, the options are + rgba, rgbx, rec709, numeric and normalxyz + -format ETC1, RGB8, SRGB8, RGBA8, SRGB8, RGB8A1, + SRGB8A1 or R11 + -help prints this message + -jobs or -j specifies the number of threads (default=1) + -normalizexyz normalize RGB to have a length of 1 + -verbose or -v shows status information during the encoding + process + -mipmaps or -m sets the maximum number of mipaps to generate (default=1) + -mipwrap or -w sets the mipmap filter wrap mode (default=clamp) + +* -analyze will run an analysis of the encoding and place it in folder +"analysis_folder" (e.g. ../analysis/kodim05). within the analysis_folder, a folder +will be created with a name of the current date/time (e.g. 20151204_153306). this +date/time folder is used to compare encodings of the same texture over time. +within the date/time folder is a text file with several encoding stats and a 2x png +image showing the encoding mode for each 4x4 block. + +* -argfile allows additional command line arguments to be placed in a text file + +* -blockAtHV selects the 4x4 pixel subset of the source image at position (H,V). +This is mainly used for debugging + +* -compare compares the source image to the created encoded image. The encoding +will dictate what error analysis is used in the comparison. + +* -effort uses an "amount" between 0 and 100 to determine how much additional effort +to apply during the encoding. + +* -errormetric selects the fitting algorithm used by the encoder. "rgba" calculates +RMS error using RGB components that are weighted by A. "rgbx" calculates RMS error +using RGBA components, where A is treated as an additional data channel, instead of +as alpha. "rec709" is similar to "rgba", except the RGB components are also weighted +according to Rec709. "numeric" calculates RMS error using unweighted RGBA components. +"normalize" calculates error based on dot product and vector length for RGB and RMS +error for A. + +* -help prints out the usage message + +* -jobs enables multi-threading to speed up image encoding + +* -normalizexyz normalizes the source RGB to have a length of 1. + +* -verbose shows information on the current encoding process. It will then display the +PSNR and time time it took to encode the image. + +* -mipmaps takes an argument that specifies how many mipmaps to generate from the +source image. The mipmaps are generated with a lanczos3 filter using edge clamping. +If the mipmaps option is not specified no mipmaps are created. + +* -mipwrap takes an argument that specifies the mipmap filter wrap mode. The options +are "x", "y" and "xy" which specify wrapping in x only, y only or x and y respectively. +The default options are clamping in both x and y. + +Note: Path names can use slashes or backslashes. The tool will convert the +slashes to the appropriate polarity for the current platform. + + +## API + +The library supports two different APIs - a C-like API that is not heavily +class-based and a class-based API. + +main() in EtcTool.cpp contains an example of both APIs. + +The Encode() method now returns an EncodingStatus that contains bit flags for +reporting various warnings and flags encountered when encoding. + + +## Copyright +Copyright 2015 Etc2Comp Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/deps/etc2comp/etcAPI.cpp b/deps/etc2comp/etcAPI.cpp new file mode 100644 index 0000000000..6870d205c5 --- /dev/null +++ b/deps/etc2comp/etcAPI.cpp @@ -0,0 +1,38 @@ +#include "EtcConfig.h" +#include "Etc.h" +#include "EtcFilter.h" +#include "EtcMath.h" +#include "EtcImage.h" +#include "EtcErrorMetric.h" +#include "EtcBlock4x4EncodingBits.h" + +#include +#include + +using namespace Etc; + +extern "C" __attribute__((visibility("default"))) void * etc2Compress(float * pixelData, unsigned int width, unsigned int height, + unsigned int * size, unsigned int * dw, unsigned int * dh) +{ + Etc::Image image(pixelData, width, height, ErrorMetric::RGBA /*BT709 /* RGBA*/); + // Image::Format::RGB8 + // Image::Format::SRGB8 + // Image::Format::SRGBA8 + image.Encode(Image::Format::RGBA8, ErrorMetric::RGBA /*BT709*/, ETCCOMP_DEFAULT_EFFORT_LEVEL, 4, 4); + *size = image.GetEncodingBitsBytes(); + *dw = image.GetExtendedWidth(); + *dh = image.GetExtendedHeight(); + void * cData = malloc(*size); + memcpy(cData, image.GetEncodingBits(), *size); + return cData; +} + +extern "C" __attribute__((visibility("default"))) void * etc2Alloc(size_t size) +{ + return malloc(size); +} + +extern "C" __attribute__((visibility("default"))) void etc2Free(void * data) +{ + free(data); +} diff --git a/deps/etc2comp/third_party/lodepng/LICENSE b/deps/etc2comp/third_party/lodepng/LICENSE new file mode 100644 index 0000000000..8bf1b068f7 --- /dev/null +++ b/deps/etc2comp/third_party/lodepng/LICENSE @@ -0,0 +1,25 @@ +LodePNG version 20160124 + +Copyright (c) 2005-2016 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + +The manual and changelog are in the header file "lodepng.h" +Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C. diff --git a/deps/etc2comp/third_party/lodepng/lodepng.cpp b/deps/etc2comp/third_party/lodepng/lodepng.cpp new file mode 100644 index 0000000000..10f8ffa30a --- /dev/null +++ b/deps/etc2comp/third_party/lodepng/lodepng.cpp @@ -0,0 +1,6168 @@ +/* +LodePNG version 20160124 + +Copyright (c) 2005-2016 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +/* +The manual and changelog are in the header file "lodepng.h" +Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C. +*/ + +#include "lodepng.h" + +#include +#include + +#ifdef LODEPNG_COMPILE_CPP +#include +#endif /*LODEPNG_COMPILE_CPP*/ + +#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ +#pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ +#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ +#endif /*_MSC_VER */ + +const char* LODEPNG_VERSION_STRING = "20160124"; + +/* +This source file is built up in the following large parts. The code sections +with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way. +-Tools for C and common code for PNG and Zlib +-C Code for Zlib (huffman, deflate, ...) +-C Code for PNG (file format chunks, adam7, PNG filters, color conversions, ...) +-The C++ wrapper around all of the above +*/ + +/*The malloc, realloc and free functions defined here with "lodepng_" in front +of the name, so that you can easily change them to others related to your +platform if needed. Everything else in the code calls these. Pass +-DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler, or comment out +#define LODEPNG_COMPILE_ALLOCATORS in the header, to disable the ones here and +define them in your own project's source files without needing to change +lodepng source code. Don't forget to remove "static" if you copypaste them +from here.*/ + +#ifdef LODEPNG_COMPILE_ALLOCATORS +static void* lodepng_malloc(size_t size) +{ + return malloc(size); +} + +static void* lodepng_realloc(void* ptr, size_t new_size) +{ + return realloc(ptr, new_size); +} + +static void lodepng_free(void* ptr) +{ + free(ptr); +} +#else /*LODEPNG_COMPILE_ALLOCATORS*/ +void* lodepng_malloc(size_t size); +void* lodepng_realloc(void* ptr, size_t new_size); +void lodepng_free(void* ptr); +#endif /*LODEPNG_COMPILE_ALLOCATORS*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // Tools for C, and common code for PNG and Zlib. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/* +Often in case of an error a value is assigned to a variable and then it breaks +out of a loop (to go to the cleanup phase of a function). This macro does that. +It makes the error handling code shorter and more readable. + +Example: if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83); +*/ +#define CERROR_BREAK(errorvar, code)\ +{\ + errorvar = code;\ + break;\ +} + +/*version of CERROR_BREAK that assumes the common case where the error variable is named "error"*/ +#define ERROR_BREAK(code) CERROR_BREAK(error, code) + +/*Set error var to the error code, and return it.*/ +#define CERROR_RETURN_ERROR(errorvar, code)\ +{\ + errorvar = code;\ + return code;\ +} + +/*Try the code, if it returns error, also return the error.*/ +#define CERROR_TRY_RETURN(call)\ +{\ + unsigned error = call;\ + if(error) return error;\ +} + +/*Set error var to the error code, and return from the void function.*/ +#define CERROR_RETURN(errorvar, code)\ +{\ + errorvar = code;\ + return;\ +} + +/* +About uivector, ucvector and string: +-All of them wrap dynamic arrays or text strings in a similar way. +-LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version. +-The string tools are made to avoid problems with compilers that declare things like strncat as deprecated. +-They're not used in the interface, only internally in this file as static functions. +-As with many other structs in this file, the init and cleanup functions serve as ctor and dtor. +*/ + +#ifdef LODEPNG_COMPILE_ZLIB +/*dynamic vector of unsigned ints*/ +typedef struct uivector +{ + unsigned* data; + size_t size; /*size in number of unsigned longs*/ + size_t allocsize; /*allocated size in bytes*/ +} uivector; + +static void uivector_cleanup(void* p) +{ + ((uivector*)p)->size = ((uivector*)p)->allocsize = 0; + lodepng_free(((uivector*)p)->data); + ((uivector*)p)->data = NULL; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_reserve(uivector* p, size_t allocsize) +{ + if(allocsize > p->allocsize) + { + size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); + void* data = lodepng_realloc(p->data, newsize); + if(data) + { + p->allocsize = newsize; + p->data = (unsigned*)data; + } + else return 0; /*error: not enough memory*/ + } + return 1; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_resize(uivector* p, size_t size) +{ + if(!uivector_reserve(p, size * sizeof(unsigned))) return 0; + p->size = size; + return 1; /*success*/ +} + +/*resize and give all new elements the value*/ +static unsigned uivector_resizev(uivector* p, size_t size, unsigned value) +{ + size_t oldsize = p->size, i; + if(!uivector_resize(p, size)) return 0; + for(i = oldsize; i < size; ++i) p->data[i] = value; + return 1; +} + +static void uivector_init(uivector* p) +{ + p->data = NULL; + p->size = p->allocsize = 0; +} + +#ifdef LODEPNG_COMPILE_ENCODER +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_push_back(uivector* p, unsigned c) +{ + if(!uivector_resize(p, p->size + 1)) return 0; + p->data[p->size - 1] = c; + return 1; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_ZLIB*/ + +/* /////////////////////////////////////////////////////////////////////////// */ + +/*dynamic vector of unsigned chars*/ +typedef struct ucvector +{ + unsigned char* data; + size_t size; /*used size*/ + size_t allocsize; /*allocated size*/ +} ucvector; + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_reserve(ucvector* p, size_t allocsize) +{ + if(allocsize > p->allocsize) + { + size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); + void* data = lodepng_realloc(p->data, newsize); + if(data) + { + p->allocsize = newsize; + p->data = (unsigned char*)data; + } + else return 0; /*error: not enough memory*/ + } + return 1; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_resize(ucvector* p, size_t size) +{ + if(!ucvector_reserve(p, size * sizeof(unsigned char))) return 0; + p->size = size; + return 1; /*success*/ +} + +#ifdef LODEPNG_COMPILE_PNG + +static void ucvector_cleanup(void* p) +{ + ((ucvector*)p)->size = ((ucvector*)p)->allocsize = 0; + lodepng_free(((ucvector*)p)->data); + ((ucvector*)p)->data = NULL; +} + +static void ucvector_init(ucvector* p) +{ + p->data = NULL; + p->size = p->allocsize = 0; +} +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ZLIB +/*you can both convert from vector to buffer&size and vica versa. If you use +init_buffer to take over a buffer and size, it is not needed to use cleanup*/ +static void ucvector_init_buffer(ucvector* p, unsigned char* buffer, size_t size) +{ + p->data = buffer; + p->allocsize = p->size = size; +} +#endif /*LODEPNG_COMPILE_ZLIB*/ + +#if (defined(LODEPNG_COMPILE_PNG) && defined(LODEPNG_COMPILE_ANCILLARY_CHUNKS)) || defined(LODEPNG_COMPILE_ENCODER) +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_push_back(ucvector* p, unsigned char c) +{ + if(!ucvector_resize(p, p->size + 1)) return 0; + p->data[p->size - 1] = c; + return 1; +} +#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ + + +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_PNG +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned string_resize(char** out, size_t size) +{ + char* data = (char*)lodepng_realloc(*out, size + 1); + if(data) + { + data[size] = 0; /*null termination char*/ + *out = data; + } + return data != 0; +} + +/*init a {char*, size_t} pair for use as string*/ +static void string_init(char** out) +{ + *out = NULL; + string_resize(out, 0); +} + +/*free the above pair again*/ +static void string_cleanup(char** out) +{ + lodepng_free(*out); + *out = NULL; +} + +static void string_set(char** out, const char* in) +{ + size_t insize = strlen(in), i; + if(string_resize(out, insize)) + { + for(i = 0; i != insize; ++i) + { + (*out)[i] = in[i]; + } + } +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned lodepng_read32bitInt(const unsigned char* buffer) +{ + return (unsigned)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); +} + +#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER) +/*buffer must have at least 4 allocated bytes available*/ +static void lodepng_set32bitInt(unsigned char* buffer, unsigned value) +{ + buffer[0] = (unsigned char)((value >> 24) & 0xff); + buffer[1] = (unsigned char)((value >> 16) & 0xff); + buffer[2] = (unsigned char)((value >> 8) & 0xff); + buffer[3] = (unsigned char)((value ) & 0xff); +} +#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ + +#ifdef LODEPNG_COMPILE_ENCODER +static void lodepng_add32bitInt(ucvector* buffer, unsigned value) +{ + ucvector_resize(buffer, buffer->size + 4); /*todo: give error if resize failed*/ + lodepng_set32bitInt(&buffer->data[buffer->size - 4], value); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / File IO / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_DISK + +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) +{ + FILE* file; + long size; + + /*provide some proper output values if error will happen*/ + *out = 0; + *outsize = 0; + + file = fopen(filename, "rb"); + if(!file) return 78; + + /*get filesize:*/ + fseek(file , 0 , SEEK_END); + size = ftell(file); + rewind(file); + + /*read contents of the file into the vector*/ + *outsize = 0; + *out = (unsigned char*)lodepng_malloc((size_t)size); + if(size && (*out)) (*outsize) = fread(*out, 1, (size_t)size, file); + + fclose(file); + if(!(*out) && size) return 83; /*the above malloc failed*/ + return 0; +} + +/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ +unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename) +{ + FILE* file; + file = fopen(filename, "wb" ); + if(!file) return 79; + fwrite((char*)buffer , 1 , buffersize, file); + fclose(file); + return 0; +} + +#endif /*LODEPNG_COMPILE_DISK*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of common code and tools. Begin of Zlib related code. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_ENCODER +/*TODO: this ignores potential out of memory errors*/ +#define addBitToStream(/*size_t**/ bitpointer, /*ucvector**/ bitstream, /*unsigned char*/ bit)\ +{\ + /*add a new byte at the end*/\ + if(((*bitpointer) & 7) == 0) ucvector_push_back(bitstream, (unsigned char)0);\ + /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/\ + (bitstream->data[bitstream->size - 1]) |= (bit << ((*bitpointer) & 0x7));\ + ++(*bitpointer);\ +} + +static void addBitsToStream(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) +{ + size_t i; + for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> i) & 1)); +} + +static void addBitsToStreamReversed(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) +{ + size_t i; + for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> (nbits - 1 - i)) & 1)); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +#define READBIT(bitpointer, bitstream) ((bitstream[bitpointer >> 3] >> (bitpointer & 0x7)) & (unsigned char)1) + +static unsigned char readBitFromStream(size_t* bitpointer, const unsigned char* bitstream) +{ + unsigned char result = (unsigned char)(READBIT(*bitpointer, bitstream)); + ++(*bitpointer); + return result; +} + +static unsigned readBitsFromStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) +{ + unsigned result = 0, i; + for(i = 0; i != nbits; ++i) + { + result += ((unsigned)READBIT(*bitpointer, bitstream)) << i; + ++(*bitpointer); + } + return result; +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Deflate - Huffman / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#define FIRST_LENGTH_CODE_INDEX 257 +#define LAST_LENGTH_CODE_INDEX 285 +/*256 literals, the end code, some length codes, and 2 unused codes*/ +#define NUM_DEFLATE_CODE_SYMBOLS 288 +/*the distance codes have their own symbols, 30 used, 2 unused*/ +#define NUM_DISTANCE_SYMBOLS 32 +/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/ +#define NUM_CODE_LENGTH_CODES 19 + +/*the base lengths represented by codes 257-285*/ +static const unsigned LENGTHBASE[29] + = {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}; + +/*the extra bits used by codes 257-285 (added to base length)*/ +static const unsigned LENGTHEXTRA[29] + = {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}; + +/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/ +static const unsigned DISTANCEBASE[30] + = {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}; + +/*the extra bits of backwards distances (added to base)*/ +static const unsigned DISTANCEEXTRA[30] + = {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}; + +/*the order in which "code length alphabet code lengths" are stored, out of this +the huffman tree of the dynamic huffman tree lengths is generated*/ +static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES] + = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* ////////////////////////////////////////////////////////////////////////// */ + +/* +Huffman tree struct, containing multiple representations of the tree +*/ +typedef struct HuffmanTree +{ + unsigned* tree2d; + unsigned* tree1d; + unsigned* lengths; /*the lengths of the codes of the 1d-tree*/ + unsigned maxbitlen; /*maximum number of bits a single code can get*/ + unsigned numcodes; /*number of symbols in the alphabet = number of codes*/ +} HuffmanTree; + +/*function used for debug purposes to draw the tree in ascii art with C++*/ +/* +static void HuffmanTree_draw(HuffmanTree* tree) +{ + std::cout << "tree. length: " << tree->numcodes << " maxbitlen: " << tree->maxbitlen << std::endl; + for(size_t i = 0; i != tree->tree1d.size; ++i) + { + if(tree->lengths.data[i]) + std::cout << i << " " << tree->tree1d.data[i] << " " << tree->lengths.data[i] << std::endl; + } + std::cout << std::endl; +}*/ + +static void HuffmanTree_init(HuffmanTree* tree) +{ + tree->tree2d = 0; + tree->tree1d = 0; + tree->lengths = 0; +} + +static void HuffmanTree_cleanup(HuffmanTree* tree) +{ + lodepng_free(tree->tree2d); + lodepng_free(tree->tree1d); + lodepng_free(tree->lengths); +} + +/*the tree representation used by the decoder. return value is error*/ +static unsigned HuffmanTree_make2DTree(HuffmanTree* tree) +{ + unsigned nodefilled = 0; /*up to which node it is filled*/ + unsigned treepos = 0; /*position in the tree (1 of the numcodes columns)*/ + unsigned n, i; + + tree->tree2d = (unsigned*)lodepng_malloc(tree->numcodes * 2 * sizeof(unsigned)); + if(!tree->tree2d) return 83; /*alloc fail*/ + + /* + convert tree1d[] to tree2d[][]. In the 2D array, a value of 32767 means + uninited, a value >= numcodes is an address to another bit, a value < numcodes + is a code. The 2 rows are the 2 possible bit values (0 or 1), there are as + many columns as codes - 1. + A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. + Here, the internal nodes are stored (what their 0 and 1 option point to). + There is only memory for such good tree currently, if there are more nodes + (due to too long length codes), error 55 will happen + */ + for(n = 0; n < tree->numcodes * 2; ++n) + { + tree->tree2d[n] = 32767; /*32767 here means the tree2d isn't filled there yet*/ + } + + for(n = 0; n < tree->numcodes; ++n) /*the codes*/ + { + for(i = 0; i != tree->lengths[n]; ++i) /*the bits for this code*/ + { + unsigned char bit = (unsigned char)((tree->tree1d[n] >> (tree->lengths[n] - i - 1)) & 1); + /*oversubscribed, see comment in lodepng_error_text*/ + if(treepos > 2147483647 || treepos + 2 > tree->numcodes) return 55; + if(tree->tree2d[2 * treepos + bit] == 32767) /*not yet filled in*/ + { + if(i + 1 == tree->lengths[n]) /*last bit*/ + { + tree->tree2d[2 * treepos + bit] = n; /*put the current code in it*/ + treepos = 0; + } + else + { + /*put address of the next step in here, first that address has to be found of course + (it's just nodefilled + 1)...*/ + ++nodefilled; + /*addresses encoded with numcodes added to it*/ + tree->tree2d[2 * treepos + bit] = nodefilled + tree->numcodes; + treepos = nodefilled; + } + } + else treepos = tree->tree2d[2 * treepos + bit] - tree->numcodes; + } + } + + for(n = 0; n < tree->numcodes * 2; ++n) + { + if(tree->tree2d[n] == 32767) tree->tree2d[n] = 0; /*remove possible remaining 32767's*/ + } + + return 0; +} + +/* +Second step for the ...makeFromLengths and ...makeFromFrequencies functions. +numcodes, lengths and maxbitlen must already be filled in correctly. return +value is error. +*/ +static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) +{ + uivector blcount; + uivector nextcode; + unsigned error = 0; + unsigned bits, n; + + uivector_init(&blcount); + uivector_init(&nextcode); + + tree->tree1d = (unsigned*)lodepng_malloc(tree->numcodes * sizeof(unsigned)); + if(!tree->tree1d) error = 83; /*alloc fail*/ + + if(!uivector_resizev(&blcount, tree->maxbitlen + 1, 0) + || !uivector_resizev(&nextcode, tree->maxbitlen + 1, 0)) + error = 83; /*alloc fail*/ + + if(!error) + { + /*step 1: count number of instances of each code length*/ + for(bits = 0; bits != tree->numcodes; ++bits) ++blcount.data[tree->lengths[bits]]; + /*step 2: generate the nextcode values*/ + for(bits = 1; bits <= tree->maxbitlen; ++bits) + { + nextcode.data[bits] = (nextcode.data[bits - 1] + blcount.data[bits - 1]) << 1; + } + /*step 3: generate all the codes*/ + for(n = 0; n != tree->numcodes; ++n) + { + if(tree->lengths[n] != 0) tree->tree1d[n] = nextcode.data[tree->lengths[n]]++; + } + } + + uivector_cleanup(&blcount); + uivector_cleanup(&nextcode); + + if(!error) return HuffmanTree_make2DTree(tree); + else return error; +} + +/* +given the code lengths (as stored in the PNG file), generate the tree as defined +by Deflate. maxbitlen is the maximum bits that a code in the tree can have. +return value is error. +*/ +static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, + size_t numcodes, unsigned maxbitlen) +{ + unsigned i; + tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); + if(!tree->lengths) return 83; /*alloc fail*/ + for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; + tree->numcodes = (unsigned)numcodes; /*number of symbols*/ + tree->maxbitlen = maxbitlen; + return HuffmanTree_makeFromLengths2(tree); +} + +#ifdef LODEPNG_COMPILE_ENCODER + +/*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding", +Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/ + +/*chain node for boundary package merge*/ +typedef struct BPMNode +{ + int weight; /*the sum of all weights in this chain*/ + unsigned index; /*index of this leaf node (called "count" in the paper)*/ + struct BPMNode* tail; /*the next nodes in this chain (null if last)*/ + int in_use; +} BPMNode; + +/*lists of chains*/ +typedef struct BPMLists +{ + /*memory pool*/ + unsigned memsize; + BPMNode* memory; + unsigned numfree; + unsigned nextfree; + BPMNode** freelist; + /*two heads of lookahead chains per list*/ + unsigned listsize; + BPMNode** chains0; + BPMNode** chains1; +} BPMLists; + +/*creates a new chain node with the given parameters, from the memory in the lists */ +static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) +{ + unsigned i; + BPMNode* result; + + /*memory full, so garbage collect*/ + if(lists->nextfree >= lists->numfree) + { + /*mark only those that are in use*/ + for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0; + for(i = 0; i != lists->listsize; ++i) + { + BPMNode* node; + for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1; + for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1; + } + /*collect those that are free*/ + lists->numfree = 0; + for(i = 0; i != lists->memsize; ++i) + { + if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i]; + } + lists->nextfree = 0; + } + + result = lists->freelist[lists->nextfree++]; + result->weight = weight; + result->index = index; + result->tail = tail; + return result; +} + +static int bpmnode_compare(const void* a, const void* b) +{ + int wa = ((const BPMNode*)a)->weight; + int wb = ((const BPMNode*)b)->weight; + if(wa < wb) return -1; + if(wa > wb) return 1; + /*make the qsort a stable sort*/ + return ((const BPMNode*)a)->index < ((const BPMNode*)b)->index ? 1 : -1; +} + +/*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/ +static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) +{ + unsigned lastindex = lists->chains1[c]->index; + + if(c == 0) + { + if(lastindex >= numpresent) return; + lists->chains0[c] = lists->chains1[c]; + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0); + } + else + { + /*sum of the weights of the head nodes of the previous lookahead chains.*/ + int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight; + lists->chains0[c] = lists->chains1[c]; + if(lastindex < numpresent && sum > leaves[lastindex].weight) + { + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail); + return; + } + lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]); + /*in the end we are only interested in the chain of the last list, so no + need to recurse if we're at the last one (this gives measurable speedup)*/ + if(num + 1 < (int)(2 * numpresent - 2)) + { + boundaryPM(lists, leaves, numpresent, c - 1, num); + boundaryPM(lists, leaves, numpresent, c - 1, num); + } + } +} + +unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, + size_t numcodes, unsigned maxbitlen) +{ + unsigned error = 0; + unsigned i; + size_t numpresent = 0; /*number of symbols with non-zero frequency*/ + BPMNode* leaves; /*the symbols, only those with > 0 frequency*/ + + if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/ + if((1ull << maxbitlen) < numcodes) return 80; /*error: represent all symbols*/ + + leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves)); + if(!leaves) return 83; /*alloc fail*/ + + for(i = 0; i != numcodes; ++i) + { + if(frequencies[i] > 0) + { + leaves[numpresent].weight = (int)frequencies[i]; + leaves[numpresent].index = i; + ++numpresent; + } + } + + for(i = 0; i != numcodes; ++i) lengths[i] = 0; + + /*ensure at least two present symbols. There should be at least one symbol + according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To + make these work as well ensure there are at least two symbols. The + Package-Merge code below also doesn't work correctly if there's only one + symbol, it'd give it the theoritical 0 bits but in practice zlib wants 1 bit*/ + if(numpresent == 0) + { + lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/ + } + else if(numpresent == 1) + { + lengths[leaves[0].index] = 1; + lengths[leaves[0].index == 0 ? 1 : 0] = 1; + } + else + { + BPMLists lists; + BPMNode* node; + + qsort(leaves, numpresent, sizeof(BPMNode), bpmnode_compare); + + lists.listsize = maxbitlen; + lists.memsize = 2 * maxbitlen * (maxbitlen + 1); + lists.nextfree = 0; + lists.numfree = lists.memsize; + lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory)); + lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*)); + lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/ + + if(!error) + { + for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i]; + + bpmnode_create(&lists, leaves[0].weight, 1, 0); + bpmnode_create(&lists, leaves[1].weight, 2, 0); + + for(i = 0; i != lists.listsize; ++i) + { + lists.chains0[i] = &lists.memory[0]; + lists.chains1[i] = &lists.memory[1]; + } + + /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/ + for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i); + + for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) + { + for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index]; + } + } + + lodepng_free(lists.memory); + lodepng_free(lists.freelist); + lodepng_free(lists.chains0); + lodepng_free(lists.chains1); + } + + lodepng_free(leaves); + return error; +} + +/*Create the Huffman tree given the symbol frequencies*/ +static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies, + size_t mincodes, size_t numcodes, unsigned maxbitlen) +{ + unsigned error = 0; + while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/ + tree->maxbitlen = maxbitlen; + tree->numcodes = (unsigned)numcodes; /*number of symbols*/ + tree->lengths = (unsigned*)lodepng_realloc(tree->lengths, numcodes * sizeof(unsigned)); + if(!tree->lengths) return 83; /*alloc fail*/ + /*initialize all lengths to 0*/ + memset(tree->lengths, 0, numcodes * sizeof(unsigned)); + + error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen); + if(!error) error = HuffmanTree_makeFromLengths2(tree); + return error; +} + +static unsigned HuffmanTree_getCode(const HuffmanTree* tree, unsigned index) +{ + return tree->tree1d[index]; +} + +static unsigned HuffmanTree_getLength(const HuffmanTree* tree, unsigned index) +{ + return tree->lengths[index]; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/ +static unsigned generateFixedLitLenTree(HuffmanTree* tree) +{ + unsigned i, error = 0; + unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + if(!bitlen) return 83; /*alloc fail*/ + + /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ + for(i = 0; i <= 143; ++i) bitlen[i] = 8; + for(i = 144; i <= 255; ++i) bitlen[i] = 9; + for(i = 256; i <= 279; ++i) bitlen[i] = 7; + for(i = 280; i <= 287; ++i) bitlen[i] = 8; + + error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); + + lodepng_free(bitlen); + return error; +} + +/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static unsigned generateFixedDistanceTree(HuffmanTree* tree) +{ + unsigned i, error = 0; + unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + if(!bitlen) return 83; /*alloc fail*/ + + /*there are 32 distance codes, but 30-31 are unused*/ + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; + error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); + + lodepng_free(bitlen); + return error; +} + +#ifdef LODEPNG_COMPILE_DECODER + +/* +returns the code, or (unsigned)(-1) if error happened +inbitlength is the length of the complete buffer, in bits (so its byte length times 8) +*/ +static unsigned huffmanDecodeSymbol(const unsigned char* in, size_t* bp, + const HuffmanTree* codetree, size_t inbitlength) +{ + unsigned treepos = 0, ct; + for(;;) + { + if(*bp >= inbitlength) return (unsigned)(-1); /*error: end of input memory reached without endcode*/ + /* + decode the symbol from the tree. The "readBitFromStream" code is inlined in + the expression below because this is the biggest bottleneck while decoding + */ + ct = codetree->tree2d[(treepos << 1) + READBIT(*bp, in)]; + ++(*bp); + if(ct < codetree->numcodes) return ct; /*the symbol is decoded, return it*/ + else treepos = ct - codetree->numcodes; /*symbol not yet decoded, instead move tree position*/ + + if(treepos >= codetree->numcodes) return (unsigned)(-1); /*error: it appeared outside the codetree*/ + } +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Inflator (Decompressor) / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static void getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) +{ + /*TODO: check for out of memory errors*/ + generateFixedLitLenTree(tree_ll); + generateFixedDistanceTree(tree_d); +} + +/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ +static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, + const unsigned char* in, size_t* bp, size_t inlength) +{ + /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/ + unsigned error = 0; + unsigned n, HLIT, HDIST, HCLEN, i; + size_t inbitlength = inlength * 8; + + /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/ + unsigned* bitlen_ll = 0; /*lit,len code lengths*/ + unsigned* bitlen_d = 0; /*dist code lengths*/ + /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/ + unsigned* bitlen_cl = 0; + HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ + + if((*bp) + 14 > (inlength << 3)) return 49; /*error: the bit pointer is or will go past the memory*/ + + /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ + HLIT = readBitsFromStream(bp, in, 5) + 257; + /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/ + HDIST = readBitsFromStream(bp, in, 5) + 1; + /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ + HCLEN = readBitsFromStream(bp, in, 4) + 4; + + if((*bp) + HCLEN * 3 > (inlength << 3)) return 50; /*error: the bit pointer is or will go past the memory*/ + + HuffmanTree_init(&tree_cl); + + while(!error) + { + /*read the code length codes out of 3 * (amount of code length codes) bits*/ + + bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); + if(!bitlen_cl) ERROR_BREAK(83 /*alloc fail*/); + + for(i = 0; i != NUM_CODE_LENGTH_CODES; ++i) + { + if(i < HCLEN) bitlen_cl[CLCL_ORDER[i]] = readBitsFromStream(bp, in, 3); + else bitlen_cl[CLCL_ORDER[i]] = 0; /*if not, it must stay 0*/ + } + + error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7); + if(error) break; + + /*now we can use this tree to read the lengths for the tree that this function will return*/ + bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); + for(i = 0; i != NUM_DEFLATE_CODE_SYMBOLS; ++i) bitlen_ll[i] = 0; + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen_d[i] = 0; + + /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ + i = 0; + while(i < HLIT + HDIST) + { + unsigned code = huffmanDecodeSymbol(in, bp, &tree_cl, inbitlength); + if(code <= 15) /*a length code*/ + { + if(i < HLIT) bitlen_ll[i] = code; + else bitlen_d[i - HLIT] = code; + ++i; + } + else if(code == 16) /*repeat previous*/ + { + unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ + unsigned value; /*set value to the previous code*/ + + if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ + + if((*bp + 2) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + replength += readBitsFromStream(bp, in, 2); + + if(i < HLIT + 1) value = bitlen_ll[i - 1]; + else value = bitlen_d[i - HLIT - 1]; + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) + { + if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ + if(i < HLIT) bitlen_ll[i] = value; + else bitlen_d[i - HLIT] = value; + ++i; + } + } + else if(code == 17) /*repeat "0" 3-10 times*/ + { + unsigned replength = 3; /*read in the bits that indicate repeat length*/ + if((*bp + 3) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + replength += readBitsFromStream(bp, in, 3); + + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) + { + if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ + + if(i < HLIT) bitlen_ll[i] = 0; + else bitlen_d[i - HLIT] = 0; + ++i; + } + } + else if(code == 18) /*repeat "0" 11-138 times*/ + { + unsigned replength = 11; /*read in the bits that indicate repeat length*/ + if((*bp + 7) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + replength += readBitsFromStream(bp, in, 7); + + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) + { + if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ + + if(i < HLIT) bitlen_ll[i] = 0; + else bitlen_d[i - HLIT] = 0; + ++i; + } + } + else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ + { + if(code == (unsigned)(-1)) + { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + error = (*bp) > inbitlength ? 10 : 11; + } + else error = 16; /*unexisting code, this can never happen*/ + break; + } + } + if(error) break; + + if(bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/ + + /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/ + error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15); + if(error) break; + error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15); + + break; /*end of error-while*/ + } + + lodepng_free(bitlen_cl); + lodepng_free(bitlen_ll); + lodepng_free(bitlen_d); + HuffmanTree_cleanup(&tree_cl); + + return error; +} + +/*inflate a block with dynamic of fixed Huffman tree*/ +static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size_t* bp, + size_t* pos, size_t inlength, unsigned btype) +{ + unsigned error = 0; + HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ + HuffmanTree tree_d; /*the huffman tree for distance codes*/ + size_t inbitlength = inlength * 8; + + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + + if(btype == 1) getTreeInflateFixed(&tree_ll, &tree_d); + else if(btype == 2) error = getTreeInflateDynamic(&tree_ll, &tree_d, in, bp, inlength); + + while(!error) /*decode all symbols until end reached, breaks at end code*/ + { + /*code_ll is literal, length or end code*/ + unsigned code_ll = huffmanDecodeSymbol(in, bp, &tree_ll, inbitlength); + if(code_ll <= 255) /*literal symbol*/ + { + /*ucvector_push_back would do the same, but for some reason the two lines below run 10% faster*/ + if(!ucvector_resize(out, (*pos) + 1)) ERROR_BREAK(83 /*alloc fail*/); + out->data[*pos] = (unsigned char)code_ll; + ++(*pos); + } + else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ + { + unsigned code_d, distance; + unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ + size_t start, forward, backward, length; + + /*part 1: get length base*/ + length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX]; + + /*part 2: get extra bits and add the value of that to length*/ + numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; + if((*bp + numextrabits_l) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ + length += readBitsFromStream(bp, in, numextrabits_l); + + /*part 3: get distance code*/ + code_d = huffmanDecodeSymbol(in, bp, &tree_d, inbitlength); + if(code_d > 29) + { + if(code_ll == (unsigned)(-1)) /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ + { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + error = (*bp) > inlength * 8 ? 10 : 11; + } + else error = 18; /*error: invalid distance code (30-31 are never used)*/ + break; + } + distance = DISTANCEBASE[code_d]; + + /*part 4: get extra bits from distance*/ + numextrabits_d = DISTANCEEXTRA[code_d]; + if((*bp + numextrabits_d) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ + distance += readBitsFromStream(bp, in, numextrabits_d); + + /*part 5: fill in all the out[n] values based on the length and dist*/ + start = (*pos); + if(distance > start) ERROR_BREAK(52); /*too long backward distance*/ + backward = start - distance; + + if(!ucvector_resize(out, (*pos) + length)) ERROR_BREAK(83 /*alloc fail*/); + if (distance < length) { + for(forward = 0; forward < length; ++forward) + { + out->data[(*pos)++] = out->data[backward++]; + } + } else { + memcpy(out->data + *pos, out->data + backward, length); + *pos += length; + } + } + else if(code_ll == 256) + { + break; /*end code, break the loop*/ + } + else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ + { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + error = ((*bp) > inlength * 8) ? 10 : 11; + break; + } + } + + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + + return error; +} + +static unsigned inflateNoCompression(ucvector* out, const unsigned char* in, size_t* bp, size_t* pos, size_t inlength) +{ + size_t p; + unsigned LEN, NLEN, n, error = 0; + + /*go to first boundary of byte*/ + while(((*bp) & 0x7) != 0) ++(*bp); + p = (*bp) / 8; /*byte position*/ + + /*read LEN (2 bytes) and NLEN (2 bytes)*/ + if(p + 4 >= inlength) return 52; /*error, bit pointer will jump past memory*/ + LEN = in[p] + 256u * in[p + 1]; p += 2; + NLEN = in[p] + 256u * in[p + 1]; p += 2; + + /*check if 16-bit NLEN is really the one's complement of LEN*/ + if(LEN + NLEN != 65535) return 21; /*error: NLEN is not one's complement of LEN*/ + + if(!ucvector_resize(out, (*pos) + LEN)) return 83; /*alloc fail*/ + + /*read the literal data: LEN bytes are now stored in the out buffer*/ + if(p + LEN > inlength) return 23; /*error: reading outside of in buffer*/ + for(n = 0; n < LEN; ++n) out->data[(*pos)++] = in[p++]; + + (*bp) = p * 8; + + return error; +} + +static unsigned lodepng_inflatev(ucvector* out, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) +{ + /*bit pointer in the "in" data, current byte is bp >> 3, current bit is bp & 0x7 (from lsb to msb of the byte)*/ + size_t bp = 0; + unsigned BFINAL = 0; + size_t pos = 0; /*byte position in the out buffer*/ + unsigned error = 0; + + (void)settings; + + while(!BFINAL) + { + unsigned BTYPE; + if(bp + 2 >= insize * 8) return 52; /*error, bit pointer will jump past memory*/ + BFINAL = readBitFromStream(&bp, in); + BTYPE = 1u * readBitFromStream(&bp, in); + BTYPE += 2u * readBitFromStream(&bp, in); + + if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ + else if(BTYPE == 0) error = inflateNoCompression(out, in, &bp, &pos, insize); /*no compression*/ + else error = inflateHuffmanBlock(out, in, &bp, &pos, insize, BTYPE); /*compression, BTYPE 01 or 10*/ + + if(error) return error; + } + + return error; +} + +unsigned lodepng_inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) +{ + unsigned error; + ucvector v; + ucvector_init_buffer(&v, *out, *outsize); + error = lodepng_inflatev(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +static unsigned inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) +{ + if(settings->custom_inflate) + { + return settings->custom_inflate(out, outsize, in, insize, settings); + } + else + { + return lodepng_inflate(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Deflator (Compressor) / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258; + +/*bitlen is the size in bits of the code*/ +static void addHuffmanSymbol(size_t* bp, ucvector* compressed, unsigned code, unsigned bitlen) +{ + addBitsToStreamReversed(bp, compressed, code, bitlen); +} + +/*search the index in the array, that has the largest value smaller than or equal to the given value, +given array must be sorted (if no value is smaller, it returns the size of the given array)*/ +static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) +{ + /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/ + size_t left = 1; + size_t right = array_size - 1; + + while(left <= right) { + size_t mid = (left + right) >> 1; + if (array[mid] >= value) right = mid - 1; + else left = mid + 1; + } + if(left >= array_size || array[left] > value) left--; + return left; +} + +static void addLengthDistance(uivector* values, size_t length, size_t distance) +{ + /*values in encoded vector are those used by deflate: + 0-255: literal bytes + 256: end + 257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits) + 286-287: invalid*/ + + unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length); + unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]); + unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance); + unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]); + + uivector_push_back(values, length_code + FIRST_LENGTH_CODE_INDEX); + uivector_push_back(values, extra_length); + uivector_push_back(values, dist_code); + uivector_push_back(values, extra_distance); +} + +/*3 bytes of data get encoded into two bytes. The hash cannot use more than 3 +bytes as input because 3 is the minimum match length for deflate*/ +static const unsigned HASH_NUM_VALUES = 65536; +static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/ + +typedef struct Hash +{ + int* head; /*hash value to head circular pos - can be outdated if went around window*/ + /*circular pos to prev circular pos*/ + unsigned short* chain; + int* val; /*circular pos to hash value*/ + + /*TODO: do this not only for zeros but for any repeated byte. However for PNG + it's always going to be the zeros that dominate, so not important for PNG*/ + int* headz; /*similar to head, but for chainz*/ + unsigned short* chainz; /*those with same amount of zeros*/ + unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/ +} Hash; + +static unsigned hash_init(Hash* hash, unsigned windowsize) +{ + unsigned i; + hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES); + hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize); + hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + + hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1)); + hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + + if(!hash->head || !hash->chain || !hash->val || !hash->headz|| !hash->chainz || !hash->zeros) + { + return 83; /*alloc fail*/ + } + + /*initialize hash table*/ + for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1; + for(i = 0; i != windowsize; ++i) hash->val[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/ + + for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/ + + return 0; +} + +static void hash_cleanup(Hash* hash) +{ + lodepng_free(hash->head); + lodepng_free(hash->val); + lodepng_free(hash->chain); + + lodepng_free(hash->zeros); + lodepng_free(hash->headz); + lodepng_free(hash->chainz); +} + + + +static unsigned getHash(const unsigned char* data, size_t size, size_t pos) +{ + unsigned result = 0; + if(pos + 2 < size) + { + /*A simple shift and xor hash is used. Since the data of PNGs is dominated + by zeroes due to the filters, a better hash does not have a significant + effect on speed in traversing the chain, and causes more time spend on + calculating the hash.*/ + result ^= (unsigned)(data[pos + 0] << 0u); + result ^= (unsigned)(data[pos + 1] << 4u); + result ^= (unsigned)(data[pos + 2] << 8u); + } else { + size_t amount, i; + if(pos >= size) return 0; + amount = size - pos; + for(i = 0; i != amount; ++i) result ^= (unsigned)(data[pos + i] << (i * 8u)); + } + return result & HASH_BIT_MASK; +} + +static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) +{ + const unsigned char* start = data + pos; + const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH; + if(end > data + size) end = data + size; + data = start; + while(data != end && *data == 0) ++data; + /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/ + return (unsigned)(data - start); +} + +/*wpos = pos & (windowsize - 1)*/ +static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) +{ + hash->val[wpos] = (int)hashval; + if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval]; + hash->head[hashval] = (int)wpos; + + hash->zeros[wpos] = numzeros; + if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros]; + hash->headz[numzeros] = (int)wpos; +} + +/* +LZ77-encode the data. Return value is error code. The input are raw bytes, the output +is in the form of unsigned integers with codes representing for example literal bytes, or +length/distance pairs. +It uses a hash table technique to let it encode faster. When doing LZ77 encoding, a +sliding window (of windowsize) is used, and all past bytes in that window can be used as +the "dictionary". A brute force search through all possible distances would be slow, and +this hash technique is one out of several ways to speed this up. +*/ +static unsigned encodeLZ77(uivector* out, Hash* hash, + const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize, + unsigned minmatch, unsigned nicematch, unsigned lazymatching) +{ + size_t pos; + unsigned i, error = 0; + /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/ + unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8; + unsigned maxlazymatch = windowsize >= 8192 ? MAX_SUPPORTED_DEFLATE_LENGTH : 64; + + unsigned usezeros = 1; /*not sure if setting it to false for windowsize < 8192 is better or worse*/ + unsigned numzeros = 0; + + unsigned offset; /*the offset represents the distance in LZ77 terminology*/ + unsigned length; + unsigned lazy = 0; + unsigned lazylength = 0, lazyoffset = 0; + unsigned hashval; + unsigned current_offset, current_length; + unsigned prev_offset; + const unsigned char *lastptr, *foreptr, *backptr; + unsigned hashpos; + + if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ + if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/ + + if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH; + + for(pos = inpos; pos < insize; ++pos) + { + size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/ + unsigned chainlength = 0; + + hashval = getHash(in, insize, pos); + + if(usezeros && hashval == 0) + { + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; + } + else + { + numzeros = 0; + } + + updateHashChain(hash, wpos, hashval, numzeros); + + /*the length and offset found for the current position*/ + length = 0; + offset = 0; + + hashpos = hash->chain[wpos]; + + lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH]; + + /*search for the longest string*/ + prev_offset = 0; + for(;;) + { + if(chainlength++ >= maxchainlength) break; + current_offset = hashpos <= wpos ? (unsigned int)(wpos - hashpos) : (unsigned int)(wpos - hashpos + windowsize); + + if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/ + prev_offset = current_offset; + if(current_offset > 0) + { + /*test the next characters*/ + foreptr = &in[pos]; + backptr = &in[pos - current_offset]; + + /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/ + if(numzeros >= 3) + { + unsigned skip = hash->zeros[hashpos]; + if(skip > numzeros) skip = numzeros; + backptr += skip; + foreptr += skip; + } + + while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/ + { + ++backptr; + ++foreptr; + } + current_length = (unsigned)(foreptr - &in[pos]); + + if(current_length > length) + { + length = current_length; /*the longest length*/ + offset = current_offset; /*the offset that is related to this longest length*/ + /*jump out once a length of max length is found (speed gain). This also jumps + out if length is MAX_SUPPORTED_DEFLATE_LENGTH*/ + if(current_length >= nicematch) break; + } + } + + if(hashpos == hash->chain[hashpos]) break; + + if(numzeros >= 3 && length > numzeros) + { + hashpos = hash->chainz[hashpos]; + if(hash->zeros[hashpos] != numzeros) break; + } + else + { + hashpos = hash->chain[hashpos]; + /*outdated hash value, happens if particular value was not encountered in whole last window*/ + if(hash->val[hashpos] != (int)hashval) break; + } + } + + if(lazymatching) + { + if(!lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH) + { + lazy = 1; + lazylength = length; + lazyoffset = offset; + continue; /*try the next byte*/ + } + if(lazy) + { + lazy = 0; + if(pos == 0) ERROR_BREAK(81); + if(length > lazylength + 1) + { + /*push the previous character as literal*/ + if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/); + } + else + { + length = lazylength; + offset = lazyoffset; + hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/ + hash->headz[numzeros] = -1; /*idem*/ + --pos; + } + } + } + if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/); + + /*encode it as length/distance pair or literal value*/ + if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/ + { + if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); + } + else if(length < minmatch || (length == 3 && offset > 4096)) + { + /*compensate for the fact that longer offsets have more extra bits, a + length of only 3 may be not worth it then*/ + if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); + } + else + { + addLengthDistance(out, length, offset); + for(i = 1; i < length; ++i) + { + ++pos; + wpos = pos & (windowsize - 1); + hashval = getHash(in, insize, pos); + if(usezeros && hashval == 0) + { + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; + } + else + { + numzeros = 0; + } + updateHashChain(hash, wpos, hashval, numzeros); + } + } + } /*end of the loop through each character of input*/ + + return error; +} + +/* /////////////////////////////////////////////////////////////////////////// */ + +static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize) +{ + /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte, + 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/ + + size_t i, j, numdeflateblocks = (datasize + 65534) / 65535; + unsigned datapos = 0; + for(i = 0; i != numdeflateblocks; ++i) + { + unsigned BFINAL, BTYPE, LEN, NLEN; + unsigned char firstbyte; + + BFINAL = (i == numdeflateblocks - 1); + BTYPE = 0; + + firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1) << 1) + ((BTYPE & 2) << 1)); + ucvector_push_back(out, firstbyte); + + LEN = 65535; + if(datasize - datapos < 65535) LEN = (unsigned)datasize - datapos; + NLEN = 65535 - LEN; + + ucvector_push_back(out, (unsigned char)(LEN & 255)); + ucvector_push_back(out, (unsigned char)(LEN >> 8)); + ucvector_push_back(out, (unsigned char)(NLEN & 255)); + ucvector_push_back(out, (unsigned char)(NLEN >> 8)); + + /*Decompressed data*/ + for(j = 0; j < 65535 && datapos < datasize; ++j) + { + ucvector_push_back(out, data[datapos++]); + } + } + + return 0; +} + +/* +write the lz77-encoded data, which has lit, len and dist codes, to compressed stream using huffman trees. +tree_ll: the tree for lit and len codes. +tree_d: the tree for distance codes. +*/ +static void writeLZ77data(size_t* bp, ucvector* out, const uivector* lz77_encoded, + const HuffmanTree* tree_ll, const HuffmanTree* tree_d) +{ + size_t i = 0; + for(i = 0; i != lz77_encoded->size; ++i) + { + unsigned val = lz77_encoded->data[i]; + addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_ll, val), HuffmanTree_getLength(tree_ll, val)); + if(val > 256) /*for a length code, 3 more things have to be added*/ + { + unsigned length_index = val - FIRST_LENGTH_CODE_INDEX; + unsigned n_length_extra_bits = LENGTHEXTRA[length_index]; + unsigned length_extra_bits = lz77_encoded->data[++i]; + + unsigned distance_code = lz77_encoded->data[++i]; + + unsigned distance_index = distance_code; + unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index]; + unsigned distance_extra_bits = lz77_encoded->data[++i]; + + addBitsToStream(bp, out, length_extra_bits, n_length_extra_bits); + addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_d, distance_code), + HuffmanTree_getLength(tree_d, distance_code)); + addBitsToStream(bp, out, distance_extra_bits, n_distance_extra_bits); + } + } +} + +/*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/ +static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, + const unsigned char* data, size_t datapos, size_t dataend, + const LodePNGCompressSettings* settings, unsigned final) +{ + unsigned error = 0; + + /* + A block is compressed as follows: The PNG data is lz77 encoded, resulting in + literal bytes and length/distance pairs. This is then huffman compressed with + two huffman trees. One huffman tree is used for the lit and len values ("ll"), + another huffman tree is used for the dist values ("d"). These two trees are + stored using their code lengths, and to compress even more these code lengths + are also run-length encoded and huffman compressed. This gives a huffman tree + of code lengths "cl". The code lenghts used to describe this third tree are + the code length code lengths ("clcl"). + */ + + /*The lz77 encoded data, represented with integers since there will also be length and distance codes in it*/ + uivector lz77_encoded; + HuffmanTree tree_ll; /*tree for lit,len values*/ + HuffmanTree tree_d; /*tree for distance codes*/ + HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/ + uivector frequencies_ll; /*frequency of lit,len codes*/ + uivector frequencies_d; /*frequency of dist codes*/ + uivector frequencies_cl; /*frequency of code length codes*/ + uivector bitlen_lld; /*lit,len,dist code lenghts (int bits), literally (without repeat codes).*/ + uivector bitlen_lld_e; /*bitlen_lld encoded with repeat codes (this is a rudemtary run length compression)*/ + /*bitlen_cl is the code length code lengths ("clcl"). The bit lengths of codes to represent tree_cl + (these are written as is in the file, it would be crazy to compress these using yet another huffman + tree that needs to be represented by yet another set of code lengths)*/ + uivector bitlen_cl; + size_t datasize = dataend - datapos; + + /* + Due to the huffman compression of huffman tree representations ("two levels"), there are some anologies: + bitlen_lld is to tree_cl what data is to tree_ll and tree_d. + bitlen_lld_e is to bitlen_lld what lz77_encoded is to data. + bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded. + */ + + unsigned BFINAL = final; + size_t numcodes_ll, numcodes_d, i; + unsigned HLIT, HDIST, HCLEN; + + uivector_init(&lz77_encoded); + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + HuffmanTree_init(&tree_cl); + uivector_init(&frequencies_ll); + uivector_init(&frequencies_d); + uivector_init(&frequencies_cl); + uivector_init(&bitlen_lld); + uivector_init(&bitlen_lld_e); + uivector_init(&bitlen_cl); + + /*This while loop never loops due to a break at the end, it is here to + allow breaking out of it to the cleanup phase on error conditions.*/ + while(!error) + { + if(settings->use_lz77) + { + error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, + settings->minmatch, settings->nicematch, settings->lazymatching); + if(error) break; + } + else + { + if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/); + for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/ + } + + if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83 /*alloc fail*/); + if(!uivector_resizev(&frequencies_d, 30, 0)) ERROR_BREAK(83 /*alloc fail*/); + + /*Count the frequencies of lit, len and dist codes*/ + for(i = 0; i != lz77_encoded.size; ++i) + { + unsigned symbol = lz77_encoded.data[i]; + ++frequencies_ll.data[symbol]; + if(symbol > 256) + { + unsigned dist = lz77_encoded.data[i + 2]; + ++frequencies_d.data[dist]; + i += 3; + } + } + frequencies_ll.data[256] = 1; /*there will be exactly 1 end code, at the end of the block*/ + + /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/ + error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll.data, 257, frequencies_ll.size, 15); + if(error) break; + /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/ + error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d.data, 2, frequencies_d.size, 15); + if(error) break; + + numcodes_ll = tree_ll.numcodes; if(numcodes_ll > 286) numcodes_ll = 286; + numcodes_d = tree_d.numcodes; if(numcodes_d > 30) numcodes_d = 30; + /*store the code lengths of both generated trees in bitlen_lld*/ + for(i = 0; i != numcodes_ll; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_ll, (unsigned)i)); + for(i = 0; i != numcodes_d; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_d, (unsigned)i)); + + /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times), + 17 (3-10 zeroes), 18 (11-138 zeroes)*/ + for(i = 0; i != (unsigned)bitlen_lld.size; ++i) + { + unsigned j = 0; /*amount of repititions*/ + while(i + j + 1 < (unsigned)bitlen_lld.size && bitlen_lld.data[i + j + 1] == bitlen_lld.data[i]) ++j; + + if(bitlen_lld.data[i] == 0 && j >= 2) /*repeat code for zeroes*/ + { + ++j; /*include the first zero*/ + if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ + { + uivector_push_back(&bitlen_lld_e, 17); + uivector_push_back(&bitlen_lld_e, j - 3); + } + else /*repeat code 18 supports max 138 zeroes*/ + { + if(j > 138) j = 138; + uivector_push_back(&bitlen_lld_e, 18); + uivector_push_back(&bitlen_lld_e, j - 11); + } + i += (j - 1); + } + else if(j >= 3) /*repeat code for value other than zero*/ + { + size_t k; + unsigned num = j / 6, rest = j % 6; + uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); + for(k = 0; k < num; ++k) + { + uivector_push_back(&bitlen_lld_e, 16); + uivector_push_back(&bitlen_lld_e, 6 - 3); + } + if(rest >= 3) + { + uivector_push_back(&bitlen_lld_e, 16); + uivector_push_back(&bitlen_lld_e, rest - 3); + } + else j -= rest; + i += j; + } + else /*too short to benefit from repeat code*/ + { + uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); + } + } + + /*generate tree_cl, the huffmantree of huffmantrees*/ + + if(!uivector_resizev(&frequencies_cl, NUM_CODE_LENGTH_CODES, 0)) ERROR_BREAK(83 /*alloc fail*/); + for(i = 0; i != bitlen_lld_e.size; ++i) + { + ++frequencies_cl.data[bitlen_lld_e.data[i]]; + /*after a repeat code come the bits that specify the number of repetitions, + those don't need to be in the frequencies_cl calculation*/ + if(bitlen_lld_e.data[i] >= 16) ++i; + } + + error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl.data, + frequencies_cl.size, frequencies_cl.size, 7); + if(error) break; + + if(!uivector_resize(&bitlen_cl, tree_cl.numcodes)) ERROR_BREAK(83 /*alloc fail*/); + for(i = 0; i != tree_cl.numcodes; ++i) + { + /*lenghts of code length tree is in the order as specified by deflate*/ + bitlen_cl.data[i] = HuffmanTree_getLength(&tree_cl, CLCL_ORDER[i]); + } + while(bitlen_cl.data[bitlen_cl.size - 1] == 0 && bitlen_cl.size > 4) + { + /*remove zeros at the end, but minimum size must be 4*/ + if(!uivector_resize(&bitlen_cl, bitlen_cl.size - 1)) ERROR_BREAK(83 /*alloc fail*/); + } + if(error) break; + + /* + Write everything into the output + + After the BFINAL and BTYPE, the dynamic block consists out of the following: + - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN + - (HCLEN+4)*3 bits code lengths of code length alphabet + - HLIT + 257 code lenghts of lit/length alphabet (encoded using the code length + alphabet, + possible repetition codes 16, 17, 18) + - HDIST + 1 code lengths of distance alphabet (encoded using the code length + alphabet, + possible repetition codes 16, 17, 18) + - compressed data + - 256 (end code) + */ + + /*Write block type*/ + addBitToStream(bp, out, BFINAL); + addBitToStream(bp, out, 0); /*first bit of BTYPE "dynamic"*/ + addBitToStream(bp, out, 1); /*second bit of BTYPE "dynamic"*/ + + /*write the HLIT, HDIST and HCLEN values*/ + HLIT = (unsigned)(numcodes_ll - 257); + HDIST = (unsigned)(numcodes_d - 1); + HCLEN = (unsigned)bitlen_cl.size - 4; + /*trim zeroes for HCLEN. HLIT and HDIST were already trimmed at tree creation*/ + while(!bitlen_cl.data[HCLEN + 4 - 1] && HCLEN > 0) --HCLEN; + addBitsToStream(bp, out, HLIT, 5); + addBitsToStream(bp, out, HDIST, 5); + addBitsToStream(bp, out, HCLEN, 4); + + /*write the code lenghts of the code length alphabet*/ + for(i = 0; i != HCLEN + 4; ++i) addBitsToStream(bp, out, bitlen_cl.data[i], 3); + + /*write the lenghts of the lit/len AND the dist alphabet*/ + for(i = 0; i != bitlen_lld_e.size; ++i) + { + addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_cl, bitlen_lld_e.data[i]), + HuffmanTree_getLength(&tree_cl, bitlen_lld_e.data[i])); + /*extra bits of repeat codes*/ + if(bitlen_lld_e.data[i] == 16) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 2); + else if(bitlen_lld_e.data[i] == 17) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 3); + else if(bitlen_lld_e.data[i] == 18) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 7); + } + + /*write the compressed data symbols*/ + writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); + /*error: the length of the end code 256 must be larger than 0*/ + if(HuffmanTree_getLength(&tree_ll, 256) == 0) ERROR_BREAK(64); + + /*write the end code*/ + addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); + + break; /*end of error-while*/ + } + + /*cleanup*/ + uivector_cleanup(&lz77_encoded); + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + HuffmanTree_cleanup(&tree_cl); + uivector_cleanup(&frequencies_ll); + uivector_cleanup(&frequencies_d); + uivector_cleanup(&frequencies_cl); + uivector_cleanup(&bitlen_lld_e); + uivector_cleanup(&bitlen_lld); + uivector_cleanup(&bitlen_cl); + + return error; +} + +static unsigned deflateFixed(ucvector* out, size_t* bp, Hash* hash, + const unsigned char* data, + size_t datapos, size_t dataend, + const LodePNGCompressSettings* settings, unsigned final) +{ + HuffmanTree tree_ll; /*tree for literal values and length codes*/ + HuffmanTree tree_d; /*tree for distance codes*/ + + unsigned BFINAL = final; + unsigned error = 0; + size_t i; + + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + + generateFixedLitLenTree(&tree_ll); + generateFixedDistanceTree(&tree_d); + + addBitToStream(bp, out, BFINAL); + addBitToStream(bp, out, 1); /*first bit of BTYPE*/ + addBitToStream(bp, out, 0); /*second bit of BTYPE*/ + + if(settings->use_lz77) /*LZ77 encoded*/ + { + uivector lz77_encoded; + uivector_init(&lz77_encoded); + error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, + settings->minmatch, settings->nicematch, settings->lazymatching); + if(!error) writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); + uivector_cleanup(&lz77_encoded); + } + else /*no LZ77, but still will be Huffman compressed*/ + { + for(i = datapos; i < dataend; ++i) + { + addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, data[i]), HuffmanTree_getLength(&tree_ll, data[i])); + } + } + /*add END code*/ + if(!error) addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); + + /*cleanup*/ + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + + return error; +} + +static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) +{ + unsigned error = 0; + size_t i, blocksize, numdeflateblocks; + size_t bp = 0; /*the bit pointer*/ + Hash hash; + + if(settings->btype > 2) return 61; + else if(settings->btype == 0) return deflateNoCompression(out, in, insize); + else if(settings->btype == 1) blocksize = insize; + else /*if(settings->btype == 2)*/ + { + /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/ + blocksize = insize / 8 + 8; + if(blocksize < 65536) blocksize = 65536; + if(blocksize > 262144) blocksize = 262144; + } + + numdeflateblocks = (insize + blocksize - 1) / blocksize; + if(numdeflateblocks == 0) numdeflateblocks = 1; + + error = hash_init(&hash, settings->windowsize); + if(error) return error; + + for(i = 0; i != numdeflateblocks && !error; ++i) + { + unsigned final = (i == numdeflateblocks - 1); + size_t start = i * blocksize; + size_t end = start + blocksize; + if(end > insize) end = insize; + + if(settings->btype == 1) error = deflateFixed(out, &bp, &hash, in, start, end, settings, final); + else if(settings->btype == 2) error = deflateDynamic(out, &bp, &hash, in, start, end, settings, final); + } + + hash_cleanup(&hash); + + return error; +} + +unsigned lodepng_deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) +{ + unsigned error; + ucvector v; + ucvector_init_buffer(&v, *out, *outsize); + error = lodepng_deflatev(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +static unsigned deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) +{ + if(settings->custom_deflate) + { + return settings->custom_deflate(out, outsize, in, insize, settings); + } + else + { + return lodepng_deflate(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Adler32 */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) +{ + unsigned s1 = adler & 0xffff; + unsigned s2 = (adler >> 16) & 0xffff; + + while(len > 0) + { + /*at least 5550 sums can be done before the sums overflow, saving a lot of module divisions*/ + unsigned amount = len > 5550 ? 5550 : len; + len -= amount; + while(amount > 0) + { + s1 += (*data++); + s2 += s1; + --amount; + } + s1 %= 65521; + s2 %= 65521; + } + + return (s2 << 16) | s1; +} + +/*Return the adler32 of the bytes data[0..len-1]*/ +static unsigned adler32(const unsigned char* data, unsigned len) +{ + return update_adler32(1L, data, len); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Zlib / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_DECODER + +unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) +{ + unsigned error = 0; + unsigned CM, CINFO, FDICT; + + if(insize < 2) return 53; /*error, size of zlib data too small*/ + /*read information from zlib header*/ + if((in[0] * 256 + in[1]) % 31 != 0) + { + /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/ + return 24; + } + + CM = in[0] & 15; + CINFO = (in[0] >> 4) & 15; + /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/ + FDICT = (in[1] >> 5) & 1; + /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/ + + if(CM != 8 || CINFO > 7) + { + /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/ + return 25; + } + if(FDICT != 0) + { + /*error: the specification of PNG says about the zlib stream: + "The additional flags shall not specify a preset dictionary."*/ + return 26; + } + + error = inflate(out, outsize, in + 2, insize - 2, settings); + if(error) return error; + + if(!settings->ignore_adler32) + { + unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]); + unsigned checksum = adler32(*out, (unsigned)(*outsize)); + if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/ + } + + return 0; /*no error*/ +} + +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) +{ + if(settings->custom_zlib) + { + return settings->custom_zlib(out, outsize, in, insize, settings); + } + else + { + return lodepng_zlib_decompress(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER + +unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) +{ + /*initially, *out must be NULL and outsize 0, if you just give some random *out + that's pointing to a non allocated buffer, this'll crash*/ + ucvector outv; + size_t i; + unsigned error; + unsigned char* deflatedata = 0; + size_t deflatesize = 0; + + /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/ + unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/ + unsigned FLEVEL = 0; + unsigned FDICT = 0; + unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64; + unsigned FCHECK = 31 - CMFFLG % 31; + CMFFLG += FCHECK; + + /*ucvector-controlled version of the output buffer, for dynamic array*/ + ucvector_init_buffer(&outv, *out, *outsize); + + ucvector_push_back(&outv, (unsigned char)(CMFFLG >> 8)); + ucvector_push_back(&outv, (unsigned char)(CMFFLG & 255)); + + error = deflate(&deflatedata, &deflatesize, in, insize, settings); + + if(!error) + { + unsigned ADLER32 = adler32(in, (unsigned)insize); + for(i = 0; i != deflatesize; ++i) ucvector_push_back(&outv, deflatedata[i]); + lodepng_free(deflatedata); + lodepng_add32bitInt(&outv, ADLER32); + } + + *out = outv.data; + *outsize = outv.size; + + return error; +} + +/* compress using the default or custom zlib function */ +static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) +{ + if(settings->custom_zlib) + { + return settings->custom_zlib(out, outsize, in, insize, settings); + } + else + { + return lodepng_zlib_compress(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#else /*no LODEPNG_COMPILE_ZLIB*/ + +#ifdef LODEPNG_COMPILE_DECODER +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) +{ + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ + return settings->custom_zlib(out, outsize, in, insize, settings); +} +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER +static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) +{ + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ + return settings->custom_zlib(out, outsize, in, insize, settings); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#endif /*LODEPNG_COMPILE_ZLIB*/ + +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_ENCODER + +/*this is a good tradeoff between speed and compression ratio*/ +#define DEFAULT_WINDOWSIZE 2048 + +void lodepng_compress_settings_init(LodePNGCompressSettings* settings) +{ + /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/ + settings->btype = 2; + settings->use_lz77 = 1; + settings->windowsize = DEFAULT_WINDOWSIZE; + settings->minmatch = 3; + settings->nicematch = 128; + settings->lazymatching = 1; + + settings->custom_zlib = 0; + settings->custom_deflate = 0; + settings->custom_context = 0; +} + +const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT_WINDOWSIZE, 3, 128, 1, 0, 0, 0}; + + +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) +{ + settings->ignore_adler32 = 0; + + settings->custom_zlib = 0; + settings->custom_inflate = 0; + settings->custom_context = 0; +} + +const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0}; + +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of Zlib related code. Begin of PNG related code. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_PNG + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / CRC32 / */ +/* ////////////////////////////////////////////////////////////////////////// */ + + +#ifndef LODEPNG_NO_COMPILE_CRC +/* CRC polynomial: 0xedb88320 */ +static unsigned lodepng_crc32_table[256] = { + 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, + 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, + 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, + 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, + 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, + 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, + 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, + 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, + 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, + 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, + 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, + 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, + 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, + 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, + 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, + 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, + 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, + 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, + 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, + 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, + 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, + 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, + 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, + 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, + 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, + 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, + 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, + 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, + 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, + 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, + 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, + 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u +}; + +/*Return the CRC of the bytes buf[0..len-1].*/ +unsigned lodepng_crc32(const unsigned char* data, size_t length) +{ + unsigned r = 0xffffffffu; + size_t i; + for(i = 0; i < length; ++i) + { + r = lodepng_crc32_table[(r ^ data[i]) & 0xff] ^ (r >> 8); + } + return r ^ 0xffffffffu; +} +#else /* !LODEPNG_NO_COMPILE_CRC */ +unsigned lodepng_crc32(const unsigned char* data, size_t length); +#endif /* !LODEPNG_NO_COMPILE_CRC */ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Reading and writing single bits and bytes from/to stream for LodePNG / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) +{ + unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); + ++(*bitpointer); + return result; +} + +static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) +{ + unsigned result = 0; + size_t i; + for(i = nbits - 1; i < nbits; --i) + { + result += (unsigned)readBitFromReversedStream(bitpointer, bitstream) << i; + } + return result; +} + +#ifdef LODEPNG_COMPILE_DECODER +static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) +{ + /*the current bit in bitstream must be 0 for this to work*/ + if(bit) + { + /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/ + bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); + } + ++(*bitpointer); +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) +{ + /*the current bit in bitstream may be 0 or 1 for this to work*/ + if(bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7)))); + else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7))); + ++(*bitpointer); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG chunks / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned lodepng_chunk_length(const unsigned char* chunk) +{ + return lodepng_read32bitInt(&chunk[0]); +} + +void lodepng_chunk_type(char type[5], const unsigned char* chunk) +{ + unsigned i; + for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i]; + type[4] = 0; /*null termination char*/ +} + +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) +{ + if(strlen(type) != 4) return 0; + return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); +} + +unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) +{ + return((chunk[4] & 32) != 0); +} + +unsigned char lodepng_chunk_private(const unsigned char* chunk) +{ + return((chunk[6] & 32) != 0); +} + +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk) +{ + return((chunk[7] & 32) != 0); +} + +unsigned char* lodepng_chunk_data(unsigned char* chunk) +{ + return &chunk[8]; +} + +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) +{ + return &chunk[8]; +} + +unsigned lodepng_chunk_check_crc(const unsigned char* chunk) +{ + unsigned length = lodepng_chunk_length(chunk); + unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]); + /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ + unsigned checksum = lodepng_crc32(&chunk[4], length + 4); + if(CRC != checksum) return 1; + else return 0; +} + +void lodepng_chunk_generate_crc(unsigned char* chunk) +{ + unsigned length = lodepng_chunk_length(chunk); + unsigned CRC = lodepng_crc32(&chunk[4], length + 4); + lodepng_set32bitInt(chunk + 8 + length, CRC); +} + +unsigned char* lodepng_chunk_next(unsigned char* chunk) +{ + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + return &chunk[total_chunk_length]; +} + +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk) +{ + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + return &chunk[total_chunk_length]; +} + +unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk) +{ + unsigned i; + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + unsigned char *chunk_start, *new_buffer; + size_t new_length = (*outlength) + total_chunk_length; + if(new_length < total_chunk_length || new_length < (*outlength)) return 77; /*integer overflow happened*/ + + new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); + if(!new_buffer) return 83; /*alloc fail*/ + (*out) = new_buffer; + (*outlength) = new_length; + chunk_start = &(*out)[new_length - total_chunk_length]; + + for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i]; + + return 0; +} + +unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, + const char* type, const unsigned char* data) +{ + unsigned i; + unsigned char *chunk, *new_buffer; + size_t new_length = (*outlength) + length + 12; + if(new_length < length + 12 || new_length < (*outlength)) return 77; /*integer overflow happened*/ + new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); + if(!new_buffer) return 83; /*alloc fail*/ + (*out) = new_buffer; + (*outlength) = new_length; + chunk = &(*out)[(*outlength) - length - 12]; + + /*1: length*/ + lodepng_set32bitInt(chunk, (unsigned)length); + + /*2: chunk name (4 letters)*/ + chunk[4] = (unsigned char)type[0]; + chunk[5] = (unsigned char)type[1]; + chunk[6] = (unsigned char)type[2]; + chunk[7] = (unsigned char)type[3]; + + /*3: the data*/ + for(i = 0; i != length; ++i) chunk[8 + i] = data[i]; + + /*4: CRC (of the chunkname characters and the data)*/ + lodepng_chunk_generate_crc(chunk); + + return 0; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Color types and such / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*return type is a LodePNG error code*/ +static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) /*bd = bitdepth*/ +{ + switch(colortype) + { + case 0: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/ + case 2: if(!( bd == 8 || bd == 16)) return 37; break; /*RGB*/ + case 3: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/ + case 4: if(!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/ + case 6: if(!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/ + default: return 31; + } + return 0; /*allowed color type / bits combination*/ +} + +static unsigned getNumColorChannels(LodePNGColorType colortype) +{ + switch(colortype) + { + case 0: return 1; /*grey*/ + case 2: return 3; /*RGB*/ + case 3: return 1; /*palette*/ + case 4: return 2; /*grey + alpha*/ + case 6: return 4; /*RGBA*/ + } + return 0; /*unexisting color type*/ +} + +static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) +{ + /*bits per pixel is amount of channels * bits per channel*/ + return getNumColorChannels(colortype) * bitdepth; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +void lodepng_color_mode_init(LodePNGColorMode* info) +{ + info->key_defined = 0; + info->key_r = info->key_g = info->key_b = 0; + info->colortype = LCT_RGBA; + info->bitdepth = 8; + info->palette = 0; + info->palettesize = 0; +} + +void lodepng_color_mode_cleanup(LodePNGColorMode* info) +{ + lodepng_palette_clear(info); +} + +unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) +{ + size_t i; + lodepng_color_mode_cleanup(dest); + *dest = *source; + if(source->palette) + { + dest->palette = (unsigned char*)lodepng_malloc(1024); + if(!dest->palette && source->palettesize) return 83; /*alloc fail*/ + for(i = 0; i != source->palettesize * 4; ++i) dest->palette[i] = source->palette[i]; + } + return 0; +} + +static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) +{ + size_t i; + if(a->colortype != b->colortype) return 0; + if(a->bitdepth != b->bitdepth) return 0; + if(a->key_defined != b->key_defined) return 0; + if(a->key_defined) + { + if(a->key_r != b->key_r) return 0; + if(a->key_g != b->key_g) return 0; + if(a->key_b != b->key_b) return 0; + } + /*if one of the palette sizes is 0, then we consider it to be the same as the + other: it means that e.g. the palette was not given by the user and should be + considered the same as the palette inside the PNG.*/ + if(1/*a->palettesize != 0 && b->palettesize != 0*/) { + if(a->palettesize != b->palettesize) return 0; + for(i = 0; i != a->palettesize * 4; ++i) + { + if(a->palette[i] != b->palette[i]) return 0; + } + } + return 1; +} + +void lodepng_palette_clear(LodePNGColorMode* info) +{ + if(info->palette) lodepng_free(info->palette); + info->palette = 0; + info->palettesize = 0; +} + +unsigned lodepng_palette_add(LodePNGColorMode* info, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + unsigned char* data; + /*the same resize technique as C++ std::vectors is used, and here it's made so that for a palette with + the max of 256 colors, it'll have the exact alloc size*/ + if(!info->palette) /*allocate palette if empty*/ + { + /*room for 256 colors with 4 bytes each*/ + data = (unsigned char*)lodepng_realloc(info->palette, 1024); + if(!data) return 83; /*alloc fail*/ + else info->palette = data; + } + info->palette[4 * info->palettesize + 0] = r; + info->palette[4 * info->palettesize + 1] = g; + info->palette[4 * info->palettesize + 2] = b; + info->palette[4 * info->palettesize + 3] = a; + ++info->palettesize; + return 0; +} + +unsigned lodepng_get_bpp(const LodePNGColorMode* info) +{ + /*calculate bits per pixel out of colortype and bitdepth*/ + return lodepng_get_bpp_lct(info->colortype, info->bitdepth); +} + +unsigned lodepng_get_channels(const LodePNGColorMode* info) +{ + return getNumColorChannels(info->colortype); +} + +unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info) +{ + return info->colortype == LCT_GREY || info->colortype == LCT_GREY_ALPHA; +} + +unsigned lodepng_is_alpha_type(const LodePNGColorMode* info) +{ + return (info->colortype & 4) != 0; /*4 or 6*/ +} + +unsigned lodepng_is_palette_type(const LodePNGColorMode* info) +{ + return info->colortype == LCT_PALETTE; +} + +unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) +{ + size_t i; + for(i = 0; i != info->palettesize; ++i) + { + if(info->palette[i * 4 + 3] < 255) return 1; + } + return 0; +} + +unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) +{ + return info->key_defined + || lodepng_is_alpha_type(info) + || lodepng_has_palette_alpha(info); +} + +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) +{ + /*will not overflow for any color type if roughly w * h < 268435455*/ + int bpp = lodepng_get_bpp(color); + size_t n = w * h; + return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; +} + +size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) +{ + /*will not overflow for any color type if roughly w * h < 268435455*/ + int bpp = lodepng_get_bpp_lct(colortype, bitdepth); + size_t n = w * h; + return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; +} + + +#ifdef LODEPNG_COMPILE_PNG +#ifdef LODEPNG_COMPILE_DECODER +/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer*/ +static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color) +{ + /*will not overflow for any color type if roughly w * h < 268435455*/ + int bpp = lodepng_get_bpp(color); + size_t line = ((w / 8) * bpp) + ((w & 7) * bpp + 7) / 8; + return h * line; +} +#endif /*LODEPNG_COMPILE_DECODER*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +static void LodePNGUnknownChunks_init(LodePNGInfo* info) +{ + unsigned i; + for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0; + for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0; +} + +static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) +{ + unsigned i; + for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]); +} + +static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) +{ + unsigned i; + + LodePNGUnknownChunks_cleanup(dest); + + for(i = 0; i != 3; ++i) + { + size_t j; + dest->unknown_chunks_size[i] = src->unknown_chunks_size[i]; + dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]); + if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/ + for(j = 0; j < src->unknown_chunks_size[i]; ++j) + { + dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j]; + } + } + + return 0; +} + +/******************************************************************************/ + +static void LodePNGText_init(LodePNGInfo* info) +{ + info->text_num = 0; + info->text_keys = NULL; + info->text_strings = NULL; +} + +static void LodePNGText_cleanup(LodePNGInfo* info) +{ + size_t i; + for(i = 0; i != info->text_num; ++i) + { + string_cleanup(&info->text_keys[i]); + string_cleanup(&info->text_strings[i]); + } + lodepng_free(info->text_keys); + lodepng_free(info->text_strings); +} + +static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) +{ + size_t i = 0; + dest->text_keys = 0; + dest->text_strings = 0; + dest->text_num = 0; + for(i = 0; i != source->text_num; ++i) + { + CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); + } + return 0; +} + +void lodepng_clear_text(LodePNGInfo* info) +{ + LodePNGText_cleanup(info); +} + +unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) +{ + char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1))); + char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1))); + if(!new_keys || !new_strings) + { + lodepng_free(new_keys); + lodepng_free(new_strings); + return 83; /*alloc fail*/ + } + + ++info->text_num; + info->text_keys = new_keys; + info->text_strings = new_strings; + + string_init(&info->text_keys[info->text_num - 1]); + string_set(&info->text_keys[info->text_num - 1], key); + + string_init(&info->text_strings[info->text_num - 1]); + string_set(&info->text_strings[info->text_num - 1], str); + + return 0; +} + +/******************************************************************************/ + +static void LodePNGIText_init(LodePNGInfo* info) +{ + info->itext_num = 0; + info->itext_keys = NULL; + info->itext_langtags = NULL; + info->itext_transkeys = NULL; + info->itext_strings = NULL; +} + +static void LodePNGIText_cleanup(LodePNGInfo* info) +{ + size_t i; + for(i = 0; i != info->itext_num; ++i) + { + string_cleanup(&info->itext_keys[i]); + string_cleanup(&info->itext_langtags[i]); + string_cleanup(&info->itext_transkeys[i]); + string_cleanup(&info->itext_strings[i]); + } + lodepng_free(info->itext_keys); + lodepng_free(info->itext_langtags); + lodepng_free(info->itext_transkeys); + lodepng_free(info->itext_strings); +} + +static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) +{ + size_t i = 0; + dest->itext_keys = 0; + dest->itext_langtags = 0; + dest->itext_transkeys = 0; + dest->itext_strings = 0; + dest->itext_num = 0; + for(i = 0; i != source->itext_num; ++i) + { + CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], + source->itext_transkeys[i], source->itext_strings[i])); + } + return 0; +} + +void lodepng_clear_itext(LodePNGInfo* info) +{ + LodePNGIText_cleanup(info); +} + +unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str) +{ + char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1))); + char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1))); + char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1))); + char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1))); + if(!new_keys || !new_langtags || !new_transkeys || !new_strings) + { + lodepng_free(new_keys); + lodepng_free(new_langtags); + lodepng_free(new_transkeys); + lodepng_free(new_strings); + return 83; /*alloc fail*/ + } + + ++info->itext_num; + info->itext_keys = new_keys; + info->itext_langtags = new_langtags; + info->itext_transkeys = new_transkeys; + info->itext_strings = new_strings; + + string_init(&info->itext_keys[info->itext_num - 1]); + string_set(&info->itext_keys[info->itext_num - 1], key); + + string_init(&info->itext_langtags[info->itext_num - 1]); + string_set(&info->itext_langtags[info->itext_num - 1], langtag); + + string_init(&info->itext_transkeys[info->itext_num - 1]); + string_set(&info->itext_transkeys[info->itext_num - 1], transkey); + + string_init(&info->itext_strings[info->itext_num - 1]); + string_set(&info->itext_strings[info->itext_num - 1], str); + + return 0; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +void lodepng_info_init(LodePNGInfo* info) +{ + lodepng_color_mode_init(&info->color); + info->interlace_method = 0; + info->compression_method = 0; + info->filter_method = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + info->background_defined = 0; + info->background_r = info->background_g = info->background_b = 0; + + LodePNGText_init(info); + LodePNGIText_init(info); + + info->time_defined = 0; + info->phys_defined = 0; + + LodePNGUnknownChunks_init(info); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +void lodepng_info_cleanup(LodePNGInfo* info) +{ + lodepng_color_mode_cleanup(&info->color); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + LodePNGText_cleanup(info); + LodePNGIText_cleanup(info); + + LodePNGUnknownChunks_cleanup(info); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) +{ + lodepng_info_cleanup(dest); + *dest = *source; + lodepng_color_mode_init(&dest->color); + CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color)); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + CERROR_TRY_RETURN(LodePNGText_copy(dest, source)); + CERROR_TRY_RETURN(LodePNGIText_copy(dest, source)); + + LodePNGUnknownChunks_init(dest); + CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source)); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + return 0; +} + +void lodepng_info_swap(LodePNGInfo* a, LodePNGInfo* b) +{ + LodePNGInfo temp = *a; + *a = *b; + *b = temp; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +/*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/ +static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) +{ + unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/ + /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/ + unsigned p = index & m; + in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/ + in = in << (bits * (m - p)); + if(p == 0) out[index * bits / 8] = in; + else out[index * bits / 8] |= in; +} + +typedef struct ColorTree ColorTree; + +/* +One node of a color tree +This is the data structure used to count the number of unique colors and to get a palette +index for a color. It's like an octree, but because the alpha channel is used too, each +node has 16 instead of 8 children. +*/ +struct ColorTree +{ + ColorTree* children[16]; /*up to 16 pointers to ColorTree of next level*/ + int index; /*the payload. Only has a meaningful value if this is in the last level*/ +}; + +static void color_tree_init(ColorTree* tree) +{ + int i; + for(i = 0; i != 16; ++i) tree->children[i] = 0; + tree->index = -1; +} + +static void color_tree_cleanup(ColorTree* tree) +{ + int i; + for(i = 0; i != 16; ++i) + { + if(tree->children[i]) + { + color_tree_cleanup(tree->children[i]); + lodepng_free(tree->children[i]); + } + } +} + +/*returns -1 if color not present, its index otherwise*/ +static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + int bit = 0; + for(bit = 0; bit < 8; ++bit) + { + int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); + if(!tree->children[i]) return -1; + else tree = tree->children[i]; + } + return tree ? tree->index : -1; +} + +#ifdef LODEPNG_COMPILE_ENCODER +static int color_tree_has(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return color_tree_get(tree, r, g, b, a) >= 0; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/*color is not allowed to already exist. +Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")*/ +static void color_tree_add(ColorTree* tree, + unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) +{ + int bit; + for(bit = 0; bit < 8; ++bit) + { + int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); + if(!tree->children[i]) + { + tree->children[i] = (ColorTree*)lodepng_malloc(sizeof(ColorTree)); + color_tree_init(tree->children[i]); + } + tree = tree->children[i]; + } + tree->index = (int)index; +} + +/*put a pixel, given its RGBA color, into image of any color type*/ +static unsigned rgba8ToPixel(unsigned char* out, size_t i, + const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + if(mode->colortype == LCT_GREY) + { + unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; + if(mode->bitdepth == 8) out[i] = grey; + else if(mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = grey; + else + { + /*take the most significant bits of grey*/ + grey = (grey >> (8 - mode->bitdepth)) & ((1 << mode->bitdepth) - 1); + addColorBits(out, i, mode->bitdepth, grey); + } + } + else if(mode->colortype == LCT_RGB) + { + if(mode->bitdepth == 8) + { + out[i * 3 + 0] = r; + out[i * 3 + 1] = g; + out[i * 3 + 2] = b; + } + else + { + out[i * 6 + 0] = out[i * 6 + 1] = r; + out[i * 6 + 2] = out[i * 6 + 3] = g; + out[i * 6 + 4] = out[i * 6 + 5] = b; + } + } + else if(mode->colortype == LCT_PALETTE) + { + int index = color_tree_get(tree, r, g, b, a); + if(index < 0) return 82; /*color not in palette*/ + if(mode->bitdepth == 8) out[i] = index; + else addColorBits(out, i, mode->bitdepth, (unsigned)index); + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; + if(mode->bitdepth == 8) + { + out[i * 2 + 0] = grey; + out[i * 2 + 1] = a; + } + else if(mode->bitdepth == 16) + { + out[i * 4 + 0] = out[i * 4 + 1] = grey; + out[i * 4 + 2] = out[i * 4 + 3] = a; + } + } + else if(mode->colortype == LCT_RGBA) + { + if(mode->bitdepth == 8) + { + out[i * 4 + 0] = r; + out[i * 4 + 1] = g; + out[i * 4 + 2] = b; + out[i * 4 + 3] = a; + } + else + { + out[i * 8 + 0] = out[i * 8 + 1] = r; + out[i * 8 + 2] = out[i * 8 + 3] = g; + out[i * 8 + 4] = out[i * 8 + 5] = b; + out[i * 8 + 6] = out[i * 8 + 7] = a; + } + } + + return 0; /*no error*/ +} + +/*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/ +static void rgba16ToPixel(unsigned char* out, size_t i, + const LodePNGColorMode* mode, + unsigned short r, unsigned short g, unsigned short b, unsigned short a) +{ + if(mode->colortype == LCT_GREY) + { + unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; + out[i * 2 + 0] = (grey >> 8) & 255; + out[i * 2 + 1] = grey & 255; + } + else if(mode->colortype == LCT_RGB) + { + out[i * 6 + 0] = (r >> 8) & 255; + out[i * 6 + 1] = r & 255; + out[i * 6 + 2] = (g >> 8) & 255; + out[i * 6 + 3] = g & 255; + out[i * 6 + 4] = (b >> 8) & 255; + out[i * 6 + 5] = b & 255; + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; + out[i * 4 + 0] = (grey >> 8) & 255; + out[i * 4 + 1] = grey & 255; + out[i * 4 + 2] = (a >> 8) & 255; + out[i * 4 + 3] = a & 255; + } + else if(mode->colortype == LCT_RGBA) + { + out[i * 8 + 0] = (r >> 8) & 255; + out[i * 8 + 1] = r & 255; + out[i * 8 + 2] = (g >> 8) & 255; + out[i * 8 + 3] = g & 255; + out[i * 8 + 4] = (b >> 8) & 255; + out[i * 8 + 5] = b & 255; + out[i * 8 + 6] = (a >> 8) & 255; + out[i * 8 + 7] = a & 255; + } +} + +/*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/ +static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, + unsigned char* b, unsigned char* a, + const unsigned char* in, size_t i, + const LodePNGColorMode* mode) +{ + if(mode->colortype == LCT_GREY) + { + if(mode->bitdepth == 8) + { + *r = *g = *b = in[i]; + if(mode->key_defined && *r == mode->key_r) *a = 0; + else *a = 255; + } + else if(mode->bitdepth == 16) + { + *r = *g = *b = in[i * 2 + 0]; + if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; + else *a = 255; + } + else + { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = i * mode->bitdepth; + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + *r = *g = *b = (value * 255) / highest; + if(mode->key_defined && value == mode->key_r) *a = 0; + else *a = 255; + } + } + else if(mode->colortype == LCT_RGB) + { + if(mode->bitdepth == 8) + { + *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2]; + if(mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0; + else *a = 255; + } + else + { + *r = in[i * 6 + 0]; + *g = in[i * 6 + 2]; + *b = in[i * 6 + 4]; + if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + else *a = 255; + } + } + else if(mode->colortype == LCT_PALETTE) + { + unsigned index; + if(mode->bitdepth == 8) index = in[i]; + else + { + size_t j = i * mode->bitdepth; + index = readBitsFromReversedStream(&j, in, mode->bitdepth); + } + + if(index >= mode->palettesize) + { + /*This is an error according to the PNG spec, but common PNG decoders make it black instead. + Done here too, slightly faster due to no error handling needed.*/ + *r = *g = *b = 0; + *a = 255; + } + else + { + *r = mode->palette[index * 4 + 0]; + *g = mode->palette[index * 4 + 1]; + *b = mode->palette[index * 4 + 2]; + *a = mode->palette[index * 4 + 3]; + } + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + if(mode->bitdepth == 8) + { + *r = *g = *b = in[i * 2 + 0]; + *a = in[i * 2 + 1]; + } + else + { + *r = *g = *b = in[i * 4 + 0]; + *a = in[i * 4 + 2]; + } + } + else if(mode->colortype == LCT_RGBA) + { + if(mode->bitdepth == 8) + { + *r = in[i * 4 + 0]; + *g = in[i * 4 + 1]; + *b = in[i * 4 + 2]; + *a = in[i * 4 + 3]; + } + else + { + *r = in[i * 8 + 0]; + *g = in[i * 8 + 2]; + *b = in[i * 8 + 4]; + *a = in[i * 8 + 6]; + } + } +} + +/*Similar to getPixelColorRGBA8, but with all the for loops inside of the color +mode test cases, optimized to convert the colors much faster, when converting +to RGBA or RGB with 8 bit per cannel. buffer must be RGBA or RGB output with +enough memory, if has_alpha is true the output is RGBA. mode has the color mode +of the input buffer.*/ +static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, + unsigned has_alpha, const unsigned char* in, + const LodePNGColorMode* mode) +{ + unsigned num_channels = has_alpha ? 4 : 3; + size_t i; + if(mode->colortype == LCT_GREY) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i]; + if(has_alpha) buffer[3] = mode->key_defined && in[i] == mode->key_r ? 0 : 255; + } + } + else if(mode->bitdepth == 16) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i * 2]; + if(has_alpha) buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; + } + } + else + { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; + if(has_alpha) buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255; + } + } + } + else if(mode->colortype == LCT_RGB) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 3 + 0]; + buffer[1] = in[i * 3 + 1]; + buffer[2] = in[i * 3 + 2]; + if(has_alpha) buffer[3] = mode->key_defined && buffer[0] == mode->key_r + && buffer[1]== mode->key_g && buffer[2] == mode->key_b ? 0 : 255; + } + } + else + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 6 + 0]; + buffer[1] = in[i * 6 + 2]; + buffer[2] = in[i * 6 + 4]; + if(has_alpha) buffer[3] = mode->key_defined + && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255; + } + } + } + else if(mode->colortype == LCT_PALETTE) + { + unsigned index; + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + if(mode->bitdepth == 8) index = in[i]; + else index = readBitsFromReversedStream(&j, in, mode->bitdepth); + + if(index >= mode->palettesize) + { + /*This is an error according to the PNG spec, but most PNG decoders make it black instead. + Done here too, slightly faster due to no error handling needed.*/ + buffer[0] = buffer[1] = buffer[2] = 0; + if(has_alpha) buffer[3] = 255; + } + else + { + buffer[0] = mode->palette[index * 4 + 0]; + buffer[1] = mode->palette[index * 4 + 1]; + buffer[2] = mode->palette[index * 4 + 2]; + if(has_alpha) buffer[3] = mode->palette[index * 4 + 3]; + } + } + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; + if(has_alpha) buffer[3] = in[i * 2 + 1]; + } + } + else + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; + if(has_alpha) buffer[3] = in[i * 4 + 2]; + } + } + } + else if(mode->colortype == LCT_RGBA) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 4 + 0]; + buffer[1] = in[i * 4 + 1]; + buffer[2] = in[i * 4 + 2]; + if(has_alpha) buffer[3] = in[i * 4 + 3]; + } + } + else + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 8 + 0]; + buffer[1] = in[i * 8 + 2]; + buffer[2] = in[i * 8 + 4]; + if(has_alpha) buffer[3] = in[i * 8 + 6]; + } + } + } +} + +/*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with +given color type, but the given color type must be 16-bit itself.*/ +static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, + const unsigned char* in, size_t i, const LodePNGColorMode* mode) +{ + if(mode->colortype == LCT_GREY) + { + *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1]; + if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; + else *a = 65535; + } + else if(mode->colortype == LCT_RGB) + { + *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; + *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; + *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; + if(mode->key_defined + && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + else *a = 65535; + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; + *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; + } + else if(mode->colortype == LCT_RGBA) + { + *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; + *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; + *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; + *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; + } +} + +unsigned lodepng_convert(unsigned char* out, const unsigned char* in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h) +{ + int i; + ColorTree tree; + size_t numpixels = w * h; + + if(lodepng_color_mode_equal(mode_out, mode_in)) + { + size_t numbytes = lodepng_get_raw_size(w, h, mode_in); + for(i = 0; i != numbytes; ++i) out[i] = in[i]; + return 0; + } + + if(mode_out->colortype == LCT_PALETTE) + { + size_t palettesize = mode_out->palettesize; + const unsigned char* palette = mode_out->palette; + size_t palsize = 1u << mode_out->bitdepth; + /*if the user specified output palette but did not give the values, assume + they want the values of the input color type (assuming that one is palette). + Note that we never create a new palette ourselves.*/ + if(palettesize == 0) + { + palettesize = mode_in->palettesize; + palette = mode_in->palette; + } + if(palettesize < palsize) palsize = palettesize; + color_tree_init(&tree); + for(i = 0; i != palsize; ++i) + { + const unsigned char* p = &palette[i * 4]; + color_tree_add(&tree, p[0], p[1], p[2], p[3], i); + } + } + + if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) + { + for(i = 0; i != numpixels; ++i) + { + unsigned short r = 0, g = 0, b = 0, a = 0; + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); + rgba16ToPixel(out, i, mode_out, r, g, b, a); + } + } + else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) + { + getPixelColorsRGBA8(out, numpixels, 1, in, mode_in); + } + else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) + { + getPixelColorsRGBA8(out, numpixels, 0, in, mode_in); + } + else + { + unsigned char r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); + CERROR_TRY_RETURN(rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a)); + } + } + + if(mode_out->colortype == LCT_PALETTE) + { + color_tree_cleanup(&tree); + } + + return 0; /*no error*/ +} + +#ifdef LODEPNG_COMPILE_ENCODER + +void lodepng_color_profile_init(LodePNGColorProfile* profile) +{ + profile->colored = 0; + profile->key = 0; + profile->alpha = 0; + profile->key_r = profile->key_g = profile->key_b = 0; + profile->numcolors = 0; + profile->bits = 1; +} + +/*function used for debug purposes with C++*/ +/*void printColorProfile(LodePNGColorProfile* p) +{ + std::cout << "colored: " << (int)p->colored << ", "; + std::cout << "key: " << (int)p->key << ", "; + std::cout << "key_r: " << (int)p->key_r << ", "; + std::cout << "key_g: " << (int)p->key_g << ", "; + std::cout << "key_b: " << (int)p->key_b << ", "; + std::cout << "alpha: " << (int)p->alpha << ", "; + std::cout << "numcolors: " << (int)p->numcolors << ", "; + std::cout << "bits: " << (int)p->bits << std::endl; +}*/ + +/*Returns how many bits needed to represent given value (max 8 bit)*/ +static unsigned getValueRequiredBits(unsigned char value) +{ + if(value == 0 || value == 255) return 1; + /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/ + if(value % 17 == 0) return value % 85 == 0 ? 2 : 4; + return 8; +} + +/*profile must already have been inited with mode. +It's ok to set some parameters of profile to done already.*/ +unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, + const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* mode) +{ + unsigned error = 0; + size_t i; + ColorTree tree; + size_t numpixels = w * h; + + unsigned colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0; + unsigned alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1; + unsigned numcolors_done = 0; + unsigned bpp = lodepng_get_bpp(mode); + unsigned bits_done = bpp == 1 ? 1 : 0; + unsigned maxnumcolors = 257; + unsigned sixteen = 0; + if(bpp <= 8) maxnumcolors = bpp == 1 ? 2 : (bpp == 2 ? 4 : (bpp == 4 ? 16 : 256)); + + color_tree_init(&tree); + + /*Check if the 16-bit input is truly 16-bit*/ + if(mode->bitdepth == 16) + { + unsigned short r, g, b, a; + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || + (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ + { + sixteen = 1; + break; + } + } + } + + if(sixteen) + { + unsigned short r = 0, g = 0, b = 0, a = 0; + profile->bits = 16; + bits_done = numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ + + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + + if(!colored_done && (r != g || r != b)) + { + profile->colored = 1; + colored_done = 1; + } + + if(!alpha_done) + { + unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); + if(a != 65535 && (a != 0 || (profile->key && !matchkey))) + { + profile->alpha = 1; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + else if(a == 0 && !profile->alpha && !profile->key) + { + profile->key = 1; + profile->key_r = r; + profile->key_g = g; + profile->key_b = b; + } + else if(a == 65535 && profile->key && matchkey) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + alpha_done = 1; + } + } + + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } + } + else /* < 16-bit */ + { + for(i = 0; i != numpixels; ++i) + { + unsigned char r = 0, g = 0, b = 0, a = 0; + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); + + if(!bits_done && profile->bits < 8) + { + /*only r is checked, < 8 bits is only relevant for greyscale*/ + unsigned bits = getValueRequiredBits(r); + if(bits > profile->bits) profile->bits = bits; + } + bits_done = (profile->bits >= bpp); + + if(!colored_done && (r != g || r != b)) + { + profile->colored = 1; + colored_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/ + } + + if(!alpha_done) + { + unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); + if(a != 255 && (a != 0 || (profile->key && !matchkey))) + { + profile->alpha = 1; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + else if(a == 0 && !profile->alpha && !profile->key) + { + profile->key = 1; + profile->key_r = r; + profile->key_g = g; + profile->key_b = b; + } + else if(a == 255 && profile->key && matchkey) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + } + + if(!numcolors_done) + { + if(!color_tree_has(&tree, r, g, b, a)) + { + color_tree_add(&tree, r, g, b, a, profile->numcolors); + if(profile->numcolors < 256) + { + unsigned char* p = profile->palette; + unsigned n = profile->numcolors; + p[n * 4 + 0] = r; + p[n * 4 + 1] = g; + p[n * 4 + 2] = b; + p[n * 4 + 3] = a; + } + ++profile->numcolors; + numcolors_done = profile->numcolors >= maxnumcolors; + } + } + + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } + + /*make the profile's key always 16-bit for consistency - repeat each byte twice*/ + profile->key_r += (profile->key_r << 8); + profile->key_g += (profile->key_g << 8); + profile->key_b += (profile->key_b << 8); + } + + color_tree_cleanup(&tree); + return error; +} + +/*Automatically chooses color type that gives smallest amount of bits in the +output image, e.g. grey if there are only greyscale pixels, palette if there +are less than 256 colors, ... +Updates values of mode with a potentially smaller color model. mode_out should +contain the user chosen color model, but will be overwritten with the new chosen one.*/ +unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in) +{ + LodePNGColorProfile prof; + unsigned error = 0; + unsigned i, n, palettebits, grey_ok, palette_ok; + + lodepng_color_profile_init(&prof); + error = lodepng_get_color_profile(&prof, image, w, h, mode_in); + if(error) return error; + mode_out->key_defined = 0; + + if(prof.key && w * h <= 16) + { + prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ + if(prof.bits < 8) prof.bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + grey_ok = !prof.colored && !prof.alpha; /*grey without alpha, with potentially low bits*/ + n = prof.numcolors; + palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); + palette_ok = n <= 256 && (n * 2 < w * h) && prof.bits <= 8; + if(w * h < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ + if(grey_ok && prof.bits <= palettebits) palette_ok = 0; /*grey is less overhead*/ + + if(palette_ok) + { + unsigned char* p = prof.palette; + lodepng_palette_clear(mode_out); /*remove potential earlier palette*/ + for(i = 0; i != prof.numcolors; ++i) + { + error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); + if(error) break; + } + + mode_out->colortype = LCT_PALETTE; + mode_out->bitdepth = palettebits; + + if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize + && mode_in->bitdepth == mode_out->bitdepth) + { + /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/ + lodepng_color_mode_cleanup(mode_out); + lodepng_color_mode_copy(mode_out, mode_in); + } + } + else /*8-bit or 16-bit per channel*/ + { + mode_out->bitdepth = prof.bits; + mode_out->colortype = prof.alpha ? (prof.colored ? LCT_RGBA : LCT_GREY_ALPHA) + : (prof.colored ? LCT_RGB : LCT_GREY); + + if(prof.key && !prof.alpha) + { + unsigned mask = (1u << mode_out->bitdepth) - 1u; /*profile always uses 16-bit, mask converts it*/ + mode_out->key_r = prof.key_r & mask; + mode_out->key_g = prof.key_g & mask; + mode_out->key_b = prof.key_b & mask; + mode_out->key_defined = 1; + } + } + + return error; +} + +#endif /* #ifdef LODEPNG_COMPILE_ENCODER */ + +/* +Paeth predicter, used by PNG filter type 4 +The parameters are of type short, but should come from unsigned chars, the shorts +are only needed to make the paeth calculation correct. +*/ +static unsigned char paethPredictor(short a, short b, short c) +{ + short pa = abs(b - c); + short pb = abs(a - c); + short pc = abs(a + b - c - c); + + if(pc < pa && pc < pb) return (unsigned char)c; + else if(pb < pa) return (unsigned char)b; + else return (unsigned char)a; +} + +/*shared values used by multiple Adam7 related functions*/ + +static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ +static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ +static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ +static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ + +/* +Outputs various dimensions and positions in the image related to the Adam7 reduced images. +passw: output containing the width of the 7 passes +passh: output containing the height of the 7 passes +filter_passstart: output containing the index of the start and end of each + reduced image with filter bytes +padded_passstart output containing the index of the start and end of each + reduced image when without filter bytes but with padded scanlines +passstart: output containing the index of the start and end of each reduced + image without padding between scanlines, but still padding between the images +w, h: width and height of non-interlaced image +bpp: bits per pixel +"padded" is only relevant if bpp is less than 8 and a scanline or image does not + end at a full byte +*/ +static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], + size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) +{ + /*the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass*/ + unsigned i; + + /*calculate width and height in pixels of each pass*/ + for(i = 0; i != 7; ++i) + { + passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; + passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; + if(passw[i] == 0) passh[i] = 0; + if(passh[i] == 0) passw[i] = 0; + } + + filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; + for(i = 0; i != 7; ++i) + { + /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/ + filter_passstart[i + 1] = filter_passstart[i] + + ((passw[i] && passh[i]) ? passh[i] * (1 + (passw[i] * bpp + 7) / 8) : 0); + /*bits padded if needed to fill full byte at end of each scanline*/ + padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7) / 8); + /*only padded at end of reduced image*/ + passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; + } +} + +#ifdef LODEPNG_COMPILE_DECODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG Decoder / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*read the information from the header and store it in the LodePNGInfo. return value is error*/ +unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, + const unsigned char* in, size_t insize) +{ + LodePNGInfo* info = &state->info_png; + if(insize == 0 || in == 0) + { + CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ + } + if(insize < 33) + { + CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ + } + + /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ + lodepng_info_cleanup(info); + lodepng_info_init(info); + + if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 + || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) + { + CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ + } + if(lodepng_chunk_length(in + 8) != 13) + { + CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ + } + if(!lodepng_chunk_type_equals(in + 8, "IHDR")) + { + CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ + } + + /*read the values given in the header*/ + *w = lodepng_read32bitInt(&in[16]); + *h = lodepng_read32bitInt(&in[20]); + info->color.bitdepth = in[24]; + info->color.colortype = (LodePNGColorType)in[25]; + info->compression_method = in[26]; + info->filter_method = in[27]; + info->interlace_method = in[28]; + + if(*w == 0 || *h == 0) + { + CERROR_RETURN_ERROR(state->error, 93); + } + + if(!state->decoder.ignore_crc) + { + unsigned CRC = lodepng_read32bitInt(&in[29]); + unsigned checksum = lodepng_crc32(&in[12], 17); + if(CRC != checksum) + { + CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/ + } + } + + /*error: only compression method 0 is allowed in the specification*/ + if(info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32); + /*error: only filter method 0 is allowed in the specification*/ + if(info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33); + /*error: only interlace methods 0 and 1 exist in the specification*/ + if(info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34); + + state->error = checkColorValidity(info->color.colortype, info->color.bitdepth); + return state->error; +} + +static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, + size_t bytewidth, unsigned char filterType, size_t length) +{ + /* + For PNG filter method 0 + unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, + the filter works byte per byte (bytewidth = 1) + precon is the previous unfiltered scanline, recon the result, scanline the current one + the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead + recon and scanline MAY be the same memory address! precon must be disjoint. + */ + + size_t i; + switch(filterType) + { + case 0: + for(i = 0; i != length; ++i) recon[i] = scanline[i]; + break; + case 1: + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; + break; + case 2: + if(precon) + { + for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; + } + else + { + for(i = 0; i != length; ++i) recon[i] = scanline[i]; + } + break; + case 3: + if(precon) + { + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1); + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1); + } + else + { + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1); + } + break; + case 4: + if(precon) + { + for(i = 0; i != bytewidth; ++i) + { + recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ + } + for(i = bytewidth; i < length; ++i) + { + recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); + } + } + else + { + for(i = 0; i != bytewidth; ++i) + { + recon[i] = scanline[i]; + } + for(i = bytewidth; i < length; ++i) + { + /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ + recon[i] = (scanline[i] + recon[i - bytewidth]); + } + } + break; + default: return 36; /*error: unexisting filter type given*/ + } + return 0; +} + +static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) +{ + /* + For PNG filter method 0 + this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times) + out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline + w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel + in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) + */ + + unsigned y; + unsigned char* prevline = 0; + + /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ + size_t bytewidth = (bpp + 7) / 8; + size_t linebytes = (w * bpp + 7) / 8; + + for(y = 0; y < h; ++y) + { + size_t outindex = linebytes * y; + size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + unsigned char filterType = in[inindex]; + + CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes)); + + prevline = &out[outindex]; + } + + return 0; +} + +/* +in: Adam7 interlaced image, with no padding bits between scanlines, but between + reduced images so that each reduced image starts at a byte. +out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h +bpp: bits per pixel +out has the following size in bits: w * h * bpp. +in is possibly bigger due to padding bits between reduced images. +out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation +(because that's likely a little bit faster) +NOTE: comments about padding bits are only relevant if bpp < 8 +*/ +static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) +{ + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + if(bpp >= 8) + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + size_t bytewidth = bpp / 8; + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; + size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; + for(b = 0; b < bytewidth; ++b) + { + out[pixeloutstart + b] = in[pixelinstart + b]; + } + } + } + } + else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + unsigned ilinebits = bpp * passw[i]; + unsigned olinebits = bpp * w; + size_t obp, ibp; /*bit pointers (for out and in buffer)*/ + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); + obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; + for(b = 0; b < bpp; ++b) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/ + setBitOfReversedStream0(&obp, out, bit); + } + } + } + } +} + +static void removePaddingBits(unsigned char* out, const unsigned char* in, + size_t olinebits, size_t ilinebits, unsigned h) +{ + /* + After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need + to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers + for the Adam7 code, the color convert code and the output to the user. + in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must + have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits + also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 + only useful if (ilinebits - olinebits) is a value in the range 1..7 + */ + unsigned y; + size_t diff = ilinebits - olinebits; + size_t ibp = 0, obp = 0; /*input and output bit pointers*/ + for(y = 0; y < h; ++y) + { + size_t x; + for(x = 0; x < olinebits; ++x) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + ibp += diff; + } +} + +/*out must be buffer big enough to contain full image, and in must contain the full decompressed data from +the IDAT chunks (with filter index bytes and possible padding bits) +return value is error*/ +static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, + unsigned w, unsigned h, const LodePNGInfo* info_png) +{ + /* + This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. + Steps: + *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8) + *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace + NOTE: the in buffer will be overwritten with intermediate data! + */ + unsigned bpp = lodepng_get_bpp(&info_png->color); + if(bpp == 0) return 31; /*error: invalid colortype*/ + + if(info_png->interlace_method == 0) + { + if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) + { + CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); + removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); + } + /*we can immediately filter into the out buffer, no other steps needed*/ + else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); + } + else /*interlace_method is 1 (Adam7)*/ + { + unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + for(i = 0; i != 7; ++i) + { + CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); + /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, + move bytes instead of bits or move not at all*/ + if(bpp < 8) + { + /*remove padding bits in scanlines; after this there still may be padding + bits between the different reduced images: each reduced image still starts nicely at a byte*/ + removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, + ((passw[i] * bpp + 7) / 8) * 8, passh[i]); + } + } + + Adam7_deinterlace(out, in, w, h, bpp); + } + + return 0; +} + +static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) +{ + unsigned pos = 0, i; + if(color->palette) lodepng_free(color->palette); + color->palettesize = chunkLength / 3; + color->palette = (unsigned char*)lodepng_malloc(4 * color->palettesize); + if(!color->palette && color->palettesize) + { + color->palettesize = 0; + return 83; /*alloc fail*/ + } + if(color->palettesize > 256) return 38; /*error: palette too big*/ + + for(i = 0; i != color->palettesize; ++i) + { + color->palette[4 * i + 0] = data[pos++]; /*R*/ + color->palette[4 * i + 1] = data[pos++]; /*G*/ + color->palette[4 * i + 2] = data[pos++]; /*B*/ + color->palette[4 * i + 3] = 255; /*alpha*/ + } + + return 0; /* OK */ +} + +static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) +{ + unsigned i; + if(color->colortype == LCT_PALETTE) + { + /*error: more alpha values given than there are palette entries*/ + if(chunkLength > color->palettesize) return 38; + + for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; + } + else if(color->colortype == LCT_GREY) + { + /*error: this chunk must be 2 bytes for greyscale image*/ + if(chunkLength != 2) return 30; + + color->key_defined = 1; + color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1]; + } + else if(color->colortype == LCT_RGB) + { + /*error: this chunk must be 6 bytes for RGB image*/ + if(chunkLength != 6) return 41; + + color->key_defined = 1; + color->key_r = 256u * data[0] + data[1]; + color->key_g = 256u * data[2] + data[3]; + color->key_b = 256u * data[4] + data[5]; + } + else return 42; /*error: tRNS chunk not allowed for other color models*/ + + return 0; /* OK */ +} + + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*background color chunk (bKGD)*/ +static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(info->color.colortype == LCT_PALETTE) + { + /*error: this chunk must be 1 byte for indexed color image*/ + if(chunkLength != 1) return 43; + + info->background_defined = 1; + info->background_r = info->background_g = info->background_b = data[0]; + } + else if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) + { + /*error: this chunk must be 2 bytes for greyscale image*/ + if(chunkLength != 2) return 44; + + info->background_defined = 1; + info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1]; + } + else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) + { + /*error: this chunk must be 6 bytes for greyscale image*/ + if(chunkLength != 6) return 45; + + info->background_defined = 1; + info->background_r = 256u * data[0] + data[1]; + info->background_g = 256u * data[2] + data[3]; + info->background_b = 256u * data[4] + data[5]; + } + + return 0; /* OK */ +} + +/*text chunk (tEXt)*/ +static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + char *key = 0, *str = 0; + unsigned i; + + while(!error) /*not really a while loop, only used to break on error*/ + { + unsigned length, string2_begin; + + length = 0; + while(length < chunkLength && data[length] != 0) ++length; + /*even though it's not allowed by the standard, no error is thrown if + there's no null termination char, if the text is empty*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + key[length] = 0; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; + + string2_begin = length + 1; /*skip keyword null terminator*/ + + length = chunkLength < string2_begin ? 0 : (unsigned int)(chunkLength - string2_begin); + str = (char*)lodepng_malloc(length + 1); + if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ + + str[length] = 0; + for(i = 0; i != length; ++i) str[i] = (char)data[string2_begin + i]; + + error = lodepng_add_text(info, key, str); + + break; + } + + lodepng_free(key); + lodepng_free(str); + + return error; +} + +/*compressed text chunk (zTXt)*/ +static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, + const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + unsigned i; + + unsigned length, string2_begin; + char *key = 0; + ucvector decoded; + + ucvector_init(&decoded); + + while(!error) /*not really a while loop, only used to break on error*/ + { + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + key[length] = 0; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; + + if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ + + string2_begin = length + 2; + if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ + + length = (unsigned int)(chunkLength - string2_begin); + /*will fail if zlib error, e.g. if length is too small*/ + error = zlib_decompress(&decoded.data, &decoded.size, + (unsigned char*)(&data[string2_begin]), + length, zlibsettings); + if(error) break; + ucvector_push_back(&decoded, 0); + + error = lodepng_add_text(info, key, (char*)decoded.data); + + break; + } + + lodepng_free(key); + ucvector_cleanup(&decoded); + + return error; +} + +/*international text chunk (iTXt)*/ +static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, + const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + unsigned i; + + unsigned length, begin, compressed; + char *key = 0, *langtag = 0, *transkey = 0; + ucvector decoded; + ucvector_init(&decoded); + + while(!error) /*not really a while loop, only used to break on error*/ + { + /*Quick check if the chunk length isn't too small. Even without check + it'd still fail with other error checks below if it's too short. This just gives a different error code.*/ + if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/ + + /*read the key*/ + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + key[length] = 0; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; + + /*read the compression method*/ + compressed = data[length + 1]; + if(data[length + 2] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ + + /*even though it's not allowed by the standard, no error is thrown if + there's no null termination char, if the text is empty for the next 3 texts*/ + + /*read the langtag*/ + begin = length + 3; + length = 0; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; + + langtag = (char*)lodepng_malloc(length + 1); + if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/ + + langtag[length] = 0; + for(i = 0; i != length; ++i) langtag[i] = (char)data[begin + i]; + + /*read the transkey*/ + begin += length + 1; + length = 0; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; + + transkey = (char*)lodepng_malloc(length + 1); + if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/ + + transkey[length] = 0; + for(i = 0; i != length; ++i) transkey[i] = (char)data[begin + i]; + + /*read the actual text*/ + begin += length + 1; + + length = chunkLength < begin ? 0 : (unsigned int)(chunkLength - begin); + + if(compressed) + { + /*will fail if zlib error, e.g. if length is too small*/ + error = zlib_decompress(&decoded.data, &decoded.size, + (unsigned char*)(&data[begin]), + length, zlibsettings); + if(error) break; + if(decoded.allocsize < decoded.size) decoded.allocsize = decoded.size; + ucvector_push_back(&decoded, 0); + } + else + { + if(!ucvector_resize(&decoded, length + 1)) CERROR_BREAK(error, 83 /*alloc fail*/); + + decoded.data[length] = 0; + for(i = 0; i != length; ++i) decoded.data[i] = data[begin + i]; + } + + error = lodepng_add_itext(info, key, langtag, transkey, (char*)decoded.data); + + break; + } + + lodepng_free(key); + lodepng_free(langtag); + lodepng_free(transkey); + ucvector_cleanup(&decoded); + + return error; +} + +static unsigned readChunk_tIME(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 7) return 73; /*invalid tIME chunk size*/ + + info->time_defined = 1; + info->time.year = 256u * data[0] + data[1]; + info->time.month = data[2]; + info->time.day = data[3]; + info->time.hour = data[4]; + info->time.minute = data[5]; + info->time.second = data[6]; + + return 0; /* OK */ +} + +static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/ + + info->phys_defined = 1; + info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; + info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7]; + info->phys_unit = data[8]; + + return 0; /* OK */ +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ +static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize) +{ + unsigned char IEND = 0; + const unsigned char* chunk; + size_t i; + ucvector idat; /*the data from idat chunks*/ + ucvector scanlines; + size_t predict; + size_t numpixels; + + /*for unknown chunk order*/ + unsigned unknown = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + + /*provide some proper output values if error will happen*/ + *out = 0; + + state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ + if(state->error) return; + + numpixels = *w * *h; + + /*multiplication overflow*/ + if(*h != 0 && numpixels / *h != *w) CERROR_RETURN(state->error, 92); + /*multiplication overflow possible further below. Allows up to 2^31-1 pixel + bytes with 16-bit RGBA, the rest is room for filter bytes.*/ + if(numpixels > 268435455) CERROR_RETURN(state->error, 92); + + ucvector_init(&idat); + chunk = &in[33]; /*first byte of the first chunk after the header*/ + + /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. + IDAT data is put at the start of the in buffer*/ + while(!IEND && !state->error) + { + unsigned chunkLength; + const unsigned char* data; /*the data in the chunk*/ + + /*error: size of the in buffer too small to contain next chunk*/ + if((size_t)((chunk - in) + 12) > insize || chunk < in) CERROR_BREAK(state->error, 30); + + /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ + chunkLength = lodepng_chunk_length(chunk); + /*error: chunk length larger than the max PNG chunk size*/ + if(chunkLength > 2147483647) CERROR_BREAK(state->error, 63); + + if((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) + { + CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/ + } + + data = lodepng_chunk_data_const(chunk); + + /*IDAT chunk, containing compressed image data*/ + if(lodepng_chunk_type_equals(chunk, "IDAT")) + { + size_t oldsize = idat.size; + if(!ucvector_resize(&idat, oldsize + chunkLength)) CERROR_BREAK(state->error, 83 /*alloc fail*/); + for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i]; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + critical_pos = 3; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + /*IEND chunk*/ + else if(lodepng_chunk_type_equals(chunk, "IEND")) + { + IEND = 1; + } + /*palette chunk (PLTE)*/ + else if(lodepng_chunk_type_equals(chunk, "PLTE")) + { + state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength); + if(state->error) break; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + critical_pos = 2; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + /*palette transparency chunk (tRNS)*/ + else if(lodepng_chunk_type_equals(chunk, "tRNS")) + { + state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength); + if(state->error) break; + } +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*background color chunk (bKGD)*/ + else if(lodepng_chunk_type_equals(chunk, "bKGD")) + { + state->error = readChunk_bKGD(&state->info_png, data, chunkLength); + if(state->error) break; + } + /*text chunk (tEXt)*/ + else if(lodepng_chunk_type_equals(chunk, "tEXt")) + { + if(state->decoder.read_text_chunks) + { + state->error = readChunk_tEXt(&state->info_png, data, chunkLength); + if(state->error) break; + } + } + /*compressed text chunk (zTXt)*/ + else if(lodepng_chunk_type_equals(chunk, "zTXt")) + { + if(state->decoder.read_text_chunks) + { + state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + if(state->error) break; + } + } + /*international text chunk (iTXt)*/ + else if(lodepng_chunk_type_equals(chunk, "iTXt")) + { + if(state->decoder.read_text_chunks) + { + state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + if(state->error) break; + } + } + else if(lodepng_chunk_type_equals(chunk, "tIME")) + { + state->error = readChunk_tIME(&state->info_png, data, chunkLength); + if(state->error) break; + } + else if(lodepng_chunk_type_equals(chunk, "pHYs")) + { + state->error = readChunk_pHYs(&state->info_png, data, chunkLength); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + else /*it's not an implemented chunk type, so ignore it: skip over the data*/ + { + /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ + if(!lodepng_chunk_ancillary(chunk)) CERROR_BREAK(state->error, 69); + + unknown = 1; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(state->decoder.remember_unknown_chunks) + { + state->error = lodepng_chunk_append(&state->info_png.unknown_chunks_data[critical_pos - 1], + &state->info_png.unknown_chunks_size[critical_pos - 1], chunk); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + + if(!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ + { + if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/ + } + + if(!IEND) chunk = lodepng_chunk_next_const(chunk); + } + + ucvector_init(&scanlines); + /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. + If the decompressed size does not match the prediction, the image must be corrupt.*/ + if(state->info_png.interlace_method == 0) + { + /*The extra *h is added because this are the filter bytes every scanline starts with*/ + predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color) + *h; + } + else + { + /*Adam-7 interlaced: predicted size is the sum of the 7 sub-images sizes*/ + const LodePNGColorMode* color = &state->info_png.color; + predict = 0; + predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); + if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); + predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color) + ((*h + 3) >> 3); + if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color) + ((*h + 3) >> 2); + predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color) + ((*h + 1) >> 2); + if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color) + ((*h + 1) >> 1); + predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color) + ((*h + 0) >> 1); + } + if(!state->error && !ucvector_reserve(&scanlines, predict)) state->error = 83; /*alloc fail*/ + if(!state->error) + { + state->error = zlib_decompress(&scanlines.data, &scanlines.size, idat.data, + idat.size, &state->decoder.zlibsettings); + if(!state->error && scanlines.size != predict) state->error = 91; /*decompressed size doesn't match prediction*/ + } + ucvector_cleanup(&idat); + + if(!state->error) + { + size_t outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!*out) state->error = 83; /*alloc fail*/ + for(i = 0; i < outsize; i++) (*out)[i] = 0; + if(!state->error) state->error = postProcessScanlines(*out, scanlines.data, *w, *h, &state->info_png); + } + ucvector_cleanup(&scanlines); +} + +unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize) +{ + *out = 0; + decodeGeneric(out, w, h, state, in, insize); + if(state->error) return state->error; + if(!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) + { + /*same color type, no copying or converting of data needed*/ + /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype + the raw image has to the end user*/ + if(!state->decoder.color_convert) + { + state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color); + if(state->error) return state->error; + } + } + else + { + /*color conversion needed; sort of copy of the data*/ + unsigned char* data = *out; + size_t outsize; + + /*TODO: check if this works according to the statement in the documentation: "The converter can convert + from greyscale input color type, to 8-bit greyscale or greyscale with alpha"*/ + if(!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) + && !(state->info_raw.bitdepth == 8)) + { + return 56; /*unsupported color mode conversion*/ + } + + outsize = lodepng_get_raw_size(*w, *h, &state->info_raw); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!(*out)) + { + state->error = 83; /*alloc fail*/ + } + else state->error = lodepng_convert(*out, data, &state->info_raw, + &state->info_png.color, *w, *h); + lodepng_free(data); + } + return state->error; +} + +unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, + size_t insize, LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned error; + LodePNGState state; + lodepng_state_init(&state); + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + error = lodepng_decode(out, w, h, &state, in, insize); + lodepng_state_cleanup(&state); + return error; +} + +unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) +{ + return lodepng_decode_memory(out, w, h, in, insize, LCT_RGBA, 8); +} + +unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) +{ + return lodepng_decode_memory(out, w, h, in, insize, LCT_RGB, 8); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, + LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer; + size_t buffersize; + unsigned error; + error = lodepng_load_file(&buffer, &buffersize, filename); + if(!error) error = lodepng_decode_memory(out, w, h, buffer, buffersize, colortype, bitdepth); + lodepng_free(buffer); + return error; +} + +unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) +{ + return lodepng_decode_file(out, w, h, filename, LCT_RGBA, 8); +} + +unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) +{ + return lodepng_decode_file(out, w, h, filename, LCT_RGB, 8); +} +#endif /*LODEPNG_COMPILE_DISK*/ + +void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) +{ + settings->color_convert = 1; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + settings->read_text_chunks = 1; + settings->remember_unknown_chunks = 0; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + settings->ignore_crc = 0; + lodepng_decompress_settings_init(&settings->zlibsettings); +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) + +void lodepng_state_init(LodePNGState* state) +{ +#ifdef LODEPNG_COMPILE_DECODER + lodepng_decoder_settings_init(&state->decoder); +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER + lodepng_encoder_settings_init(&state->encoder); +#endif /*LODEPNG_COMPILE_ENCODER*/ + lodepng_color_mode_init(&state->info_raw); + lodepng_info_init(&state->info_png); + state->error = 1; +} + +void lodepng_state_cleanup(LodePNGState* state) +{ + lodepng_color_mode_cleanup(&state->info_raw); + lodepng_info_cleanup(&state->info_png); +} + +void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source) +{ + lodepng_state_cleanup(dest); + *dest = *source; + lodepng_color_mode_init(&dest->info_raw); + lodepng_info_init(&dest->info_png); + dest->error = lodepng_color_mode_copy(&dest->info_raw, &source->info_raw); if(dest->error) return; + dest->error = lodepng_info_copy(&dest->info_png, &source->info_png); if(dest->error) return; +} + +#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ + +#ifdef LODEPNG_COMPILE_ENCODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG Encoder / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*chunkName must be string of 4 characters*/ +static unsigned addChunk(ucvector* out, const char* chunkName, const unsigned char* data, size_t length) +{ + CERROR_TRY_RETURN(lodepng_chunk_create(&out->data, &out->size, (unsigned)length, chunkName, data)); + out->allocsize = out->size; /*fix the allocsize again*/ + return 0; +} + +static void writeSignature(ucvector* out) +{ + /*8 bytes PNG signature, aka the magic bytes*/ + ucvector_push_back(out, 137); + ucvector_push_back(out, 80); + ucvector_push_back(out, 78); + ucvector_push_back(out, 71); + ucvector_push_back(out, 13); + ucvector_push_back(out, 10); + ucvector_push_back(out, 26); + ucvector_push_back(out, 10); +} + +static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) +{ + unsigned error = 0; + ucvector header; + ucvector_init(&header); + + lodepng_add32bitInt(&header, w); /*width*/ + lodepng_add32bitInt(&header, h); /*height*/ + ucvector_push_back(&header, (unsigned char)bitdepth); /*bit depth*/ + ucvector_push_back(&header, (unsigned char)colortype); /*color type*/ + ucvector_push_back(&header, 0); /*compression method*/ + ucvector_push_back(&header, 0); /*filter method*/ + ucvector_push_back(&header, interlace_method); /*interlace method*/ + + error = addChunk(out, "IHDR", header.data, header.size); + ucvector_cleanup(&header); + + return error; +} + +static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) +{ + unsigned error = 0; + size_t i; + ucvector PLTE; + ucvector_init(&PLTE); + for(i = 0; i != info->palettesize * 4; ++i) + { + /*add all channels except alpha channel*/ + if(i % 4 != 3) ucvector_push_back(&PLTE, info->palette[i]); + } + error = addChunk(out, "PLTE", PLTE.data, PLTE.size); + ucvector_cleanup(&PLTE); + + return error; +} + +static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) +{ + unsigned error = 0; + size_t i; + ucvector tRNS; + ucvector_init(&tRNS); + if(info->colortype == LCT_PALETTE) + { + size_t amount = info->palettesize; + /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/ + for(i = info->palettesize; i != 0; --i) + { + if(info->palette[4 * (i - 1) + 3] == 255) --amount; + else break; + } + /*add only alpha channel*/ + for(i = 0; i != amount; ++i) ucvector_push_back(&tRNS, info->palette[4 * i + 3]); + } + else if(info->colortype == LCT_GREY) + { + if(info->key_defined) + { + ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); + } + } + else if(info->colortype == LCT_RGB) + { + if(info->key_defined) + { + ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_g >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_g & 255)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_b >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_b & 255)); + } + } + + error = addChunk(out, "tRNS", tRNS.data, tRNS.size); + ucvector_cleanup(&tRNS); + + return error; +} + +static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize, + LodePNGCompressSettings* zlibsettings) +{ + ucvector zlibdata; + unsigned error = 0; + + /*compress with the Zlib compressor*/ + ucvector_init(&zlibdata); + error = zlib_compress(&zlibdata.data, &zlibdata.size, data, datasize, zlibsettings); + if(!error) error = addChunk(out, "IDAT", zlibdata.data, zlibdata.size); + ucvector_cleanup(&zlibdata); + + return error; +} + +static unsigned addChunk_IEND(ucvector* out) +{ + unsigned error = 0; + error = addChunk(out, "IEND", 0, 0); + return error; +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) +{ + unsigned error = 0; + size_t i; + ucvector text; + ucvector_init(&text); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)keyword[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&text, 0); /*0 termination char*/ + for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)textstring[i]); + error = addChunk(out, "tEXt", text.data, text.size); + ucvector_cleanup(&text); + + return error; +} + +static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring, + LodePNGCompressSettings* zlibsettings) +{ + unsigned error = 0; + ucvector data, compressed; + size_t i, textsize = strlen(textstring); + + ucvector_init(&data); + ucvector_init(&compressed); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&data, 0); /*0 termination char*/ + ucvector_push_back(&data, 0); /*compression method: 0*/ + + error = zlib_compress(&compressed.data, &compressed.size, + (unsigned char*)textstring, textsize, zlibsettings); + if(!error) + { + for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]); + error = addChunk(out, "zTXt", data.data, data.size); + } + + ucvector_cleanup(&compressed); + ucvector_cleanup(&data); + return error; +} + +static unsigned addChunk_iTXt(ucvector* out, unsigned compressed, const char* keyword, const char* langtag, + const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) +{ + unsigned error = 0; + ucvector data; + size_t i, textsize = strlen(textstring); + + ucvector_init(&data); + + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&data, 0); /*null termination char*/ + ucvector_push_back(&data, compressed ? 1 : 0); /*compression flag*/ + ucvector_push_back(&data, 0); /*compression method*/ + for(i = 0; langtag[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)langtag[i]); + ucvector_push_back(&data, 0); /*null termination char*/ + for(i = 0; transkey[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)transkey[i]); + ucvector_push_back(&data, 0); /*null termination char*/ + + if(compressed) + { + ucvector compressed_data; + ucvector_init(&compressed_data); + error = zlib_compress(&compressed_data.data, &compressed_data.size, + (unsigned char*)textstring, textsize, zlibsettings); + if(!error) + { + for(i = 0; i != compressed_data.size; ++i) ucvector_push_back(&data, compressed_data.data[i]); + } + ucvector_cleanup(&compressed_data); + } + else /*not compressed*/ + { + for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)textstring[i]); + } + + if(!error) error = addChunk(out, "iTXt", data.data, data.size); + ucvector_cleanup(&data); + return error; +} + +static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) +{ + unsigned error = 0; + ucvector bKGD; + ucvector_init(&bKGD); + if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) + { + ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); + } + else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) + { + ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_g >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_g & 255)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_b >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_b & 255)); + } + else if(info->color.colortype == LCT_PALETTE) + { + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); /*palette index*/ + } + + error = addChunk(out, "bKGD", bKGD.data, bKGD.size); + ucvector_cleanup(&bKGD); + + return error; +} + +static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) +{ + unsigned error = 0; + unsigned char* data = (unsigned char*)lodepng_malloc(7); + if(!data) return 83; /*alloc fail*/ + data[0] = (unsigned char)(time->year >> 8); + data[1] = (unsigned char)(time->year & 255); + data[2] = (unsigned char)time->month; + data[3] = (unsigned char)time->day; + data[4] = (unsigned char)time->hour; + data[5] = (unsigned char)time->minute; + data[6] = (unsigned char)time->second; + error = addChunk(out, "tIME", data, 7); + lodepng_free(data); + return error; +} + +static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) +{ + unsigned error = 0; + ucvector data; + ucvector_init(&data); + + lodepng_add32bitInt(&data, info->phys_x); + lodepng_add32bitInt(&data, info->phys_y); + ucvector_push_back(&data, info->phys_unit); + + error = addChunk(out, "pHYs", data.data, data.size); + ucvector_cleanup(&data); + + return error; +} + +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, + size_t length, size_t bytewidth, unsigned char filterType) +{ + size_t i; + switch(filterType) + { + case 0: /*None*/ + for(i = 0; i != length; ++i) out[i] = scanline[i]; + break; + case 1: /*Sub*/ + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth]; + break; + case 2: /*Up*/ + if(prevline) + { + for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i]; + } + else + { + for(i = 0; i != length; ++i) out[i] = scanline[i]; + } + break; + case 3: /*Average*/ + if(prevline) + { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1); + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1); + } + else + { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1); + } + break; + case 4: /*Paeth*/ + if(prevline) + { + /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/ + for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]); + for(i = bytewidth; i < length; ++i) + { + out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth])); + } + } + else + { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/ + for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]); + } + break; + default: return; /*unexisting filter type given*/ + } +} + +/* log2 approximation. A slight bit faster than std::log. */ +static float flog2(float f) +{ + float result = 0; + while(f > 32) { result += 4; f /= 16; } + while(f > 2) { ++result; f /= 2; } + return result + 1.442695f * (f * f * f / 3 - 3 * f * f / 2 + 3 * f - 1.83333f); +} + +static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* info, const LodePNGEncoderSettings* settings) +{ + /* + For PNG filter method 0 + out must be a buffer with as size: h + (w * h * bpp + 7) / 8, because there are + the scanlines with 1 extra byte per scanline + */ + + unsigned bpp = lodepng_get_bpp(info); + /*the width of a scanline in bytes, not including the filter type*/ + size_t linebytes = (w * bpp + 7) / 8; + /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ + size_t bytewidth = (bpp + 7) / 8; + const unsigned char* prevline = 0; + unsigned x, y; + unsigned error = 0; + LodePNGFilterStrategy strategy = settings->filter_strategy; + + /* + There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard: + * If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e. + use fixed filtering, with the filter None). + * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is + not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply + all five filters and select the filter that produces the smallest sum of absolute values per row. + This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true. + + If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed, + but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum + heuristic is used. + */ + if(settings->filter_palette_zero && + (info->colortype == LCT_PALETTE || info->bitdepth < 8)) strategy = LFS_ZERO; + + if(bpp == 0) return 31; /*error: invalid color type*/ + + if(strategy == LFS_ZERO) + { + for(y = 0; y != h; ++y) + { + size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + size_t inindex = linebytes * y; + out[outindex] = 0; /*filter type byte*/ + filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, 0); + prevline = &in[inindex]; + } + } + else if(strategy == LFS_MINSUM) + { + /*adaptive filtering*/ + size_t sum[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t smallest = 0; + unsigned char type, bestType = 0; + + for(type = 0; type != 5; ++type) + { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ + } + + if(!error) + { + for(y = 0; y != h; ++y) + { + /*try the 5 filter types*/ + for(type = 0; type != 5; ++type) + { + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + + /*calculate the sum of the result*/ + sum[type] = 0; + if(type == 0) + { + for(x = 0; x != linebytes; ++x) sum[type] += (unsigned char)(attempt[type][x]); + } + else + { + for(x = 0; x != linebytes; ++x) + { + /*For differences, each byte should be treated as signed, values above 127 are negative + (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there. + This means filtertype 0 is almost never chosen, but that is justified.*/ + unsigned char s = attempt[type][x]; + sum[type] += s < 128 ? s : (255U - s); + } + } + + /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || sum[type] < smallest) + { + bestType = type; + smallest = sum[type]; + } + } + + prevline = &in[y * linebytes]; + + /*now fill the out values*/ + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + } + + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } + else if(strategy == LFS_ENTROPY) + { + float sum[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + float smallest = 0; + unsigned type, bestType = 0; + unsigned count[256]; + + for(type = 0; type != 5; ++type) + { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ + } + + for(y = 0; y != h; ++y) + { + /*try the 5 filter types*/ + for(type = 0; type != 5; ++type) + { + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + for(x = 0; x != 256; ++x) count[x] = 0; + for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]]; + ++count[type]; /*the filter type itself is part of the scanline*/ + sum[type] = 0; + for(x = 0; x != 256; ++x) + { + float p = count[x] / (float)(linebytes + 1); + sum[type] += count[x] == 0 ? 0 : flog2(1 / p) * p; + } + /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || sum[type] < smallest) + { + bestType = type; + smallest = sum[type]; + } + } + + prevline = &in[y * linebytes]; + + /*now fill the out values*/ + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } + else if(strategy == LFS_PREDEFINED) + { + for(y = 0; y != h; ++y) + { + size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + size_t inindex = linebytes * y; + unsigned char type = settings->predefined_filters[y]; + out[outindex] = type; /*filter type byte*/ + filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); + prevline = &in[inindex]; + } + } + else if(strategy == LFS_BRUTE_FORCE) + { + /*brute force filter chooser. + deflate the scanline after every filter attempt to see which one deflates best. + This is very slow and gives only slightly smaller, sometimes even larger, result*/ + size_t size[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t smallest = 0; + unsigned type = 0, bestType = 0; + unsigned char* dummy; + LodePNGCompressSettings zlibsettings = settings->zlibsettings; + /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose, + to simulate the true case where the tree is the same for the whole image. Sometimes it gives + better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare + cases better compression. It does make this a bit less slow, so it's worth doing this.*/ + zlibsettings.btype = 1; + /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG + images only, so disable it*/ + zlibsettings.custom_zlib = 0; + zlibsettings.custom_deflate = 0; + for(type = 0; type != 5; ++type) + { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ + } + for(y = 0; y != h; ++y) /*try the 5 filter types*/ + { + for(type = 0; type != 5; ++type) + { + size_t testsize = linebytes; + /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/ + + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + size[type] = 0; + dummy = 0; + zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings); + lodepng_free(dummy); + /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || size[type] < smallest) + { + bestType = type; + smallest = size[type]; + } + } + prevline = &in[y * linebytes]; + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + for(type = 0; type != 5; ++type) free(attempt[type]); + } + else return 88; /* unknown filter strategy */ + + return error; +} + +static void addPaddingBits(unsigned char* out, const unsigned char* in, + size_t olinebits, size_t ilinebits, unsigned h) +{ + /*The opposite of the removePaddingBits function + olinebits must be >= ilinebits*/ + unsigned y; + size_t diff = olinebits - ilinebits; + size_t obp = 0, ibp = 0; /*bit pointers*/ + for(y = 0; y != h; ++y) + { + size_t x; + for(x = 0; x < ilinebits; ++x) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + /*obp += diff; --> no, fill in some value in the padding bits too, to avoid + "Use of uninitialised value of size ###" warning from valgrind*/ + for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0); + } +} + +/* +in: non-interlaced image with size w*h +out: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with + no padding bits between scanlines, but between reduced images so that each + reduced image starts at a byte. +bpp: bits per pixel +there are no padding bits, not between scanlines, not between reduced images +in has the following size in bits: w * h * bpp. +out is possibly bigger due to padding bits between reduced images +NOTE: comments about padding bits are only relevant if bpp < 8 +*/ +static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) +{ + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + if(bpp >= 8) + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + size_t bytewidth = bpp / 8; + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; + size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth; + for(b = 0; b < bytewidth; ++b) + { + out[pixeloutstart + b] = in[pixelinstart + b]; + } + } + } + } + else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + unsigned ilinebits = bpp * passw[i]; + unsigned olinebits = bpp * w; + size_t obp, ibp; /*bit pointers (for out and in buffer)*/ + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; + obp = (8 * passstart[i]) + (y * ilinebits + x * bpp); + for(b = 0; b < bpp; ++b) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + } + } + } +} + +/*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image. +return value is error**/ +static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in, + unsigned w, unsigned h, + const LodePNGInfo* info_png, const LodePNGEncoderSettings* settings) +{ + /* + This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps: + *) if no Adam7: 1) add padding bits (= posible extra bits per scanline if bpp < 8) 2) filter + *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter + */ + unsigned bpp = lodepng_get_bpp(&info_png->color); + unsigned error = 0; + + if(info_png->interlace_method == 0) + { + *outsize = h + (h * ((w * bpp + 7) / 8)); /*image size plus an extra byte per scanline + possible padding bits*/ + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!(*out) && (*outsize)) error = 83; /*alloc fail*/ + + if(!error) + { + /*non multiple of 8 bits per scanline, padding bits needed per scanline*/ + if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) + { + unsigned char* padded = (unsigned char*)lodepng_malloc(h * ((w * bpp + 7) / 8)); + if(!padded) error = 83; /*alloc fail*/ + if(!error) + { + addPaddingBits(padded, in, ((w * bpp + 7) / 8) * 8, w * bpp, h); + error = filter(*out, padded, w, h, &info_png->color, settings); + } + lodepng_free(padded); + } + else + { + /*we can immediately filter into the out buffer, no other steps needed*/ + error = filter(*out, in, w, h, &info_png->color, settings); + } + } + } + else /*interlace_method is 1 (Adam7)*/ + { + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned char* adam7; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/ + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!(*out)) error = 83; /*alloc fail*/ + + adam7 = (unsigned char*)lodepng_malloc(passstart[7]); + if(!adam7 && passstart[7]) error = 83; /*alloc fail*/ + + if(!error) + { + unsigned i; + + Adam7_interlace(adam7, in, w, h, bpp); + for(i = 0; i != 7; ++i) + { + if(bpp < 8) + { + unsigned char* padded = (unsigned char*)lodepng_malloc(padded_passstart[i + 1] - padded_passstart[i]); + if(!padded) ERROR_BREAK(83); /*alloc fail*/ + addPaddingBits(padded, &adam7[passstart[i]], + ((passw[i] * bpp + 7) / 8) * 8, passw[i] * bpp, passh[i]); + error = filter(&(*out)[filter_passstart[i]], padded, + passw[i], passh[i], &info_png->color, settings); + lodepng_free(padded); + } + else + { + error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]], + passw[i], passh[i], &info_png->color, settings); + } + + if(error) break; + } + } + + lodepng_free(adam7); + } + + return error; +} + +/* +palette must have 4 * palettesize bytes allocated, and given in format RGBARGBARGBARGBA... +returns 0 if the palette is opaque, +returns 1 if the palette has a single color with alpha 0 ==> color key +returns 2 if the palette is semi-translucent. +*/ +static unsigned getPaletteTranslucency(const unsigned char* palette, size_t palettesize) +{ + size_t i; + unsigned key = 0; + unsigned r = 0, g = 0, b = 0; /*the value of the color with alpha 0, so long as color keying is possible*/ + for(i = 0; i != palettesize; ++i) + { + if(!key && palette[4 * i + 3] == 0) + { + r = palette[4 * i + 0]; g = palette[4 * i + 1]; b = palette[4 * i + 2]; + key = 1; + i = (size_t)(-1); /*restart from beginning, to detect earlier opaque colors with key's value*/ + } + else if(palette[4 * i + 3] != 255) return 2; + /*when key, no opaque RGB may have key's RGB*/ + else if(key && r == palette[i * 4 + 0] && g == palette[i * 4 + 1] && b == palette[i * 4 + 2]) return 2; + } + return key; +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) +{ + unsigned char* inchunk = data; + while((size_t)(inchunk - data) < datasize) + { + CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk)); + out->allocsize = out->size; /*fix the allocsize again*/ + inchunk = lodepng_chunk_next(inchunk); + } + return 0; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +unsigned lodepng_encode(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGState* state) +{ + LodePNGInfo info; + ucvector outv; + unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/ + size_t datasize = 0; + + /*provide some proper output values if error will happen*/ + *out = 0; + *outsize = 0; + state->error = 0; + + lodepng_info_init(&info); + lodepng_info_copy(&info, &state->info_png); + + if((info.color.colortype == LCT_PALETTE || state->encoder.force_palette) + && (info.color.palettesize == 0 || info.color.palettesize > 256)) + { + state->error = 68; /*invalid palette size, it is only allowed to be 1-256*/ + return state->error; + } + + if(state->encoder.auto_convert) + { + state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw); + } + if(state->error) return state->error; + + if(state->encoder.zlibsettings.btype > 2) + { + CERROR_RETURN_ERROR(state->error, 61); /*error: unexisting btype*/ + } + if(state->info_png.interlace_method > 1) + { + CERROR_RETURN_ERROR(state->error, 71); /*error: unexisting interlace mode*/ + } + + state->error = checkColorValidity(info.color.colortype, info.color.bitdepth); + if(state->error) return state->error; /*error: unexisting color type given*/ + state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth); + if(state->error) return state->error; /*error: unexisting color type given*/ + + if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) + { + unsigned char* converted; + size_t size = (w * h * lodepng_get_bpp(&info.color) + 7) / 8; + + converted = (unsigned char*)lodepng_malloc(size); + if(!converted && size) state->error = 83; /*alloc fail*/ + if(!state->error) + { + state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); + } + if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); + lodepng_free(converted); + } + else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); + + ucvector_init(&outv); + while(!state->error) /*while only executed once, to break on error*/ + { +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + size_t i; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*write signature and chunks*/ + writeSignature(&outv); + /*IHDR*/ + addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*unknown chunks between IHDR and PLTE*/ + if(info.unknown_chunks_data[0]) + { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*PLTE*/ + if(info.color.colortype == LCT_PALETTE) + { + addChunk_PLTE(&outv, &info.color); + } + if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) + { + addChunk_PLTE(&outv, &info.color); + } + /*tRNS*/ + if(info.color.colortype == LCT_PALETTE && getPaletteTranslucency(info.color.palette, info.color.palettesize) != 0) + { + addChunk_tRNS(&outv, &info.color); + } + if((info.color.colortype == LCT_GREY || info.color.colortype == LCT_RGB) && info.color.key_defined) + { + addChunk_tRNS(&outv, &info.color); + } +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*bKGD (must come between PLTE and the IDAt chunks*/ + if(info.background_defined) addChunk_bKGD(&outv, &info); + /*pHYs (must come before the IDAT chunks)*/ + if(info.phys_defined) addChunk_pHYs(&outv, &info); + + /*unknown chunks between PLTE and IDAT*/ + if(info.unknown_chunks_data[1]) + { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*IDAT (multiple IDAT chunks must be consecutive)*/ + state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings); + if(state->error) break; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*tIME*/ + if(info.time_defined) addChunk_tIME(&outv, &info.time); + /*tEXt and/or zTXt*/ + for(i = 0; i != info.text_num; ++i) + { + if(strlen(info.text_keys[i]) > 79) + { + state->error = 66; /*text chunk too large*/ + break; + } + if(strlen(info.text_keys[i]) < 1) + { + state->error = 67; /*text chunk too small*/ + break; + } + if(state->encoder.text_compression) + { + addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings); + } + else + { + addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]); + } + } + /*LodePNG version id in text chunk*/ + if(state->encoder.add_id) + { + unsigned alread_added_id_text = 0; + for(i = 0; i != info.text_num; ++i) + { + if(!strcmp(info.text_keys[i], "LodePNG")) + { + alread_added_id_text = 1; + break; + } + } + if(alread_added_id_text == 0) + { + addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ + } + } + /*iTXt*/ + for(i = 0; i != info.itext_num; ++i) + { + if(strlen(info.itext_keys[i]) > 79) + { + state->error = 66; /*text chunk too large*/ + break; + } + if(strlen(info.itext_keys[i]) < 1) + { + state->error = 67; /*text chunk too small*/ + break; + } + addChunk_iTXt(&outv, state->encoder.text_compression, + info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i], + &state->encoder.zlibsettings); + } + + /*unknown chunks between IDAT and IEND*/ + if(info.unknown_chunks_data[2]) + { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + addChunk_IEND(&outv); + + break; /*this isn't really a while loop; no error happened so break out now!*/ + } + + lodepng_info_cleanup(&info); + lodepng_free(data); + /*instead of cleaning the vector up, give it to the output*/ + *out = outv.data; + *outsize = outv.size; + + return state->error; +} + +unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image, + unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned error; + LodePNGState state; + lodepng_state_init(&state); + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + state.info_png.color.colortype = colortype; + state.info_png.color.bitdepth = bitdepth; + lodepng_encode(out, outsize, image, w, h, &state); + error = state.error; + lodepng_state_cleanup(&state); + return error; +} + +unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGBA, 8); +} + +unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGB, 8); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode_memory(&buffer, &buffersize, image, w, h, colortype, bitdepth); + if(!error) error = lodepng_save_file(buffer, buffersize, filename); + lodepng_free(buffer); + return error; +} + +unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_file(filename, image, w, h, LCT_RGBA, 8); +} + +unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_file(filename, image, w, h, LCT_RGB, 8); +} +#endif /*LODEPNG_COMPILE_DISK*/ + +void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings) +{ + lodepng_compress_settings_init(&settings->zlibsettings); + settings->filter_palette_zero = 1; + settings->filter_strategy = LFS_MINSUM; + settings->auto_convert = 1; + settings->force_palette = 0; + settings->predefined_filters = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + settings->add_id = 0; + settings->text_compression = 1; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ERROR_TEXT +/* +This returns the description of a numerical error code in English. This is also +the documentation of all the error codes. +*/ +const char* lodepng_error_text(unsigned code) +{ + switch(code) + { + case 0: return "no error, everything went ok"; + case 1: return "nothing done yet"; /*the Encoder/Decoder has done nothing yet, error checking makes no sense yet*/ + case 10: return "end of input memory reached without huffman end code"; /*while huffman decoding*/ + case 11: return "error in code tree made it jump outside of huffman tree"; /*while huffman decoding*/ + case 13: return "problem while processing dynamic deflate block"; + case 14: return "problem while processing dynamic deflate block"; + case 15: return "problem while processing dynamic deflate block"; + case 16: return "unexisting code while processing dynamic deflate block"; + case 17: return "end of out buffer memory reached while inflating"; + case 18: return "invalid distance code while inflating"; + case 19: return "end of out buffer memory reached while inflating"; + case 20: return "invalid deflate block BTYPE encountered while decoding"; + case 21: return "NLEN is not ones complement of LEN in a deflate block"; + /*end of out buffer memory reached while inflating: + This can happen if the inflated deflate data is longer than the amount of bytes required to fill up + all the pixels of the image, given the color depth and image dimensions. Something that doesn't + happen in a normal, well encoded, PNG image.*/ + case 22: return "end of out buffer memory reached while inflating"; + case 23: return "end of in buffer memory reached while inflating"; + case 24: return "invalid FCHECK in zlib header"; + case 25: return "invalid compression method in zlib header"; + case 26: return "FDICT encountered in zlib header while it's not used for PNG"; + case 27: return "PNG file is smaller than a PNG header"; + /*Checks the magic file header, the first 8 bytes of the PNG file*/ + case 28: return "incorrect PNG signature, it's no PNG or corrupted"; + case 29: return "first chunk is not the header chunk"; + case 30: return "chunk length too large, chunk broken off at end of file"; + case 31: return "illegal PNG color type or bpp"; + case 32: return "illegal PNG compression method"; + case 33: return "illegal PNG filter method"; + case 34: return "illegal PNG interlace method"; + case 35: return "chunk length of a chunk is too large or the chunk too small"; + case 36: return "illegal PNG filter type encountered"; + case 37: return "illegal bit depth for this color type given"; + case 38: return "the palette is too big"; /*more than 256 colors*/ + case 39: return "more palette alpha values given in tRNS chunk than there are colors in the palette"; + case 40: return "tRNS chunk has wrong size for greyscale image"; + case 41: return "tRNS chunk has wrong size for RGB image"; + case 42: return "tRNS chunk appeared while it was not allowed for this color type"; + case 43: return "bKGD chunk has wrong size for palette image"; + case 44: return "bKGD chunk has wrong size for greyscale image"; + case 45: return "bKGD chunk has wrong size for RGB image"; + case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?"; + case 49: return "jumped past memory while generating dynamic huffman tree"; + case 50: return "jumped past memory while generating dynamic huffman tree"; + case 51: return "jumped past memory while inflating huffman block"; + case 52: return "jumped past memory while inflating"; + case 53: return "size of zlib data too small"; + case 54: return "repeat symbol in tree while there was no value symbol yet"; + /*jumped past tree while generating huffman tree, this could be when the + tree will have more leaves than symbols after generating it out of the + given lenghts. They call this an oversubscribed dynamic bit lengths tree in zlib.*/ + case 55: return "jumped past tree while generating huffman tree"; + case 56: return "given output image colortype or bitdepth not supported for color conversion"; + case 57: return "invalid CRC encountered (checking CRC can be disabled)"; + case 58: return "invalid ADLER32 encountered (checking ADLER32 can be disabled)"; + case 59: return "requested color conversion not supported"; + case 60: return "invalid window size given in the settings of the encoder (must be 0-32768)"; + case 61: return "invalid BTYPE given in the settings of the encoder (only 0, 1 and 2 are allowed)"; + /*LodePNG leaves the choice of RGB to greyscale conversion formula to the user.*/ + case 62: return "conversion from color to greyscale not supported"; + case 63: return "length of a chunk too long, max allowed for PNG is 2147483647 bytes per chunk"; /*(2^31-1)*/ + /*this would result in the inability of a deflated block to ever contain an end code. It must be at least 1.*/ + case 64: return "the length of the END symbol 256 in the Huffman tree is 0"; + case 66: return "the length of a text chunk keyword given to the encoder is longer than the maximum of 79 bytes"; + case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte"; + case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors"; + case 69: return "unknown chunk type with 'critical' flag encountered by the decoder"; + case 71: return "unexisting interlace mode given to encoder (must be 0 or 1)"; + case 72: return "while decoding, unexisting compression method encountering in zTXt or iTXt chunk (it must be 0)"; + case 73: return "invalid tIME chunk size"; + case 74: return "invalid pHYs chunk size"; + /*length could be wrong, or data chopped off*/ + case 75: return "no null termination char found while decoding text chunk"; + case 76: return "iTXt chunk too short to contain required bytes"; + case 77: return "integer overflow in buffer size"; + case 78: return "failed to open file for reading"; /*file doesn't exist or couldn't be opened for reading*/ + case 79: return "failed to open file for writing"; + case 80: return "tried creating a tree of 0 symbols"; + case 81: return "lazy matching at pos 0 is impossible"; + case 82: return "color conversion to palette requested while a color isn't in palette"; + case 83: return "memory allocation failed"; + case 84: return "given image too small to contain all pixels to be encoded"; + case 86: return "impossible offset in lz77 encoding (internal bug)"; + case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined"; + case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy"; + case 89: return "text chunk keyword too short or long: must have size 1-79"; + /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ + case 90: return "windowsize must be a power of two"; + case 91: return "invalid decompressed idat size"; + case 92: return "too many pixels, not supported"; + case 93: return "zero width or height is invalid"; + case 94: return "header chunk must have a size of 13 bytes"; + } + return "unknown error code"; +} +#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // C++ Wrapper // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_CPP +namespace lodepng +{ + +#ifdef LODEPNG_COMPILE_DISK +unsigned load_file(std::vector& buffer, const std::string& filename) +{ + std::ifstream file(filename.c_str(), std::ios::in|std::ios::binary|std::ios::ate); + if(!file) return 78; + + /*get filesize*/ + std::streamsize size = 0; + if(file.seekg(0, std::ios::end).good()) size = file.tellg(); + if(file.seekg(0, std::ios::beg).good()) size -= file.tellg(); + + /*read contents of the file into the vector*/ + buffer.resize(size_t(size)); + if(size > 0) file.read((char*)(&buffer[0]), size); + + return 0; /* OK */ +} + +/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ +unsigned save_file(const std::vector& buffer, const std::string& filename) +{ + std::ofstream file(filename.c_str(), std::ios::out|std::ios::binary); + if(!file) return 79; + file.write(buffer.empty() ? 0 : (char*)&buffer[0], std::streamsize(buffer.size())); + return 0; +} +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_DECODER +unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings& settings) +{ + unsigned char* buffer = 0; + size_t buffersize = 0; + unsigned error = zlib_decompress(&buffer, &buffersize, in, insize, &settings); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned decompress(std::vector& out, const std::vector& in, + const LodePNGDecompressSettings& settings) +{ + return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings); +} +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +unsigned compress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings& settings) +{ + unsigned char* buffer = 0; + size_t buffersize = 0; + unsigned error = zlib_compress(&buffer, &buffersize, in, insize, &settings); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned compress(std::vector& out, const std::vector& in, + const LodePNGCompressSettings& settings) +{ + return compress(out, in.empty() ? 0 : &in[0], in.size(), settings); +} +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ + + +#ifdef LODEPNG_COMPILE_PNG + +State::State() +{ + lodepng_state_init(this); +} + +State::State(const State& other) +{ + lodepng_state_init(this); + lodepng_state_copy(this, &other); +} + +State::~State() +{ + lodepng_state_cleanup(this); +} + +State& State::operator=(const State& other) +{ + lodepng_state_copy(this, &other); + return *this; +} + +#ifdef LODEPNG_COMPILE_DECODER + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, const unsigned char* in, + size_t insize, LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer; + unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth); + if(buffer && !error) + { + State state; + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::vector& in, LodePNGColorType colortype, unsigned bitdepth) +{ + return decode(out, w, h, in.empty() ? 0 : &in[0], (unsigned)in.size(), colortype, bitdepth); +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const unsigned char* in, size_t insize) +{ + unsigned char* buffer = NULL; + unsigned error = lodepng_decode(&buffer, &w, &h, &state, in, insize); + if(buffer && !error) + { + size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + } + lodepng_free(buffer); + return error; +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const std::vector& in) +{ + return decode(out, w, h, state, in.empty() ? 0 : &in[0], in.size()); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::string& filename, + LodePNGColorType colortype, unsigned bitdepth) +{ + std::vector buffer; + unsigned error = load_file(buffer, filename); + if(error) return error; + return decode(out, w, h, buffer, colortype, bitdepth); +} +#endif /* LODEPNG_COMPILE_DECODER */ +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ENCODER +unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode_memory(&buffer, &buffersize, in, w, h, colortype, bitdepth); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; + return encode(out, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); +} + +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + State& state) +{ + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode(&buffer, &buffersize, in, w, h, &state); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + State& state) +{ + if(lodepng_get_raw_size(w, h, &state.info_raw) > in.size()) return 84; + return encode(out, in.empty() ? 0 : &in[0], w, h, state); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned encode(const std::string& filename, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + std::vector buffer; + unsigned error = encode(buffer, in, w, h, colortype, bitdepth); + if(!error) error = save_file(buffer, filename); + return error; +} + +unsigned encode(const std::string& filename, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; + return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); +} +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_PNG */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ diff --git a/deps/etc2comp/third_party/lodepng/lodepng.h b/deps/etc2comp/third_party/lodepng/lodepng.h new file mode 100644 index 0000000000..9ecedd1e2f --- /dev/null +++ b/deps/etc2comp/third_party/lodepng/lodepng.h @@ -0,0 +1,1756 @@ +/* +LodePNG version 20160124 + +Copyright (c) 2005-2016 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#ifndef LODEPNG_H +#define LODEPNG_H + +#include /*for size_t*/ + +extern const char* LODEPNG_VERSION_STRING; + +/* +The following #defines are used to create code sections. They can be disabled +to disable code sections, which can give faster compile time and smaller binary. +The "NO_COMPILE" defines are designed to be used to pass as defines to the +compiler command to disable them without modifying this header, e.g. +-DLODEPNG_NO_COMPILE_ZLIB for gcc. +In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to +allow implementing a custom lodepng_crc32. +*/ +/*deflate & zlib. If disabled, you must specify alternative zlib functions in +the custom_zlib field of the compress and decompress settings*/ +#ifndef LODEPNG_NO_COMPILE_ZLIB +#define LODEPNG_COMPILE_ZLIB +#endif +/*png encoder and png decoder*/ +#ifndef LODEPNG_NO_COMPILE_PNG +#define LODEPNG_COMPILE_PNG +#endif +/*deflate&zlib decoder and png decoder*/ +#ifndef LODEPNG_NO_COMPILE_DECODER +#define LODEPNG_COMPILE_DECODER +#endif +/*deflate&zlib encoder and png encoder*/ +#ifndef LODEPNG_NO_COMPILE_ENCODER +#define LODEPNG_COMPILE_ENCODER +#endif +/*the optional built in harddisk file loading and saving functions*/ +#ifndef LODEPNG_NO_COMPILE_DISK +#define LODEPNG_COMPILE_DISK +#endif +/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/ +#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS +#define LODEPNG_COMPILE_ANCILLARY_CHUNKS +#endif +/*ability to convert error numerical codes to English text string*/ +#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT +#define LODEPNG_COMPILE_ERROR_TEXT +#endif +/*Compile the default allocators (C's free, malloc and realloc). If you disable this, +you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your +source files with custom allocators.*/ +#ifndef LODEPNG_NO_COMPILE_ALLOCATORS +#define LODEPNG_COMPILE_ALLOCATORS +#endif +/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/ +#ifdef __cplusplus +#ifndef LODEPNG_NO_COMPILE_CPP +#define LODEPNG_COMPILE_CPP +#endif +#endif + +#ifdef LODEPNG_COMPILE_CPP +#include +#include +#endif /*LODEPNG_COMPILE_CPP*/ + +#ifdef LODEPNG_COMPILE_PNG +/*The PNG color types (also used for raw).*/ +typedef enum LodePNGColorType +{ + LCT_GREY = 0, /*greyscale: 1,2,4,8,16 bit*/ + LCT_RGB = 2, /*RGB: 8,16 bit*/ + LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/ + LCT_GREY_ALPHA = 4, /*greyscale with alpha: 8,16 bit*/ + LCT_RGBA = 6 /*RGB with alpha: 8,16 bit*/ +} LodePNGColorType; + +#ifdef LODEPNG_COMPILE_DECODER +/* +Converts PNG data in memory to raw pixel data. +out: Output parameter. Pointer to buffer that will contain the raw pixel data. + After decoding, its size is w * h * (bytes per pixel) bytes larger than + initially. Bytes per pixel depends on colortype and bitdepth. + Must be freed after usage with free(*out). + Note: for 16-bit per channel colors, uses big endian format like PNG does. +w: Output parameter. Pointer to width of pixel data. +h: Output parameter. Pointer to height of pixel data. +in: Memory buffer with the PNG file. +insize: size of the in buffer. +colortype: the desired color type for the raw output image. See explanation on PNG color types. +bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types. +Return value: LodePNG error code (0 means no error). +*/ +unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/ +unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize); + +/*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/ +unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize); + +#ifdef LODEPNG_COMPILE_DISK +/* +Load PNG from disk, from file with given name. +Same as the other decode functions, but instead takes a filename as input. +*/ +unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/ +unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename); + +/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/ +unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename); +#endif /*LODEPNG_COMPILE_DISK*/ +#endif /*LODEPNG_COMPILE_DECODER*/ + + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Converts raw pixel data into a PNG image in memory. The colortype and bitdepth + of the output PNG image cannot be chosen, they are automatically determined + by the colortype, bitdepth and content of the input pixel data. + Note: for 16-bit per channel colors, needs big endian format like PNG does. +out: Output parameter. Pointer to buffer that will contain the PNG image data. + Must be freed after usage with free(*out). +outsize: Output parameter. Pointer to the size in bytes of the out buffer. +image: The raw pixel data to encode. The size of this buffer should be + w * h * (bytes per pixel), bytes per pixel depends on colortype and bitdepth. +w: width of the raw pixel data in pixels. +h: height of the raw pixel data in pixels. +colortype: the color type of the raw input image. See explanation on PNG color types. +bitdepth: the bit depth of the raw input image. See explanation on PNG color types. +Return value: LodePNG error code (0 means no error). +*/ +unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/ +unsigned lodepng_encode32(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h); + +/*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/ +unsigned lodepng_encode24(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h); + +#ifdef LODEPNG_COMPILE_DISK +/* +Converts raw pixel data into a PNG file on disk. +Same as the other encode functions, but instead takes a filename as output. +NOTE: This overwrites existing files without warning! +*/ +unsigned lodepng_encode_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/ +unsigned lodepng_encode32_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h); + +/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/ +unsigned lodepng_encode24_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h); +#endif /*LODEPNG_COMPILE_DISK*/ +#endif /*LODEPNG_COMPILE_ENCODER*/ + + +#ifdef LODEPNG_COMPILE_CPP +namespace lodepng +{ +#ifdef LODEPNG_COMPILE_DECODER +/*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype +is the format to output the pixels to. Default is RGBA 8-bit per channel.*/ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const unsigned char* in, size_t insize, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::vector& in, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#ifdef LODEPNG_COMPILE_DISK +/* +Converts PNG file from disk to raw pixel data in memory. +Same as the other decode functions, but instead takes a filename as input. +*/ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::string& filename, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +/*Same as lodepng_encode_memory, but encodes to an std::vector. colortype +is that of the raw input data. The output PNG color type will be auto chosen.*/ +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#ifdef LODEPNG_COMPILE_DISK +/* +Converts 32-bit RGBA raw pixel data into a PNG file on disk. +Same as the other encode functions, but instead takes a filename as output. +NOTE: This overwrites existing files without warning! +*/ +unsigned encode(const std::string& filename, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned encode(const std::string& filename, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ERROR_TEXT +/*Returns an English description of the numerical error code.*/ +const char* lodepng_error_text(unsigned code); +#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ + +#ifdef LODEPNG_COMPILE_DECODER +/*Settings for zlib decompression*/ +typedef struct LodePNGDecompressSettings LodePNGDecompressSettings; +struct LodePNGDecompressSettings +{ + unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ + + /*use custom zlib decoder instead of built in one (default: null)*/ + unsigned (*custom_zlib)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGDecompressSettings*); + /*use custom deflate decoder instead of built in one (default: null) + if custom_zlib is used, custom_deflate is ignored since only the built in + zlib function will call custom_deflate*/ + unsigned (*custom_inflate)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGDecompressSettings*); + + const void* custom_context; /*optional custom settings for custom functions*/ +}; + +extern const LodePNGDecompressSettings lodepng_default_decompress_settings; +void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Settings for zlib compression. Tweaking these settings tweaks the balance +between speed and compression ratio. +*/ +typedef struct LodePNGCompressSettings LodePNGCompressSettings; +struct LodePNGCompressSettings /*deflate = compress*/ +{ + /*LZ77 related settings*/ + unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/ + unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/ + unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/ + unsigned minmatch; /*mininum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/ + unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/ + unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/ + + /*use custom zlib encoder instead of built in one (default: null)*/ + unsigned (*custom_zlib)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGCompressSettings*); + /*use custom deflate encoder instead of built in one (default: null) + if custom_zlib is used, custom_deflate is ignored since only the built in + zlib function will call custom_deflate*/ + unsigned (*custom_deflate)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGCompressSettings*); + + const void* custom_context; /*optional custom settings for custom functions*/ +}; + +extern const LodePNGCompressSettings lodepng_default_compress_settings; +void lodepng_compress_settings_init(LodePNGCompressSettings* settings); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_PNG +/* +Color mode of an image. Contains all information required to decode the pixel +bits to RGBA colors. This information is the same as used in the PNG file +format, and is used both for PNG and raw image data in LodePNG. +*/ +typedef struct LodePNGColorMode +{ + /*header (IHDR)*/ + LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/ + unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/ + + /* + palette (PLTE and tRNS) + + Dynamically allocated with the colors of the palette, including alpha. + When encoding a PNG, to store your colors in the palette of the LodePNGColorMode, first use + lodepng_palette_clear, then for each color use lodepng_palette_add. + If you encode an image without alpha with palette, don't forget to put value 255 in each A byte of the palette. + + When decoding, by default you can ignore this palette, since LodePNG already + fills the palette colors in the pixels of the raw RGBA output. + + The palette is only supported for color type 3. + */ + unsigned char* palette; /*palette in RGBARGBA... order. When allocated, must be either 0, or have size 1024*/ + size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/ + + /* + transparent color key (tRNS) + + This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit. + For greyscale PNGs, r, g and b will all 3 be set to the same. + + When decoding, by default you can ignore this information, since LodePNG sets + pixels with this key to transparent already in the raw RGBA output. + + The color key is only supported for color types 0 and 2. + */ + unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/ + unsigned key_r; /*red/greyscale component of color key*/ + unsigned key_g; /*green component of color key*/ + unsigned key_b; /*blue component of color key*/ +} LodePNGColorMode; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_color_mode_init(LodePNGColorMode* info); +void lodepng_color_mode_cleanup(LodePNGColorMode* info); +/*return value is error code (0 means no error)*/ +unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source); + +void lodepng_palette_clear(LodePNGColorMode* info); +/*add 1 color to the palette*/ +unsigned lodepng_palette_add(LodePNGColorMode* info, + unsigned char r, unsigned char g, unsigned char b, unsigned char a); + +/*get the total amount of bits per pixel, based on colortype and bitdepth in the struct*/ +unsigned lodepng_get_bpp(const LodePNGColorMode* info); +/*get the amount of color channels used, based on colortype in the struct. +If a palette is used, it counts as 1 channel.*/ +unsigned lodepng_get_channels(const LodePNGColorMode* info); +/*is it a greyscale type? (only colortype 0 or 4)*/ +unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info); +/*has it got an alpha channel? (only colortype 2 or 6)*/ +unsigned lodepng_is_alpha_type(const LodePNGColorMode* info); +/*has it got a palette? (only colortype 3)*/ +unsigned lodepng_is_palette_type(const LodePNGColorMode* info); +/*only returns true if there is a palette and there is a value in the palette with alpha < 255. +Loops through the palette to check this.*/ +unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info); +/* +Check if the given color info indicates the possibility of having non-opaque pixels in the PNG image. +Returns true if the image can have translucent or invisible pixels (it still be opaque if it doesn't use such pixels). +Returns false if the image can only have opaque pixels. +In detail, it returns true only if it's a color type with alpha, or has a palette with non-opaque values, +or if "key_defined" is true. +*/ +unsigned lodepng_can_have_alpha(const LodePNGColorMode* info); +/*Returns the byte size of a raw image buffer with given width, height and color mode*/ +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*The information of a Time chunk in PNG.*/ +typedef struct LodePNGTime +{ + unsigned year; /*2 bytes used (0-65535)*/ + unsigned month; /*1-12*/ + unsigned day; /*1-31*/ + unsigned hour; /*0-23*/ + unsigned minute; /*0-59*/ + unsigned second; /*0-60 (to allow for leap seconds)*/ +} LodePNGTime; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/*Information about the PNG image, except pixels, width and height.*/ +typedef struct LodePNGInfo +{ + /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/ + unsigned compression_method;/*compression method of the original file. Always 0.*/ + unsigned filter_method; /*filter method of the original file*/ + unsigned interlace_method; /*interlace method of the original file*/ + LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /* + suggested background color chunk (bKGD) + This color uses the same color mode as the PNG (except alpha channel), which can be 1-bit to 16-bit. + + For greyscale PNGs, r, g and b will all 3 be set to the same. When encoding + the encoder writes the red one. For palette PNGs: When decoding, the RGB value + will be stored, not a palette index. But when encoding, specify the index of + the palette in background_r, the other two are then ignored. + + The decoder does not use this background color to edit the color of pixels. + */ + unsigned background_defined; /*is a suggested background color given?*/ + unsigned background_r; /*red component of suggested background color*/ + unsigned background_g; /*green component of suggested background color*/ + unsigned background_b; /*blue component of suggested background color*/ + + /* + non-international text chunks (tEXt and zTXt) + + The char** arrays each contain num strings. The actual messages are in + text_strings, while text_keys are keywords that give a short description what + the actual text represents, e.g. Title, Author, Description, or anything else. + + A keyword is minimum 1 character and maximum 79 characters long. It's + discouraged to use a single line length longer than 79 characters for texts. + + Don't allocate these text buffers yourself. Use the init/cleanup functions + correctly and use lodepng_add_text and lodepng_clear_text. + */ + size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/ + char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/ + char** text_strings; /*the actual text*/ + + /* + international text chunks (iTXt) + Similar to the non-international text chunks, but with additional strings + "langtags" and "transkeys". + */ + size_t itext_num; /*the amount of international texts in this PNG*/ + char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/ + char** itext_langtags; /*language tag for this text's language, ISO/IEC 646 string, e.g. ISO 639 language tag*/ + char** itext_transkeys; /*keyword translated to the international language - UTF-8 string*/ + char** itext_strings; /*the actual international text - UTF-8 string*/ + + /*time chunk (tIME)*/ + unsigned time_defined; /*set to 1 to make the encoder generate a tIME chunk*/ + LodePNGTime time; + + /*phys chunk (pHYs)*/ + unsigned phys_defined; /*if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one*/ + unsigned phys_x; /*pixels per unit in x direction*/ + unsigned phys_y; /*pixels per unit in y direction*/ + unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ + + /* + unknown chunks + There are 3 buffers, one for each position in the PNG where unknown chunks can appear + each buffer contains all unknown chunks for that position consecutively + The 3 buffers are the unknown chunks between certain critical chunks: + 0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND + Do not allocate or traverse this data yourself. Use the chunk traversing functions declared + later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct. + */ + unsigned char* unknown_chunks_data[3]; + size_t unknown_chunks_size[3]; /*size in bytes of the unknown chunks, given for protection*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGInfo; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_info_init(LodePNGInfo* info); +void lodepng_info_cleanup(LodePNGInfo* info); +/*return value is error code (0 means no error)*/ +unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ +unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/ + +void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/ +unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/* +Converts raw buffer from one color type to another color type, based on +LodePNGColorMode structs to describe the input and output color type. +See the reference manual at the end of this header file to see which color conversions are supported. +return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported) +The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel +of the output color type (lodepng_get_bpp). +For < 8 bpp images, there should not be padding bits at the end of scanlines. +For 16-bit per channel colors, uses big endian format like PNG does. +Return value is LodePNG error code +*/ +unsigned lodepng_convert(unsigned char* out, const unsigned char* in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h); + +#ifdef LODEPNG_COMPILE_DECODER +/* +Settings for the decoder. This contains settings for the PNG and the Zlib +decoder, but not the Info settings from the Info structs. +*/ +typedef struct LodePNGDecoderSettings +{ + LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ + + unsigned ignore_crc; /*ignore CRC checksums*/ + + unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/ + /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/ + unsigned remember_unknown_chunks; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGDecoderSettings; + +void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/ +typedef enum LodePNGFilterStrategy +{ + /*every filter at zero*/ + LFS_ZERO, + /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/ + LFS_MINSUM, + /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending + on the image, this is better or worse than minsum.*/ + LFS_ENTROPY, + /* + Brute-force-search PNG filters by compressing each filter for each scanline. + Experimental, very slow, and only rarely gives better compression than MINSUM. + */ + LFS_BRUTE_FORCE, + /*use predefined_filters buffer: you specify the filter type for each scanline*/ + LFS_PREDEFINED +} LodePNGFilterStrategy; + +/*Gives characteristics about the colors of the image, which helps decide which color model to use for encoding. +Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/ +typedef struct LodePNGColorProfile +{ + unsigned colored; /*not greyscale*/ + unsigned key; /*if true, image is not opaque. Only if true and alpha is false, color key is possible.*/ + unsigned short key_r; /*these values are always in 16-bit bitdepth in the profile*/ + unsigned short key_g; + unsigned short key_b; + unsigned alpha; /*alpha channel or alpha palette required*/ + unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16.*/ + unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order*/ + unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for greyscale only. 16 if 16-bit per channel required.*/ +} LodePNGColorProfile; + +void lodepng_color_profile_init(LodePNGColorProfile* profile); + +/*Get a LodePNGColorProfile of the image.*/ +unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in); +/*The function LodePNG uses internally to decide the PNG color with auto_convert. +Chooses an optimal color model, e.g. grey if only grey pixels, palette if < 256 colors, ...*/ +unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in); + +/*Settings for the encoder.*/ +typedef struct LodePNGEncoderSettings +{ + LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/ + + unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/ + + /*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than + 8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to + completely follow the official PNG heuristic, filter_palette_zero must be true and + filter_strategy must be LFS_MINSUM*/ + unsigned filter_palette_zero; + /*Which filter strategy to use when not using zeroes due to filter_palette_zero. + Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: LFS_MINSUM*/ + LodePNGFilterStrategy filter_strategy; + /*used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with + the same length as the amount of scanlines in the image, and each value must <= 5. You + have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero + must be set to 0 to ensure this is also used on palette or low bitdepth images.*/ + const unsigned char* predefined_filters; + + /*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette). + If colortype is 3, PLTE is _always_ created.*/ + unsigned force_palette; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*add LodePNG identifier and version as a text chunk, for debugging*/ + unsigned add_id; + /*encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks*/ + unsigned text_compression; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGEncoderSettings; + +void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings); +#endif /*LODEPNG_COMPILE_ENCODER*/ + + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) +/*The settings, state and information for extended encoding and decoding.*/ +typedef struct LodePNGState +{ +#ifdef LODEPNG_COMPILE_DECODER + LodePNGDecoderSettings decoder; /*the decoding settings*/ +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER + LodePNGEncoderSettings encoder; /*the encoding settings*/ +#endif /*LODEPNG_COMPILE_ENCODER*/ + LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/ + LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/ + unsigned error; +#ifdef LODEPNG_COMPILE_CPP + /* For the lodepng::State subclass. */ + virtual ~LodePNGState(){} +#endif +} LodePNGState; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_state_init(LodePNGState* state); +void lodepng_state_cleanup(LodePNGState* state); +void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source); +#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ + +#ifdef LODEPNG_COMPILE_DECODER +/* +Same as lodepng_decode_memory, but uses a LodePNGState to allow custom settings and +getting much more information about the PNG image and color mode. +*/ +unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize); + +/* +Read the PNG header, but not the actual data. This returns only the information +that is in the header chunk of the PNG, such as width, height and color type. The +information is placed in the info_png field of the LodePNGState. +*/ +unsigned lodepng_inspect(unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize); +#endif /*LODEPNG_COMPILE_DECODER*/ + + +#ifdef LODEPNG_COMPILE_ENCODER +/*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/ +unsigned lodepng_encode(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGState* state); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/* +The lodepng_chunk functions are normally not needed, except to traverse the +unknown chunks stored in the LodePNGInfo struct, or add new ones to it. +It also allows traversing the chunks of an encoded PNG file yourself. + +PNG standard chunk naming conventions: +First byte: uppercase = critical, lowercase = ancillary +Second byte: uppercase = public, lowercase = private +Third byte: must be uppercase +Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy +*/ + +/* +Gets the length of the data of the chunk. Total chunk length has 12 bytes more. +There must be at least 4 bytes to read from. If the result value is too large, +it may be corrupt data. +*/ +unsigned lodepng_chunk_length(const unsigned char* chunk); + +/*puts the 4-byte type in null terminated string*/ +void lodepng_chunk_type(char type[5], const unsigned char* chunk); + +/*check if the type is the given type*/ +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type); + +/*0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard)*/ +unsigned char lodepng_chunk_ancillary(const unsigned char* chunk); + +/*0: public, 1: private (see PNG standard)*/ +unsigned char lodepng_chunk_private(const unsigned char* chunk); + +/*0: the chunk is unsafe to copy, 1: the chunk is safe to copy (see PNG standard)*/ +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk); + +/*get pointer to the data of the chunk, where the input points to the header of the chunk*/ +unsigned char* lodepng_chunk_data(unsigned char* chunk); +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk); + +/*returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!)*/ +unsigned lodepng_chunk_check_crc(const unsigned char* chunk); + +/*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/ +void lodepng_chunk_generate_crc(unsigned char* chunk); + +/*iterate to next chunks. don't use on IEND chunk, as there is no next chunk then*/ +unsigned char* lodepng_chunk_next(unsigned char* chunk); +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk); + +/* +Appends chunk to the data in out. The given chunk should already have its chunk header. +The out variable and outlength are updated to reflect the new reallocated buffer. +Returns error code (0 if it went ok) +*/ +unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk); + +/* +Appends new chunk to out. The chunk to append is given by giving its length, type +and data separately. The type is a 4-letter string. +The out variable and outlength are updated to reflect the new reallocated buffer. +Returne error code (0 if it went ok) +*/ +unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, + const char* type, const unsigned char* data); + + +/*Calculate CRC32 of buffer*/ +unsigned lodepng_crc32(const unsigned char* buf, size_t len); +#endif /*LODEPNG_COMPILE_PNG*/ + + +#ifdef LODEPNG_COMPILE_ZLIB +/* +This zlib part can be used independently to zlib compress and decompress a +buffer. It cannot be used to create gzip files however, and it only supports the +part of zlib that is required for PNG, it does not support dictionaries. +*/ + +#ifdef LODEPNG_COMPILE_DECODER +/*Inflate a buffer. Inflate is the decompression step of deflate. Out buffer must be freed after use.*/ +unsigned lodepng_inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings); + +/* +Decompresses Zlib data. Reallocates the out buffer and appends the data. The +data must be according to the zlib specification. +Either, *out must be NULL and *outsize must be 0, or, *out must be a valid +buffer and *outsize its size in bytes. out must be freed by user after usage. +*/ +unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Compresses data with Zlib. Reallocates the out buffer and appends the data. +Zlib adds a small header and trailer around the deflate data. +The data is output in the format of the zlib specification. +Either, *out must be NULL and *outsize must be 0, or, *out must be a valid +buffer and *outsize its size in bytes. out must be freed by user after usage. +*/ +unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings); + +/* +Find length-limited Huffman code for given frequencies. This function is in the +public interface only for tests, it's used internally by lodepng_deflate. +*/ +unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, + size_t numcodes, unsigned maxbitlen); + +/*Compress a buffer with deflate. See RFC 1951. Out buffer must be freed after use.*/ +unsigned lodepng_deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings); + +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_ZLIB*/ + +#ifdef LODEPNG_COMPILE_DISK +/* +Load a file from disk into buffer. The function allocates the out buffer, and +after usage you should free it. +out: output parameter, contains pointer to loaded buffer. +outsize: output parameter, size of the allocated out buffer +filename: the path to the file to load +return value: error code (0 means ok) +*/ +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename); + +/* +Save a file from buffer to disk. Warning, if it exists, this function overwrites +the file without warning! +buffer: the buffer to write +buffersize: size of the buffer to write +filename: the path to the file to save to +return value: error code (0 means ok) +*/ +unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename); +#endif /*LODEPNG_COMPILE_DISK*/ + +#ifdef LODEPNG_COMPILE_CPP +/* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */ +namespace lodepng +{ +#ifdef LODEPNG_COMPILE_PNG +class State : public LodePNGState +{ + public: + State(); + State(const State& other); + virtual ~State(); + State& operator=(const State& other); +}; + +#ifdef LODEPNG_COMPILE_DECODER +/* Same as other lodepng::decode, but using a State for more settings and information. */ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const unsigned char* in, size_t insize); +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const std::vector& in); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* Same as other lodepng::encode, but using a State for more settings and information. */ +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + State& state); +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + State& state); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DISK +/* +Load a file from disk into an std::vector. +return value: error code (0 means ok) +*/ +unsigned load_file(std::vector& buffer, const std::string& filename); + +/* +Save the binary data in an std::vector to a file on disk. The file is overwritten +without warning. +*/ +unsigned save_file(const std::vector& buffer, const std::string& filename); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_PNG */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_DECODER +/* Zlib-decompress an unsigned char buffer */ +unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); + +/* Zlib-decompress an std::vector */ +unsigned decompress(std::vector& out, const std::vector& in, + const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +/* Zlib-compress an unsigned char buffer */ +unsigned compress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings& settings = lodepng_default_compress_settings); + +/* Zlib-compress an std::vector */ +unsigned compress(std::vector& out, const std::vector& in, + const LodePNGCompressSettings& settings = lodepng_default_compress_settings); +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ + +/* +TODO: +[.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often +[.] check compatibility with various compilers - done but needs to be redone for every newer version +[X] converting color to 16-bit per channel types +[ ] read all public PNG chunk types (but never let the color profile and gamma ones touch RGB values) +[ ] make sure encoder generates no chunks with size > (2^31)-1 +[ ] partial decoding (stream processing) +[X] let the "isFullyOpaque" function check color keys and transparent palettes too +[X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl" +[ ] don't stop decoding on errors like 69, 57, 58 (make warnings) +[ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes +[ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ... +[ ] allow user to give data (void*) to custom allocator +*/ + +#endif /*LODEPNG_H inclusion guard*/ + +/* +LodePNG Documentation +--------------------- + +0. table of contents +-------------------- + + 1. about + 1.1. supported features + 1.2. features not supported + 2. C and C++ version + 3. security + 4. decoding + 5. encoding + 6. color conversions + 6.1. PNG color types + 6.2. color conversions + 6.3. padding bits + 6.4. A note about 16-bits per channel and endianness + 7. error values + 8. chunks and PNG editing + 9. compiler support + 10. examples + 10.1. decoder C++ example + 10.2. decoder C example + 11. state settings reference + 12. changes + 13. contact information + + +1. about +-------- + +PNG is a file format to store raster images losslessly with good compression, +supporting different color types and alpha channel. + +LodePNG is a PNG codec according to the Portable Network Graphics (PNG) +Specification (Second Edition) - W3C Recommendation 10 November 2003. + +The specifications used are: + +*) Portable Network Graphics (PNG) Specification (Second Edition): + http://www.w3.org/TR/2003/REC-PNG-20031110 +*) RFC 1950 ZLIB Compressed Data Format version 3.3: + http://www.gzip.org/zlib/rfc-zlib.html +*) RFC 1951 DEFLATE Compressed Data Format Specification ver 1.3: + http://www.gzip.org/zlib/rfc-deflate.html + +The most recent version of LodePNG can currently be found at +http://lodev.org/lodepng/ + +LodePNG works both in C (ISO C90) and C++, with a C++ wrapper that adds +extra functionality. + +LodePNG exists out of two files: +-lodepng.h: the header file for both C and C++ +-lodepng.c(pp): give it the name lodepng.c or lodepng.cpp (or .cc) depending on your usage + +If you want to start using LodePNG right away without reading this doc, get the +examples from the LodePNG website to see how to use it in code, or check the +smaller examples in chapter 13 here. + +LodePNG is simple but only supports the basic requirements. To achieve +simplicity, the following design choices were made: There are no dependencies +on any external library. There are functions to decode and encode a PNG with +a single function call, and extended versions of these functions taking a +LodePNGState struct allowing to specify or get more information. By default +the colors of the raw image are always RGB or RGBA, no matter what color type +the PNG file uses. To read and write files, there are simple functions to +convert the files to/from buffers in memory. + +This all makes LodePNG suitable for loading textures in games, demos and small +programs, ... It's less suitable for full fledged image editors, loading PNGs +over network (it requires all the image data to be available before decoding can +begin), life-critical systems, ... + +1.1. supported features +----------------------- + +The following features are supported by the decoder: + +*) decoding of PNGs with any color type, bit depth and interlace mode, to a 24- or 32-bit color raw image, + or the same color type as the PNG +*) encoding of PNGs, from any raw image to 24- or 32-bit color, or the same color type as the raw image +*) Adam7 interlace and deinterlace for any color type +*) loading the image from harddisk or decoding it from a buffer from other sources than harddisk +*) support for alpha channels, including RGBA color model, translucent palettes and color keying +*) zlib decompression (inflate) +*) zlib compression (deflate) +*) CRC32 and ADLER32 checksums +*) handling of unknown chunks, allowing making a PNG editor that stores custom and unknown chunks. +*) the following chunks are supported (generated/interpreted) by both encoder and decoder: + IHDR: header information + PLTE: color palette + IDAT: pixel data + IEND: the final chunk + tRNS: transparency for palettized images + tEXt: textual information + zTXt: compressed textual information + iTXt: international textual information + bKGD: suggested background color + pHYs: physical dimensions + tIME: modification time + +1.2. features not supported +--------------------------- + +The following features are _not_ supported: + +*) some features needed to make a conformant PNG-Editor might be still missing. +*) partial loading/stream processing. All data must be available and is processed in one call. +*) The following public chunks are not supported but treated as unknown chunks by LodePNG + cHRM, gAMA, iCCP, sRGB, sBIT, hIST, sPLT + Some of these are not supported on purpose: LodePNG wants to provide the RGB values + stored in the pixels, not values modified by system dependent gamma or color models. + + +2. C and C++ version +-------------------- + +The C version uses buffers allocated with alloc that you need to free() +yourself. You need to use init and cleanup functions for each struct whenever +using a struct from the C version to avoid exploits and memory leaks. + +The C++ version has extra functions with std::vectors in the interface and the +lodepng::State class which is a LodePNGState with constructor and destructor. + +These files work without modification for both C and C++ compilers because all +the additional C++ code is in "#ifdef __cplusplus" blocks that make C-compilers +ignore it, and the C code is made to compile both with strict ISO C90 and C++. + +To use the C++ version, you need to rename the source file to lodepng.cpp +(instead of lodepng.c), and compile it with a C++ compiler. + +To use the C version, you need to rename the source file to lodepng.c (instead +of lodepng.cpp), and compile it with a C compiler. + + +3. Security +----------- + +Even if carefully designed, it's always possible that LodePNG contains possible +exploits. If you discover one, please let me know, and it will be fixed. + +When using LodePNG, care has to be taken with the C version of LodePNG, as well +as the C-style structs when working with C++. The following conventions are used +for all C-style structs: + +-if a struct has a corresponding init function, always call the init function when making a new one +-if a struct has a corresponding cleanup function, call it before the struct disappears to avoid memory leaks +-if a struct has a corresponding copy function, use the copy function instead of "=". + The destination must also be inited already. + + +4. Decoding +----------- + +Decoding converts a PNG compressed image to a raw pixel buffer. + +Most documentation on using the decoder is at its declarations in the header +above. For C, simple decoding can be done with functions such as +lodepng_decode32, and more advanced decoding can be done with the struct +LodePNGState and lodepng_decode. For C++, all decoding can be done with the +various lodepng::decode functions, and lodepng::State can be used for advanced +features. + +When using the LodePNGState, it uses the following fields for decoding: +*) LodePNGInfo info_png: it stores extra information about the PNG (the input) in here +*) LodePNGColorMode info_raw: here you can say what color mode of the raw image (the output) you want to get +*) LodePNGDecoderSettings decoder: you can specify a few extra settings for the decoder to use + +LodePNGInfo info_png +-------------------- + +After decoding, this contains extra information of the PNG image, except the actual +pixels, width and height because these are already gotten directly from the decoder +functions. + +It contains for example the original color type of the PNG image, text comments, +suggested background color, etc... More details about the LodePNGInfo struct are +at its declaration documentation. + +LodePNGColorMode info_raw +------------------------- + +When decoding, here you can specify which color type you want +the resulting raw image to be. If this is different from the colortype of the +PNG, then the decoder will automatically convert the result. This conversion +always works, except if you want it to convert a color PNG to greyscale or to +a palette with missing colors. + +By default, 32-bit color is used for the result. + +LodePNGDecoderSettings decoder +------------------------------ + +The settings can be used to ignore the errors created by invalid CRC and Adler32 +chunks, and to disable the decoding of tEXt chunks. + +There's also a setting color_convert, true by default. If false, no conversion +is done, the resulting data will be as it was in the PNG (after decompression) +and you'll have to puzzle the colors of the pixels together yourself using the +color type information in the LodePNGInfo. + + +5. Encoding +----------- + +Encoding converts a raw pixel buffer to a PNG compressed image. + +Most documentation on using the encoder is at its declarations in the header +above. For C, simple encoding can be done with functions such as +lodepng_encode32, and more advanced decoding can be done with the struct +LodePNGState and lodepng_encode. For C++, all encoding can be done with the +various lodepng::encode functions, and lodepng::State can be used for advanced +features. + +Like the decoder, the encoder can also give errors. However it gives less errors +since the encoder input is trusted, the decoder input (a PNG image that could +be forged by anyone) is not trusted. + +When using the LodePNGState, it uses the following fields for encoding: +*) LodePNGInfo info_png: here you specify how you want the PNG (the output) to be. +*) LodePNGColorMode info_raw: here you say what color type of the raw image (the input) has +*) LodePNGEncoderSettings encoder: you can specify a few settings for the encoder to use + +LodePNGInfo info_png +-------------------- + +When encoding, you use this the opposite way as when decoding: for encoding, +you fill in the values you want the PNG to have before encoding. By default it's +not needed to specify a color type for the PNG since it's automatically chosen, +but it's possible to choose it yourself given the right settings. + +The encoder will not always exactly match the LodePNGInfo struct you give, +it tries as close as possible. Some things are ignored by the encoder. The +encoder uses, for example, the following settings from it when applicable: +colortype and bitdepth, text chunks, time chunk, the color key, the palette, the +background color, the interlace method, unknown chunks, ... + +When encoding to a PNG with colortype 3, the encoder will generate a PLTE chunk. +If the palette contains any colors for which the alpha channel is not 255 (so +there are translucent colors in the palette), it'll add a tRNS chunk. + +LodePNGColorMode info_raw +------------------------- + +You specify the color type of the raw image that you give to the input here, +including a possible transparent color key and palette you happen to be using in +your raw image data. + +By default, 32-bit color is assumed, meaning your input has to be in RGBA +format with 4 bytes (unsigned chars) per pixel. + +LodePNGEncoderSettings encoder +------------------------------ + +The following settings are supported (some are in sub-structs): +*) auto_convert: when this option is enabled, the encoder will +automatically choose the smallest possible color mode (including color key) that +can encode the colors of all pixels without information loss. +*) btype: the block type for LZ77. 0 = uncompressed, 1 = fixed huffman tree, + 2 = dynamic huffman tree (best compression). Should be 2 for proper + compression. +*) use_lz77: whether or not to use LZ77 for compressed block types. Should be + true for proper compression. +*) windowsize: the window size used by the LZ77 encoder (1 - 32768). Has value + 2048 by default, but can be set to 32768 for better, but slow, compression. +*) force_palette: if colortype is 2 or 6, you can make the encoder write a PLTE + chunk if force_palette is true. This can used as suggested palette to convert + to by viewers that don't support more than 256 colors (if those still exist) +*) add_id: add text chunk "Encoder: LodePNG " to the image. +*) text_compression: default 1. If 1, it'll store texts as zTXt instead of tEXt chunks. + zTXt chunks use zlib compression on the text. This gives a smaller result on + large texts but a larger result on small texts (such as a single program name). + It's all tEXt or all zTXt though, there's no separate setting per text yet. + + +6. color conversions +-------------------- + +An important thing to note about LodePNG, is that the color type of the PNG, and +the color type of the raw image, are completely independent. By default, when +you decode a PNG, you get the result as a raw image in the color type you want, +no matter whether the PNG was encoded with a palette, greyscale or RGBA color. +And if you encode an image, by default LodePNG will automatically choose the PNG +color type that gives good compression based on the values of colors and amount +of colors in the image. It can be configured to let you control it instead as +well, though. + +To be able to do this, LodePNG does conversions from one color mode to another. +It can convert from almost any color type to any other color type, except the +following conversions: RGB to greyscale is not supported, and converting to a +palette when the palette doesn't have a required color is not supported. This is +not supported on purpose: this is information loss which requires a color +reduction algorithm that is beyong the scope of a PNG encoder (yes, RGB to grey +is easy, but there are multiple ways if you want to give some channels more +weight). + +By default, when decoding, you get the raw image in 32-bit RGBA or 24-bit RGB +color, no matter what color type the PNG has. And by default when encoding, +LodePNG automatically picks the best color model for the output PNG, and expects +the input image to be 32-bit RGBA or 24-bit RGB. So, unless you want to control +the color format of the images yourself, you can skip this chapter. + +6.1. PNG color types +-------------------- + +A PNG image can have many color types, ranging from 1-bit color to 64-bit color, +as well as palettized color modes. After the zlib decompression and unfiltering +in the PNG image is done, the raw pixel data will have that color type and thus +a certain amount of bits per pixel. If you want the output raw image after +decoding to have another color type, a conversion is done by LodePNG. + +The PNG specification gives the following color types: + +0: greyscale, bit depths 1, 2, 4, 8, 16 +2: RGB, bit depths 8 and 16 +3: palette, bit depths 1, 2, 4 and 8 +4: greyscale with alpha, bit depths 8 and 16 +6: RGBA, bit depths 8 and 16 + +Bit depth is the amount of bits per pixel per color channel. So the total amount +of bits per pixel is: amount of channels * bitdepth. + +6.2. color conversions +---------------------- + +As explained in the sections about the encoder and decoder, you can specify +color types and bit depths in info_png and info_raw to change the default +behaviour. + +If, when decoding, you want the raw image to be something else than the default, +you need to set the color type and bit depth you want in the LodePNGColorMode, +or the parameters colortype and bitdepth of the simple decoding function. + +If, when encoding, you use another color type than the default in the raw input +image, you need to specify its color type and bit depth in the LodePNGColorMode +of the raw image, or use the parameters colortype and bitdepth of the simple +encoding function. + +If, when encoding, you don't want LodePNG to choose the output PNG color type +but control it yourself, you need to set auto_convert in the encoder settings +to false, and specify the color type you want in the LodePNGInfo of the +encoder (including palette: it can generate a palette if auto_convert is true, +otherwise not). + +If the input and output color type differ (whether user chosen or auto chosen), +LodePNG will do a color conversion, which follows the rules below, and may +sometimes result in an error. + +To avoid some confusion: +-the decoder converts from PNG to raw image +-the encoder converts from raw image to PNG +-the colortype and bitdepth in LodePNGColorMode info_raw, are those of the raw image +-the colortype and bitdepth in the color field of LodePNGInfo info_png, are those of the PNG +-when encoding, the color type in LodePNGInfo is ignored if auto_convert + is enabled, it is automatically generated instead +-when decoding, the color type in LodePNGInfo is set by the decoder to that of the original + PNG image, but it can be ignored since the raw image has the color type you requested instead +-if the color type of the LodePNGColorMode and PNG image aren't the same, a conversion + between the color types is done if the color types are supported. If it is not + supported, an error is returned. If the types are the same, no conversion is done. +-even though some conversions aren't supported, LodePNG supports loading PNGs from any + colortype and saving PNGs to any colortype, sometimes it just requires preparing + the raw image correctly before encoding. +-both encoder and decoder use the same color converter. + +Non supported color conversions: +-color to greyscale: no error is thrown, but the result will look ugly because +only the red channel is taken +-anything to palette when that palette does not have that color in it: in this +case an error is thrown + +Supported color conversions: +-anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA +-any grey or grey+alpha, to grey or grey+alpha +-anything to a palette, as long as the palette has the requested colors in it +-removing alpha channel +-higher to smaller bitdepth, and vice versa + +If you want no color conversion to be done (e.g. for speed or control): +-In the encoder, you can make it save a PNG with any color type by giving the +raw color mode and LodePNGInfo the same color mode, and setting auto_convert to +false. +-In the decoder, you can make it store the pixel data in the same color type +as the PNG has, by setting the color_convert setting to false. Settings in +info_raw are then ignored. + +The function lodepng_convert does the color conversion. It is available in the +interface but normally isn't needed since the encoder and decoder already call +it. + +6.3. padding bits +----------------- + +In the PNG file format, if a less than 8-bit per pixel color type is used and the scanlines +have a bit amount that isn't a multiple of 8, then padding bits are used so that each +scanline starts at a fresh byte. But that is NOT true for the LodePNG raw input and output. +The raw input image you give to the encoder, and the raw output image you get from the decoder +will NOT have these padding bits, e.g. in the case of a 1-bit image with a width +of 7 pixels, the first pixel of the second scanline will the the 8th bit of the first byte, +not the first bit of a new byte. + +6.4. A note about 16-bits per channel and endianness +---------------------------------------------------- + +LodePNG uses unsigned char arrays for 16-bit per channel colors too, just like +for any other color format. The 16-bit values are stored in big endian (most +significant byte first) in these arrays. This is the opposite order of the +little endian used by x86 CPU's. + +LodePNG always uses big endian because the PNG file format does so internally. +Conversions to other formats than PNG uses internally are not supported by +LodePNG on purpose, there are myriads of formats, including endianness of 16-bit +colors, the order in which you store R, G, B and A, and so on. Supporting and +converting to/from all that is outside the scope of LodePNG. + +This may mean that, depending on your use case, you may want to convert the big +endian output of LodePNG to little endian with a for loop. This is certainly not +always needed, many applications and libraries support big endian 16-bit colors +anyway, but it means you cannot simply cast the unsigned char* buffer to an +unsigned short* buffer on x86 CPUs. + + +7. error values +--------------- + +All functions in LodePNG that return an error code, return 0 if everything went +OK, or a non-zero code if there was an error. + +The meaning of the LodePNG error values can be retrieved with the function +lodepng_error_text: given the numerical error code, it returns a description +of the error in English as a string. + +Check the implementation of lodepng_error_text to see the meaning of each code. + + +8. chunks and PNG editing +------------------------- + +If you want to add extra chunks to a PNG you encode, or use LodePNG for a PNG +editor that should follow the rules about handling of unknown chunks, or if your +program is able to read other types of chunks than the ones handled by LodePNG, +then that's possible with the chunk functions of LodePNG. + +A PNG chunk has the following layout: + +4 bytes length +4 bytes type name +length bytes data +4 bytes CRC + +8.1. iterating through chunks +----------------------------- + +If you have a buffer containing the PNG image data, then the first chunk (the +IHDR chunk) starts at byte number 8 of that buffer. The first 8 bytes are the +signature of the PNG and are not part of a chunk. But if you start at byte 8 +then you have a chunk, and can check the following things of it. + +NOTE: none of these functions check for memory buffer boundaries. To avoid +exploits, always make sure the buffer contains all the data of the chunks. +When using lodepng_chunk_next, make sure the returned value is within the +allocated memory. + +unsigned lodepng_chunk_length(const unsigned char* chunk): + +Get the length of the chunk's data. The total chunk length is this length + 12. + +void lodepng_chunk_type(char type[5], const unsigned char* chunk): +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type): + +Get the type of the chunk or compare if it's a certain type + +unsigned char lodepng_chunk_critical(const unsigned char* chunk): +unsigned char lodepng_chunk_private(const unsigned char* chunk): +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk): + +Check if the chunk is critical in the PNG standard (only IHDR, PLTE, IDAT and IEND are). +Check if the chunk is private (public chunks are part of the standard, private ones not). +Check if the chunk is safe to copy. If it's not, then, when modifying data in a critical +chunk, unsafe to copy chunks of the old image may NOT be saved in the new one if your +program doesn't handle that type of unknown chunk. + +unsigned char* lodepng_chunk_data(unsigned char* chunk): +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk): + +Get a pointer to the start of the data of the chunk. + +unsigned lodepng_chunk_check_crc(const unsigned char* chunk): +void lodepng_chunk_generate_crc(unsigned char* chunk): + +Check if the crc is correct or generate a correct one. + +unsigned char* lodepng_chunk_next(unsigned char* chunk): +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk): + +Iterate to the next chunk. This works if you have a buffer with consecutive chunks. Note that these +functions do no boundary checking of the allocated data whatsoever, so make sure there is enough +data available in the buffer to be able to go to the next chunk. + +unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk): +unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, + const char* type, const unsigned char* data): + +These functions are used to create new chunks that are appended to the data in *out that has +length *outlength. The append function appends an existing chunk to the new data. The create +function creates a new chunk with the given parameters and appends it. Type is the 4-letter +name of the chunk. + +8.2. chunks in info_png +----------------------- + +The LodePNGInfo struct contains fields with the unknown chunk in it. It has 3 +buffers (each with size) to contain 3 types of unknown chunks: +the ones that come before the PLTE chunk, the ones that come between the PLTE +and the IDAT chunks, and the ones that come after the IDAT chunks. +It's necessary to make the distionction between these 3 cases because the PNG +standard forces to keep the ordering of unknown chunks compared to the critical +chunks, but does not force any other ordering rules. + +info_png.unknown_chunks_data[0] is the chunks before PLTE +info_png.unknown_chunks_data[1] is the chunks after PLTE, before IDAT +info_png.unknown_chunks_data[2] is the chunks after IDAT + +The chunks in these 3 buffers can be iterated through and read by using the same +way described in the previous subchapter. + +When using the decoder to decode a PNG, you can make it store all unknown chunks +if you set the option settings.remember_unknown_chunks to 1. By default, this +option is off (0). + +The encoder will always encode unknown chunks that are stored in the info_png. +If you need it to add a particular chunk that isn't known by LodePNG, you can +use lodepng_chunk_append or lodepng_chunk_create to the chunk data in +info_png.unknown_chunks_data[x]. + +Chunks that are known by LodePNG should not be added in that way. E.g. to make +LodePNG add a bKGD chunk, set background_defined to true and add the correct +parameters there instead. + + +9. compiler support +------------------- + +No libraries other than the current standard C library are needed to compile +LodePNG. For the C++ version, only the standard C++ library is needed on top. +Add the files lodepng.c(pp) and lodepng.h to your project, include +lodepng.h where needed, and your program can read/write PNG files. + +It is compatible with C90 and up, and C++03 and up. + +If performance is important, use optimization when compiling! For both the +encoder and decoder, this makes a large difference. + +Make sure that LodePNG is compiled with the same compiler of the same version +and with the same settings as the rest of the program, or the interfaces with +std::vectors and std::strings in C++ can be incompatible. + +CHAR_BITS must be 8 or higher, because LodePNG uses unsigned chars for octets. + +*) gcc and g++ + +LodePNG is developed in gcc so this compiler is natively supported. It gives no +warnings with compiler options "-Wall -Wextra -pedantic -ansi", with gcc and g++ +version 4.7.1 on Linux, 32-bit and 64-bit. + +*) Clang + +Fully supported and warning-free. + +*) Mingw + +The Mingw compiler (a port of gcc for Windows) should be fully supported by +LodePNG. + +*) Visual Studio and Visual C++ Express Edition + +LodePNG should be warning-free with warning level W4. Two warnings were disabled +with pragmas though: warning 4244 about implicit conversions, and warning 4996 +where it wants to use a non-standard function fopen_s instead of the standard C +fopen. + +Visual Studio may want "stdafx.h" files to be included in each source file and +give an error "unexpected end of file while looking for precompiled header". +This is not standard C++ and will not be added to the stock LodePNG. You can +disable it for lodepng.cpp only by right clicking it, Properties, C/C++, +Precompiled Headers, and set it to Not Using Precompiled Headers there. + +NOTE: Modern versions of VS should be fully supported, but old versions, e.g. +VS6, are not guaranteed to work. + +*) Compilers on Macintosh + +LodePNG has been reported to work both with gcc and LLVM for Macintosh, both for +C and C++. + +*) Other Compilers + +If you encounter problems on any compilers, feel free to let me know and I may +try to fix it if the compiler is modern and standards complient. + + +10. examples +------------ + +This decoder example shows the most basic usage of LodePNG. More complex +examples can be found on the LodePNG website. + +10.1. decoder C++ example +------------------------- + +#include "lodepng.h" +#include + +int main(int argc, char *argv[]) +{ + const char* filename = argc > 1 ? argv[1] : "test.png"; + + //load and decode + std::vector image; + unsigned width, height; + unsigned error = lodepng::decode(image, width, height, filename); + + //if there's an error, display it + if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl; + + //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ... +} + +10.2. decoder C example +----------------------- + +#include "lodepng.h" + +int main(int argc, char *argv[]) +{ + unsigned error; + unsigned char* image; + size_t width, height; + const char* filename = argc > 1 ? argv[1] : "test.png"; + + error = lodepng_decode32_file(&image, &width, &height, filename); + + if(error) printf("decoder error %u: %s\n", error, lodepng_error_text(error)); + + / * use image here * / + + free(image); + return 0; +} + +11. state settings reference +---------------------------- + +A quick reference of some settings to set on the LodePNGState + +For decoding: + +state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums +state.decoder.zlibsettings.custom_...: use custom inflate function +state.decoder.ignore_crc: ignore CRC checksums +state.decoder.color_convert: convert internal PNG color to chosen one +state.decoder.read_text_chunks: whether to read in text metadata chunks +state.decoder.remember_unknown_chunks: whether to read in unknown chunks +state.info_raw.colortype: desired color type for decoded image +state.info_raw.bitdepth: desired bit depth for decoded image +state.info_raw....: more color settings, see struct LodePNGColorMode +state.info_png....: no settings for decoder but ouput, see struct LodePNGInfo + +For encoding: + +state.encoder.zlibsettings.btype: disable compression by setting it to 0 +state.encoder.zlibsettings.use_lz77: use LZ77 in compression +state.encoder.zlibsettings.windowsize: tweak LZ77 windowsize +state.encoder.zlibsettings.minmatch: tweak min LZ77 length to match +state.encoder.zlibsettings.nicematch: tweak LZ77 match where to stop searching +state.encoder.zlibsettings.lazymatching: try one more LZ77 matching +state.encoder.zlibsettings.custom_...: use custom deflate function +state.encoder.auto_convert: choose optimal PNG color type, if 0 uses info_png +state.encoder.filter_palette_zero: PNG filter strategy for palette +state.encoder.filter_strategy: PNG filter strategy to encode with +state.encoder.force_palette: add palette even if not encoding to one +state.encoder.add_id: add LodePNG identifier and version as a text chunk +state.encoder.text_compression: use compressed text chunks for metadata +state.info_raw.colortype: color type of raw input image you provide +state.info_raw.bitdepth: bit depth of raw input image you provide +state.info_raw: more color settings, see struct LodePNGColorMode +state.info_png.color.colortype: desired color type if auto_convert is false +state.info_png.color.bitdepth: desired bit depth if auto_convert is false +state.info_png.color....: more color settings, see struct LodePNGColorMode +state.info_png....: more PNG related settings, see struct LodePNGInfo + + +12. changes +----------- + +The version number of LodePNG is the date of the change given in the format +yyyymmdd. + +Some changes aren't backwards compatible. Those are indicated with a (!) +symbol. + +*) 08 dec 2015: Made load_file function return error if file can't be opened. +*) 24 okt 2015: Bugfix with decoding to palette output. +*) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding. +*) 23 aug 2014: Reduced needless memory usage of decoder. +*) 28 jun 2014: Removed fix_png setting, always support palette OOB for + simplicity. Made ColorProfile public. +*) 09 jun 2014: Faster encoder by fixing hash bug and more zeros optimization. +*) 22 dec 2013: Power of two windowsize required for optimization. +*) 15 apr 2013: Fixed bug with LAC_ALPHA and color key. +*) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png). +*) 11 mar 2013 (!): Bugfix with custom free. Changed from "my" to "lodepng_" + prefix for the custom allocators and made it possible with a new #define to + use custom ones in your project without needing to change lodepng's code. +*) 28 jan 2013: Bugfix with color key. +*) 27 okt 2012: Tweaks in text chunk keyword length error handling. +*) 8 okt 2012 (!): Added new filter strategy (entropy) and new auto color mode. + (no palette). Better deflate tree encoding. New compression tweak settings. + Faster color conversions while decoding. Some internal cleanups. +*) 23 sep 2012: Reduced warnings in Visual Studio a little bit. +*) 1 sep 2012 (!): Removed #define's for giving custom (de)compression functions + and made it work with function pointers instead. +*) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc + and free functions and toggle #defines from compiler flags. Small fixes. +*) 6 may 2012 (!): Made plugging in custom zlib/deflate functions more flexible. +*) 22 apr 2012 (!): Made interface more consistent, renaming a lot. Removed + redundant C++ codec classes. Reduced amount of structs. Everything changed, + but it is cleaner now imho and functionality remains the same. Also fixed + several bugs and shrunk the implementation code. Made new samples. +*) 6 nov 2011 (!): By default, the encoder now automatically chooses the best + PNG color model and bit depth, based on the amount and type of colors of the + raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color. +*) 9 okt 2011: simpler hash chain implementation for the encoder. +*) 8 sep 2011: lz77 encoder lazy matching instead of greedy matching. +*) 23 aug 2011: tweaked the zlib compression parameters after benchmarking. + A bug with the PNG filtertype heuristic was fixed, so that it chooses much + better ones (it's quite significant). A setting to do an experimental, slow, + brute force search for PNG filter types is added. +*) 17 aug 2011 (!): changed some C zlib related function names. +*) 16 aug 2011: made the code less wide (max 120 characters per line). +*) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors. +*) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled. +*) 11 dec 2010: encoding is made faster, based on suggestion by Peter Eastman + to optimize long sequences of zeros. +*) 13 nov 2010: added LodePNG_InfoColor_hasPaletteAlpha and + LodePNG_InfoColor_canHaveAlpha functions for convenience. +*) 7 nov 2010: added LodePNG_error_text function to get error code description. +*) 30 okt 2010: made decoding slightly faster +*) 26 okt 2010: (!) changed some C function and struct names (more consistent). + Reorganized the documentation and the declaration order in the header. +*) 08 aug 2010: only changed some comments and external samples. +*) 05 jul 2010: fixed bug thanks to warnings in the new gcc version. +*) 14 mar 2010: fixed bug where too much memory was allocated for char buffers. +*) 02 sep 2008: fixed bug where it could create empty tree that linux apps could + read by ignoring the problem but windows apps couldn't. +*) 06 jun 2008: added more error checks for out of memory cases. +*) 26 apr 2008: added a few more checks here and there to ensure more safety. +*) 06 mar 2008: crash with encoding of strings fixed +*) 02 feb 2008: support for international text chunks added (iTXt) +*) 23 jan 2008: small cleanups, and #defines to divide code in sections +*) 20 jan 2008: support for unknown chunks allowing using LodePNG for an editor. +*) 18 jan 2008: support for tIME and pHYs chunks added to encoder and decoder. +*) 17 jan 2008: ability to encode and decode compressed zTXt chunks added + Also various fixes, such as in the deflate and the padding bits code. +*) 13 jan 2008: Added ability to encode Adam7-interlaced images. Improved + filtering code of encoder. +*) 07 jan 2008: (!) changed LodePNG to use ISO C90 instead of C++. A + C++ wrapper around this provides an interface almost identical to before. + Having LodePNG be pure ISO C90 makes it more portable. The C and C++ code + are together in these files but it works both for C and C++ compilers. +*) 29 dec 2007: (!) changed most integer types to unsigned int + other tweaks +*) 30 aug 2007: bug fixed which makes this Borland C++ compatible +*) 09 aug 2007: some VS2005 warnings removed again +*) 21 jul 2007: deflate code placed in new namespace separate from zlib code +*) 08 jun 2007: fixed bug with 2- and 4-bit color, and small interlaced images +*) 04 jun 2007: improved support for Visual Studio 2005: crash with accessing + invalid std::vector element [0] fixed, and level 3 and 4 warnings removed +*) 02 jun 2007: made the encoder add a tag with version by default +*) 27 may 2007: zlib and png code separated (but still in the same file), + simple encoder/decoder functions added for more simple usage cases +*) 19 may 2007: minor fixes, some code cleaning, new error added (error 69), + moved some examples from here to lodepng_examples.cpp +*) 12 may 2007: palette decoding bug fixed +*) 24 apr 2007: changed the license from BSD to the zlib license +*) 11 mar 2007: very simple addition: ability to encode bKGD chunks. +*) 04 mar 2007: (!) tEXt chunk related fixes, and support for encoding + palettized PNG images. Plus little interface change with palette and texts. +*) 03 mar 2007: Made it encode dynamic Huffman shorter with repeat codes. + Fixed a bug where the end code of a block had length 0 in the Huffman tree. +*) 26 feb 2007: Huffman compression with dynamic trees (BTYPE 2) now implemented + and supported by the encoder, resulting in smaller PNGs at the output. +*) 27 jan 2007: Made the Adler-32 test faster so that a timewaste is gone. +*) 24 jan 2007: gave encoder an error interface. Added color conversion from any + greyscale type to 8-bit greyscale with or without alpha. +*) 21 jan 2007: (!) Totally changed the interface. It allows more color types + to convert to and is more uniform. See the manual for how it works now. +*) 07 jan 2007: Some cleanup & fixes, and a few changes over the last days: + encode/decode custom tEXt chunks, separate classes for zlib & deflate, and + at last made the decoder give errors for incorrect Adler32 or Crc. +*) 01 jan 2007: Fixed bug with encoding PNGs with less than 8 bits per channel. +*) 29 dec 2006: Added support for encoding images without alpha channel, and + cleaned out code as well as making certain parts faster. +*) 28 dec 2006: Added "Settings" to the encoder. +*) 26 dec 2006: The encoder now does LZ77 encoding and produces much smaller files now. + Removed some code duplication in the decoder. Fixed little bug in an example. +*) 09 dec 2006: (!) Placed output parameters of public functions as first parameter. + Fixed a bug of the decoder with 16-bit per color. +*) 15 okt 2006: Changed documentation structure +*) 09 okt 2006: Encoder class added. It encodes a valid PNG image from the + given image buffer, however for now it's not compressed. +*) 08 sep 2006: (!) Changed to interface with a Decoder class +*) 30 jul 2006: (!) LodePNG_InfoPng , width and height are now retrieved in different + way. Renamed decodePNG to decodePNGGeneric. +*) 29 jul 2006: (!) Changed the interface: image info is now returned as a + struct of type LodePNG::LodePNG_Info, instead of a vector, which was a bit clumsy. +*) 28 jul 2006: Cleaned the code and added new error checks. + Corrected terminology "deflate" into "inflate". +*) 23 jun 2006: Added SDL example in the documentation in the header, this + example allows easy debugging by displaying the PNG and its transparency. +*) 22 jun 2006: (!) Changed way to obtain error value. Added + loadFile function for convenience. Made decodePNG32 faster. +*) 21 jun 2006: (!) Changed type of info vector to unsigned. + Changed position of palette in info vector. Fixed an important bug that + happened on PNGs with an uncompressed block. +*) 16 jun 2006: Internally changed unsigned into unsigned where + needed, and performed some optimizations. +*) 07 jun 2006: (!) Renamed functions to decodePNG and placed them + in LodePNG namespace. Changed the order of the parameters. Rewrote the + documentation in the header. Renamed files to lodepng.cpp and lodepng.h +*) 22 apr 2006: Optimized and improved some code +*) 07 sep 2005: (!) Changed to std::vector interface +*) 12 aug 2005: Initial release (C++, decoder only) + + +13. contact information +----------------------- + +Feel free to contact me with suggestions, problems, comments, ... concerning +LodePNG. If you encounter a PNG image that doesn't work properly with this +decoder, feel free to send it and I'll use it to find and fix the problem. + +My email address is (puzzle the account and domain together with an @ symbol): +Domain: gmail dot com. +Account: lode dot vandevenne. + + +Copyright (c) 2005-2016 Lode Vandevenne +*/ diff --git a/deps/freetype-2.3.12/Makefile b/deps/freetype-2.3.12/Makefile index b6672596c7..7794bd3a72 100644 --- a/deps/freetype-2.3.12/Makefile +++ b/deps/freetype-2.3.12/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -164,7 +168,7 @@ CECFLAGS += -cpp $(_CPP) all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(TARGET): $(SOURCES) $(RESOURCES) | objdir $(SYMBOLS) $(OBJECTS) $(AR) rcs $(TARGET) $(OBJECTS) $(LIBS) @@ -307,7 +311,7 @@ $(OBJ)type42.o: src/type42/type42.c $(OBJ)winfnt.o: src/winfonts/winfnt.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c src/winfonts/winfnt.c -o $(OBJ)winfnt.o -cleantarget: objdir +cleantarget: $(call rm,$(TARGET)) clean: cleantarget @@ -317,9 +321,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/deps/freetype-2.3.12/src/autofit/aftypes.h b/deps/freetype-2.3.12/src/autofit/aftypes.h index 5574f0c302..255ef8f839 100644 --- a/deps/freetype-2.3.12/src/autofit/aftypes.h +++ b/deps/freetype-2.3.12/src/autofit/aftypes.h @@ -309,7 +309,7 @@ extern void* _af_debug_hints; (*AF_Script_InitHintsFunc)( AF_GlyphHints hints, AF_ScriptMetrics metrics ); - typedef void + typedef FT_Error (*AF_Script_ApplyHintsFunc)( AF_GlyphHints hints, FT_Outline* outline, AF_ScriptMetrics metrics ); diff --git a/deps/jpeg-9a/Makefile b/deps/jpeg-9a/Makefile index 1aab2571a1..5d02db97d5 100644 --- a/deps/jpeg-9a/Makefile +++ b/deps/jpeg-9a/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -161,7 +165,7 @@ CECFLAGS += -cpp $(_CPP) all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(SYMBOLS): | objdir $(OBJECTS): | objdir @@ -338,7 +342,7 @@ $(OBJ)jmemnobs.o: jmemnobs.c $(OBJ)transupp.o: transupp.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(call quote_path,transupp.c) -o $(call quote_path,$@) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)objects.lst) $(call rm,$(TARGET)) ifdef SHARED_LIBRARY_TARGET @@ -357,9 +361,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/deps/libffi-3.0.11/Makefile b/deps/libffi-3.0.11/Makefile index 5a62971b29..3e0449722e 100644 --- a/deps/libffi-3.0.11/Makefile +++ b/deps/libffi-3.0.11/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -88,7 +92,7 @@ CECFLAGS += -cpp $(_CPP) all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(SYMBOLS): | objdir $(OBJECTS): | objdir @@ -126,7 +130,7 @@ $(OBJ)types.o: src/types.c $(OBJ)closures.o: src/closures.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c src/closures.c -o $(OBJ)closures.o -cleantarget: objdir +cleantarget: $(call rm,$(TARGET)) clean: cleantarget @@ -135,9 +139,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/deps/libpng-1.6.12/Makefile b/deps/libpng-1.6.12/Makefile index d61570d842..b131633048 100644 --- a/deps/libpng-1.6.12/Makefile +++ b/deps/libpng-1.6.12/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -97,7 +101,7 @@ CECFLAGS += -cpp $(_CPP) all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(SYMBOLS): | objdir $(OBJECTS): | objdir @@ -177,7 +181,7 @@ $(OBJ)pngwtran.o: ./pngwtran.c $(OBJ)pngwutil.o: ./pngwutil.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(call quote_path,./pngwutil.c) -o $(call quote_path,$@) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)objects.lst) $(call rm,$(TARGET)) ifdef SHARED_LIBRARY_TARGET @@ -195,9 +199,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/deps/libtess/README b/deps/libtess/README new file mode 100644 index 0000000000..7c314b74a0 --- /dev/null +++ b/deps/libtess/README @@ -0,0 +1,447 @@ +/* +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/README,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +General Polygon Tesselation +--------------------------- + + This note describes a tesselator for polygons consisting of one or + more closed contours. It is backward-compatible with the current + OpenGL Utilities tesselator, and is intended to replace it. Here is + a summary of the major differences: + + - input contours can be intersecting, self-intersecting, or degenerate. + + - supports a choice of several winding rules for determining which parts + of the polygon are on the "interior". This makes it possible to do + CSG operations on polygons. + + - boundary extraction: instead of tesselating the polygon, returns a + set of closed contours which separate the interior from the exterior. + + - returns the output as a small number of triangle fans and strips, + rather than a list of independent triangles (when possible). + + - output is available as an explicit mesh (a quad-edge structure), + in addition to the normal callback interface. + + - the algorithm used is extremely robust. + + +The interface +------------- + + The tesselator state is maintained in a "tesselator object". + These are allocated and destroyed using + + GLUtesselator *gluNewTess( void ); + void gluDeleteTess( GLUtesselator *tess ); + + Several tesselator objects may be used simultaneously. + + Inputs + ------ + + The input contours are specified with the following routines: + + void gluTessBeginPolygon( GLUtesselator *tess ); + void gluTessBeginContour( GLUtesselator *tess ); + void gluTessVertex( GLUtesselator *tess, GLUcoord coords[3], void *data ); + void gluTessEndContour( GLUtesselator *tess ); + void gluTessEndPolygon( GLUtesselator *tess ); + + Within each BeginPolygon/EndPolygon pair, there can be zero or more + calls to BeginContour/EndContour. Within each contour, there are zero + or more calls to gluTessVertex(). The vertices specify a closed + contour (the last vertex of each contour is automatically linked to + the first). + + "coords" give the coordinates of the vertex in 3-space. For useful + results, all vertices should lie in some plane, since the vertices + are projected onto a plane before tesselation. "data" is a pointer + to a user-defined vertex structure, which typically contains other + information such as color, texture coordinates, normal, etc. It is + used to refer to the vertex during rendering. + + The library can be compiled in single- or double-precision; the type + GLUcoord represents either "float" or "double" accordingly. The GLU + version will be available in double-precision only. Compile with + GLU_TESS_API_FLOAT defined to get the single-precision version. + + When EndPolygon is called, the tesselation algorithm determines + which regions are interior to the given contours, according to one + of several "winding rules" described below. The interior regions + are then tesselated, and the output is provided as callbacks. + + + Rendering Callbacks + ------------------- + + Callbacks are specified by the client using + + void gluTessCallback( GLUtesselator *tess, GLenum which, void (*fn)()); + + If "fn" is NULL, any previously defined callback is discarded. + + The callbacks used to provide output are: /* which == */ + + void begin( GLenum type ); /* GLU_TESS_BEGIN */ + void edgeFlag( GLboolean flag ); /* GLU_TESS_EDGE_FLAG */ + void vertex( void *data ); /* GLU_TESS_VERTEX */ + void end( void ); /* GLU_TESS_END */ + + Any of the callbacks may be left undefined; if so, the corresponding + information will not be supplied during rendering. + + The "begin" callback indicates the start of a primitive; type is one + of GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, or GL_TRIANGLES (but see the + notes on "boundary extraction" below). + + It is followed by any number of "vertex" callbacks, which supply the + vertices in the same order as expected by the corresponding glBegin() + call. After the last vertex of a given primitive, there is a callback + to "end". + + If the "edgeFlag" callback is provided, no triangle fans or strips + will be used. When edgeFlag is called, if "flag" is GL_TRUE then each + vertex which follows begins an edge which lies on the polygon boundary + (ie. an edge which separates an interior region from an exterior one). + If "flag" is GL_FALSE, each vertex which follows begins an edge which lies + in the polygon interior. "edgeFlag" will be called before the first + call to "vertex". + + Other Callbacks + --------------- + + void mesh( GLUmesh *mesh ); /* GLU_TESS_MESH */ + + - Returns an explicit mesh, represented using the quad-edge structure + (Guibas/Stolfi '85). Other implementations of this interface might + use a different mesh structure, so this is available only only as an + SGI extension. When the mesh is no longer needed, it should be freed + using + + void gluDeleteMesh( GLUmesh *mesh ); + + There is a brief description of this data structure in the include + file "mesh.h". For the full details, see L. Guibas and J. Stolfi, + Primitives for the manipulation of general subdivisions and the + computation of Voronoi diagrams, ACM Transactions on Graphics, + 4(2):74-123, April 1985. For an introduction, see the course notes + for CS348a, "Mathematical Foundations of Computer Graphics", + available at the Stanford bookstore (and taught during the fall + quarter). + + void error( GLenum errno ); /* GLU_TESS_ERROR */ + + - errno is one of GLU_TESS_MISSING_BEGIN_POLYGON, + GLU_TESS_MISSING_END_POLYGON, + GLU_TESS_MISSING_BEGIN_CONTOUR, + GLU_TESS_MISSING_END_CONTOUR, + GLU_TESS_COORD_TOO_LARGE, + GLU_TESS_NEED_COMBINE_CALLBACK + + The first four are obvious. The interface recovers from these + errors by inserting the missing call(s). + + GLU_TESS_COORD_TOO_LARGE says that some vertex coordinate exceeded + the predefined constant GLU_TESS_MAX_COORD in absolute value, and + that the value has been clamped. (Coordinate values must be small + enough so that two can be multiplied together without overflow.) + + GLU_TESS_NEED_COMBINE_CALLBACK says that the algorithm detected an + intersection between two edges in the input data, and the "combine" + callback (below) was not provided. No output will be generated. + + + void combine( GLUcoord coords[3], void *data[4], /* GLU_TESS_COMBINE */ + GLUcoord weight[4], void **outData ); + + - When the algorithm detects an intersection, or wishes to merge + features, it needs to create a new vertex. The vertex is defined + as a linear combination of up to 4 existing vertices, referenced + by data[0..3]. The coefficients of the linear combination are + given by weight[0..3]; these weights always sum to 1.0. All vertex + pointers are valid even when some of the weights are zero. + "coords" gives the location of the new vertex. + + The user must allocate another vertex, interpolate parameters + using "data" and "weights", and return the new vertex pointer in + "outData". This handle is supplied during rendering callbacks. + For example, if the polygon lies in an arbitrary plane in 3-space, + and we associate a color with each vertex, the combine callback might + look like this: + + void myCombine( GLUcoord coords[3], VERTEX *d[4], + GLUcoord w[4], VERTEX **dataOut ) + { + VERTEX *new = new_vertex(); + + new->x = coords[0]; + new->y = coords[1]; + new->z = coords[2]; + new->r = w[0]*d[0]->r + w[1]*d[1]->r + w[2]*d[2]->r + w[3]*d[3]->r; + new->g = w[0]*d[0]->g + w[1]*d[1]->g + w[2]*d[2]->g + w[3]*d[3]->g; + new->b = w[0]*d[0]->b + w[1]*d[1]->b + w[2]*d[2]->b + w[3]*d[3]->b; + new->a = w[0]*d[0]->a + w[1]*d[1]->a + w[2]*d[2]->a + w[3]*d[3]->a; + *dataOut = new; + } + + If the algorithm detects an intersection, then the "combine" callback + must be defined, and must write a non-NULL pointer into "dataOut". + Otherwise the GLU_TESS_NEED_COMBINE_CALLBACK error occurs, and no + output is generated. This is the only error that can occur during + tesselation and rendering. + + + Control over Tesselation + ------------------------ + + void gluTessProperty( GLUtesselator *tess, GLenum which, GLUcoord value ); + + Properties defined: + + - GLU_TESS_WINDING_RULE. Possible values: + + GLU_TESS_WINDING_ODD + GLU_TESS_WINDING_NONZERO + GLU_TESS_WINDING_POSITIVE + GLU_TESS_WINDING_NEGATIVE + GLU_TESS_WINDING_ABS_GEQ_TWO + + The input contours parition the plane into regions. A winding + rule determines which of these regions are inside the polygon. + + For a single contour C, the winding number of a point x is simply + the signed number of revolutions we make around x as we travel + once around C (where CCW is positive). When there are several + contours, the individual winding numbers are summed. This + procedure associates a signed integer value with each point x in + the plane. Note that the winding number is the same for all + points in a single region. + + The winding rule classifies a region as "inside" if its winding + number belongs to the chosen category (odd, nonzero, positive, + negative, or absolute value of at least two). The current GLU + tesselator implements the "odd" rule. The "nonzero" rule is another + common way to define the interior. The other three rules are + useful for polygon CSG operations (see below). + + - GLU_TESS_BOUNDARY_ONLY. Values: TRUE (non-zero) or FALSE (zero). + + If TRUE, returns a set of closed contours which separate the + polygon interior and exterior (rather than a tesselation). + Exterior contours are oriented CCW with respect to the normal, + interior contours are oriented CW. The GLU_TESS_BEGIN callback + uses the type GL_LINE_LOOP for each contour. + + - GLU_TESS_TOLERANCE. Value: a real number between 0.0 and 1.0. + + This specifies a tolerance for merging features to reduce the size + of the output. For example, two vertices which are very close to + each other might be replaced by a single vertex. The tolerance + is multiplied by the largest coordinate magnitude of any input vertex; + this specifies the maximum distance that any feature can move as the + result of a single merge operation. If a single feature takes part + in several merge operations, the total distance moved could be larger. + + Feature merging is completely optional; the tolerance is only a hint. + The implementation is free to merge in some cases and not in others, + or to never merge features at all. The default tolerance is zero. + + The current implementation merges vertices only if they are exactly + coincident, regardless of the current tolerance. A vertex is + spliced into an edge only if the implementation is unable to + distinguish which side of the edge the vertex lies on. + Two edges are merged only when both endpoints are identical. + + + void gluTessNormal( GLUtesselator *tess, + GLUcoord x, GLUcoord y, GLUcoord z ) + + - Lets the user supply the polygon normal, if known. All input data + is projected into a plane perpendicular to the normal before + tesselation. All output triangles are oriented CCW with + respect to the normal (CW orientation can be obtained by + reversing the sign of the supplied normal). For example, if + you know that all polygons lie in the x-y plane, call + "gluTessNormal(tess, 0.0, 0.0, 1.0)" before rendering any polygons. + + - If the supplied normal is (0,0,0) (the default value), the + normal is determined as follows. The direction of the normal, + up to its sign, is found by fitting a plane to the vertices, + without regard to how the vertices are connected. It is + expected that the input data lies approximately in plane; + otherwise projection perpendicular to the computed normal may + substantially change the geometry. The sign of the normal is + chosen so that the sum of the signed areas of all input contours + is non-negative (where a CCW contour has positive area). + + - The supplied normal persists until it is changed by another + call to gluTessNormal. + + + Backward compatibility with the GLU tesselator + ---------------------------------------------- + + The preferred interface is the one described above. The following + routines are obsolete, and are provided only for backward compatibility: + + typedef GLUtesselator GLUtriangulatorObj; /* obsolete name */ + + void gluBeginPolygon( GLUtesselator *tess ); + void gluNextContour( GLUtesselator *tess, GLenum type ); + void gluEndPolygon( GLUtesselator *tess ); + + "type" is one of GLU_EXTERIOR, GLU_INTERIOR, GLU_CCW, GLU_CW, or + GLU_UNKNOWN. It is ignored by the current GLU tesselator. + + GLU_BEGIN, GLU_VERTEX, GLU_END, GLU_ERROR, and GLU_EDGE_FLAG are defined + as synonyms for GLU_TESS_BEGIN, GLU_TESS_VERTEX, GLU_TESS_END, + GLU_TESS_ERROR, and GLU_TESS_EDGE_FLAG. + + +Polygon CSG operations +---------------------- + + The features of the tesselator make it easy to find the union, difference, + or intersection of several polygons. + + First, assume that each polygon is defined so that the winding number + is 0 for each exterior region, and 1 for each interior region. Under + this model, CCW contours define the outer boundary of the polygon, and + CW contours define holes. Contours may be nested, but a nested + contour must be oriented oppositely from the contour that contains it. + + If the original polygons do not satisfy this description, they can be + converted to this form by first running the tesselator with the + GLU_TESS_BOUNDARY_ONLY property turned on. This returns a list of + contours satisfying the restriction above. By allocating two + tesselator objects, the callbacks from one tesselator can be fed + directly to the input of another. + + Given two or more polygons of the form above, CSG operations can be + implemented as follows: + + Union + Draw all the input contours as a single polygon. The winding number + of each resulting region is the number of original polygons + which cover it. The union can be extracted using the + GLU_TESS_WINDING_NONZERO or GLU_TESS_WINDING_POSITIVE winding rules. + Note that with the nonzero rule, we would get the same result if + all contour orientations were reversed. + + Intersection (two polygons at a time only) + Draw a single polygon using the contours from both input polygons. + Extract the result using GLU_TESS_WINDING_ABS_GEQ_TWO. (Since this + winding rule looks at the absolute value, reversing all contour + orientations does not change the result.) + + Difference + + Suppose we want to compute A \ (B union C union D). Draw a single + polygon consisting of the unmodified contours from A, followed by + the contours of B,C,D with the vertex order reversed (this changes + the winding number of the interior regions to -1). To extract the + result, use the GLU_TESS_WINDING_POSITIVE rule. + + If B,C,D are the result of a GLU_TESS_BOUNDARY_ONLY call, an + alternative to reversing the vertex order is to reverse the sign of + the supplied normal. For example in the x-y plane, call + gluTessNormal( tess, 0.0, 0.0, -1.0 ). + + +Performance +----------- + + The tesselator is not intended for immediate-mode rendering; when + possible the output should be cached in a user structure or display + list. General polygon tesselation is an inherently difficult problem, + especially given the goal of extreme robustness. + + The implementation makes an effort to output a small number of fans + and strips; this should improve the rendering performance when the + output is used in a display list. + + Single-contour input polygons are first tested to see whether they can + be rendered as a triangle fan with respect to the first vertex (to + avoid running the full decomposition algorithm on convex polygons). + Non-convex polygons may be rendered by this "fast path" as well, if + the algorithm gets lucky in its choice of a starting vertex. + + For best performance follow these guidelines: + + - supply the polygon normal, if available, using gluTessNormal(). + This represents about 10% of the computation time. For example, + if all polygons lie in the x-y plane, use gluTessNormal(tess,0,0,1). + + - render many polygons using the same tesselator object, rather than + allocating a new tesselator for each one. (In a multi-threaded, + multi-processor environment you may get better performance using + several tesselators.) + + +Comparison with the GLU tesselator +---------------------------------- + + On polygons which make it through the "fast path", the tesselator is + 3 to 5 times faster than the GLU tesselator. + + On polygons which don't make it through the fast path (but which don't + have self-intersections or degeneracies), it is about 2 times slower. + + On polygons with self-intersections or degeneraces, there is nothing + to compare against. + + The new tesselator generates many more fans and strips, reducing the + number of vertices that need to be sent to the hardware. + + Key to the statistics: + + vert number of input vertices on all contours + cntr number of input contours + tri number of triangles in all output primitives + strip number of triangle strips + fan number of triangle fans + ind number of independent triangles + ms number of milliseconds for tesselation + (on a 150MHz R4400 Indy) + + Convex polygon examples: + +New: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.0459 ms +Old: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.149 ms +New: 4 vert, 1 cntr, 2 tri, 0 strip, 1 fan, 0 ind, 0.0459 ms +Old: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.161 ms +New: 36 vert, 1 cntr, 34 tri, 0 strip, 1 fan, 0 ind, 0.153 ms +Old: 36 vert, 1 cntr, 34 tri, 0 strip, 0 fan, 34 ind, 0.621 ms + + Concave single-contour polygons: + +New: 5 vert, 1 cntr, 3 tri, 0 strip, 1 fan, 0 ind, 0.052 ms +Old: 5 vert, 1 cntr, 3 tri, 0 strip, 0 fan, 3 ind, 0.252 ms +New: 19 vert, 1 cntr, 17 tri, 2 strip, 2 fan, 1 ind, 0.911 ms +Old: 19 vert, 1 cntr, 17 tri, 0 strip, 0 fan, 17 ind, 0.529 ms +New: 151 vert, 1 cntr, 149 tri, 13 strip, 18 fan, 3 ind, 6.82 ms +Old: 151 vert, 1 cntr, 149 tri, 0 strip, 3 fan, 143 ind, 2.7 ms +New: 574 vert, 1 cntr, 572 tri, 59 strip, 54 fan, 11 ind, 26.6 ms +Old: 574 vert, 1 cntr, 572 tri, 0 strip, 31 fan, 499 ind, 12.4 ms + + Multiple contours, but no intersections: + +New: 7 vert, 2 cntr, 7 tri, 1 strip, 0 fan, 0 ind, 0.527 ms +Old: 7 vert, 2 cntr, 7 tri, 0 strip, 0 fan, 7 ind, 0.274 ms +New: 81 vert, 6 cntr, 89 tri, 9 strip, 7 fan, 6 ind, 3.88 ms +Old: 81 vert, 6 cntr, 89 tri, 0 strip, 13 fan, 61 ind, 2.2 ms +New: 391 vert, 19 cntr, 413 tri, 37 strip, 32 fan, 26 ind, 20.2 ms +Old: 391 vert, 19 cntr, 413 tri, 0 strip, 25 fan, 363 ind, 8.68 ms + + Self-intersecting and degenerate examples: + +Bowtie: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.483 ms +Star: 5 vert, 1 cntr, 5 tri, 0 strip, 0 fan, 5 ind, 0.91 ms +Random: 24 vert, 7 cntr, 46 tri, 2 strip, 12 fan, 7 ind, 5.32 ms +Font: 333 vert, 2 cntr, 331 tri, 32 strip, 16 fan, 3 ind, 14.1 ms +: 167 vert, 35 cntr, 254 tri, 8 strip, 56 fan, 52 ind, 46.3 ms +: 78 vert, 1 cntr, 2675 tri, 148 strip, 207 fan, 180 ind, 243 ms +: 12480 vert, 2 cntr, 12478 tri, 736 strip,1275 fan, 5 ind, 1010 ms diff --git a/deps/libtess/dict.c b/deps/libtess/dict.c new file mode 100644 index 0000000000..e11404ccc7 --- /dev/null +++ b/deps/libtess/dict.c @@ -0,0 +1,118 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2006/04/19 14:42:01 $ $Revision: 1.3 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/dict.c,v 1.3 2006/04/19 14:42:01 brianp Exp $ +*/ + +#include +#include "dictList.h" +#include "memalloc.h" + +/* really __gl_dictListNewDict */ +Dict *dictNewDict( void *frame, + int (*leq)(void *frame, DictKey key1, DictKey key2) ) +{ + Dict *dict = (Dict *) memAlloc( sizeof( Dict )); + DictNode *head; + + if (dict == NULL) return NULL; + + head = &dict->head; + + head->key = NULL; + head->next = head; + head->prev = head; + + dict->frame = frame; + dict->leq = leq; + + return dict; +} + +/* really __gl_dictListDeleteDict */ +void dictDeleteDict( Dict *dict ) +{ + DictNode *node, *next; + + for( node = dict->head.next; node != &dict->head; node = next ) { + next = node->next; + memFree( node ); + } + memFree( dict ); +} + +/* really __gl_dictListInsertBefore */ +DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key ) +{ + DictNode *newNode; + + do { + node = node->prev; + } while( node->key != NULL && ! (*dict->leq)(dict->frame, node->key, key)); + + newNode = (DictNode *) memAlloc( sizeof( DictNode )); + if (newNode == NULL) return NULL; + + newNode->key = key; + newNode->next = node->next; + node->next->prev = newNode; + newNode->prev = node; + node->next = newNode; + + return newNode; +} + +/* really __gl_dictListDelete */ +void dictDelete( Dict *dict, DictNode *node ) /*ARGSUSED*/ +{ + node->next->prev = node->prev; + node->prev->next = node->next; + memFree( node ); +} + +/* really __gl_dictListSearch */ +DictNode *dictSearch( Dict *dict, DictKey key ) +{ + DictNode *node = &dict->head; + + do { + node = node->next; + } while( node->key != NULL && ! (*dict->leq)(dict->frame, key, node->key)); + + return node; +} diff --git a/deps/libtess/dict.h b/deps/libtess/dict.h new file mode 100644 index 0000000000..ea3b4064ff --- /dev/null +++ b/deps/libtess/dict.h @@ -0,0 +1,107 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/dict.h,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#ifndef __dict_list_h_ +#define __dict_list_h_ + +/* Use #define's so that another heap implementation can use this one */ + +#define DictKey DictListKey +#define Dict DictList +#define DictNode DictListNode + +#define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq) +#define dictDeleteDict(dict) __gl_dictListDeleteDict(dict) + +#define dictSearch(dict,key) __gl_dictListSearch(dict,key) +#define dictInsert(dict,key) __gl_dictListInsert(dict,key) +#define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key) +#define dictDelete(dict,node) __gl_dictListDelete(dict,node) + +#define dictKey(n) __gl_dictListKey(n) +#define dictSucc(n) __gl_dictListSucc(n) +#define dictPred(n) __gl_dictListPred(n) +#define dictMin(d) __gl_dictListMin(d) +#define dictMax(d) __gl_dictListMax(d) + + + +typedef void *DictKey; +typedef struct Dict Dict; +typedef struct DictNode DictNode; + +Dict *dictNewDict( + void *frame, + int (*leq)(void *frame, DictKey key1, DictKey key2) ); + +void dictDeleteDict( Dict *dict ); + +/* Search returns the node with the smallest key greater than or equal + * to the given key. If there is no such key, returns a node whose + * key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc. + */ +DictNode *dictSearch( Dict *dict, DictKey key ); +DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key ); +void dictDelete( Dict *dict, DictNode *node ); + +#define __gl_dictListKey(n) ((n)->key) +#define __gl_dictListSucc(n) ((n)->next) +#define __gl_dictListPred(n) ((n)->prev) +#define __gl_dictListMin(d) ((d)->head.next) +#define __gl_dictListMax(d) ((d)->head.prev) +#define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k))) + + +/*** Private data structures ***/ + +struct DictNode { + DictKey key; + DictNode *next; + DictNode *prev; +}; + +struct Dict { + DictNode head; + void *frame; + int (*leq)(void *frame, DictKey key1, DictKey key2); +}; + +#endif diff --git a/deps/libtess/dictList.h b/deps/libtess/dictList.h new file mode 100644 index 0000000000..f5b82116d8 --- /dev/null +++ b/deps/libtess/dictList.h @@ -0,0 +1,107 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/dict-list.h,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#ifndef __dict_list_h_ +#define __dict_list_h_ + +/* Use #define's so that another heap implementation can use this one */ + +#define DictKey DictListKey +#define Dict DictList +#define DictNode DictListNode + +#define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq) +#define dictDeleteDict(dict) __gl_dictListDeleteDict(dict) + +#define dictSearch(dict,key) __gl_dictListSearch(dict,key) +#define dictInsert(dict,key) __gl_dictListInsert(dict,key) +#define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key) +#define dictDelete(dict,node) __gl_dictListDelete(dict,node) + +#define dictKey(n) __gl_dictListKey(n) +#define dictSucc(n) __gl_dictListSucc(n) +#define dictPred(n) __gl_dictListPred(n) +#define dictMin(d) __gl_dictListMin(d) +#define dictMax(d) __gl_dictListMax(d) + + + +typedef void *DictKey; +typedef struct Dict Dict; +typedef struct DictNode DictNode; + +Dict *dictNewDict( + void *frame, + int (*leq)(void *frame, DictKey key1, DictKey key2) ); + +void dictDeleteDict( Dict *dict ); + +/* Search returns the node with the smallest key greater than or equal + * to the given key. If there is no such key, returns a node whose + * key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc. + */ +DictNode *dictSearch( Dict *dict, DictKey key ); +DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key ); +void dictDelete( Dict *dict, DictNode *node ); + +#define __gl_dictListKey(n) ((n)->key) +#define __gl_dictListSucc(n) ((n)->next) +#define __gl_dictListPred(n) ((n)->prev) +#define __gl_dictListMin(d) ((d)->head.next) +#define __gl_dictListMax(d) ((d)->head.prev) +#define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k))) + + +/*** Private data structures ***/ + +struct DictNode { + DictKey key; + DictNode *next; + DictNode *prev; +}; + +struct Dict { + DictNode head; + void *frame; + int (*leq)(void *frame, DictKey key1, DictKey key2); +}; + +#endif diff --git a/deps/libtess/geom.c b/deps/libtess/geom.c new file mode 100644 index 0000000000..d009e143ad --- /dev/null +++ b/deps/libtess/geom.c @@ -0,0 +1,271 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/geom.c,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#include "gluos.h" +#include +#include "mesh.h" +#include "geom.h" + +int __gl_vertLeq( GLUvertex *u, GLUvertex *v ) +{ + /* Returns TRUE if u is lexicographically <= v. */ + + return VertLeq( u, v ); +} + +GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w), + * evaluates the t-coord of the edge uw at the s-coord of the vertex v. + * Returns v->t - (uw)(v->s), ie. the signed distance from uw to v. + * If uw is vertical (and thus passes thru v), the result is zero. + * + * The calculation is extremely accurate and stable, even when v + * is very close to u or w. In particular if we set v->t = 0 and + * let r be the negated result (this evaluates (uw)(v->s)), then + * r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t). + */ + GLdouble gapL, gapR; + + assert( VertLeq( u, v ) && VertLeq( v, w )); + + gapL = v->s - u->s; + gapR = w->s - v->s; + + if( gapL + gapR > 0 ) { + if( gapL < gapR ) { + return (v->t - u->t) + (u->t - w->t) * (gapL / (gapL + gapR)); + } else { + return (v->t - w->t) + (w->t - u->t) * (gapR / (gapL + gapR)); + } + } + /* vertical line */ + return 0; +} + +GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* Returns a number whose sign matches EdgeEval(u,v,w) but which + * is cheaper to evaluate. Returns > 0, == 0 , or < 0 + * as v is above, on, or below the edge uw. + */ + GLdouble gapL, gapR; + + assert( VertLeq( u, v ) && VertLeq( v, w )); + + gapL = v->s - u->s; + gapR = w->s - v->s; + + if( gapL + gapR > 0 ) { + return (v->t - w->t) * gapL + (v->t - u->t) * gapR; + } + /* vertical line */ + return 0; +} + + +/*********************************************************************** + * Define versions of EdgeSign, EdgeEval with s and t transposed. + */ + +GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w), + * evaluates the t-coord of the edge uw at the s-coord of the vertex v. + * Returns v->s - (uw)(v->t), ie. the signed distance from uw to v. + * If uw is vertical (and thus passes thru v), the result is zero. + * + * The calculation is extremely accurate and stable, even when v + * is very close to u or w. In particular if we set v->s = 0 and + * let r be the negated result (this evaluates (uw)(v->t)), then + * r is guaranteed to satisfy MIN(u->s,w->s) <= r <= MAX(u->s,w->s). + */ + GLdouble gapL, gapR; + + assert( TransLeq( u, v ) && TransLeq( v, w )); + + gapL = v->t - u->t; + gapR = w->t - v->t; + + if( gapL + gapR > 0 ) { + if( gapL < gapR ) { + return (v->s - u->s) + (u->s - w->s) * (gapL / (gapL + gapR)); + } else { + return (v->s - w->s) + (w->s - u->s) * (gapR / (gapL + gapR)); + } + } + /* vertical line */ + return 0; +} + +GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* Returns a number whose sign matches TransEval(u,v,w) but which + * is cheaper to evaluate. Returns > 0, == 0 , or < 0 + * as v is above, on, or below the edge uw. + */ + GLdouble gapL, gapR; + + assert( TransLeq( u, v ) && TransLeq( v, w )); + + gapL = v->t - u->t; + gapR = w->t - v->t; + + if( gapL + gapR > 0 ) { + return (v->s - w->s) * gapL + (v->s - u->s) * gapR; + } + /* vertical line */ + return 0; +} + + +int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* For almost-degenerate situations, the results are not reliable. + * Unless the floating-point arithmetic can be performed without + * rounding errors, *any* implementation will give incorrect results + * on some degenerate inputs, so the client must have some way to + * handle this situation. + */ + return (u->s*(v->t - w->t) + v->s*(w->t - u->t) + w->s*(u->t - v->t)) >= 0; +} + +/* Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b), + * or (x+y)/2 if a==b==0. It requires that a,b >= 0, and enforces + * this in the rare case that one argument is slightly negative. + * The implementation is extremely stable numerically. + * In particular it guarantees that the result r satisfies + * MIN(x,y) <= r <= MAX(x,y), and the results are very accurate + * even when a and b differ greatly in magnitude. + */ +#define RealInterpolate(a,x,b,y) \ + (a = (a < 0) ? 0 : a, b = (b < 0) ? 0 : b, \ + ((a <= b) ? ((b == 0) ? ((x+y) / 2) \ + : (x + (y-x) * (a/(a+b)))) \ + : (y + (x-y) * (b/(a+b))))) + +#ifndef FOR_TRITE_TEST_PROGRAM +#define Interpolate(a,x,b,y) RealInterpolate(a,x,b,y) +#else + +/* Claim: the ONLY property the sweep algorithm relies on is that + * MIN(x,y) <= r <= MAX(x,y). This is a nasty way to test that. + */ +#include +extern int RandomInterpolate; + +GLdouble Interpolate( GLdouble a, GLdouble x, GLdouble b, GLdouble y) +{ +printf("*********************%d\n",RandomInterpolate); + if( RandomInterpolate ) { + a = 1.2 * drand48() - 0.1; + a = (a < 0) ? 0 : ((a > 1) ? 1 : a); + b = 1.0 - a; + } + return RealInterpolate(a,x,b,y); +} + +#endif + +#define Swap(a,b) if (1) { GLUvertex *t = a; a = b; b = t; } else + +void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1, + GLUvertex *o2, GLUvertex *d2, + GLUvertex *v ) +/* Given edges (o1,d1) and (o2,d2), compute their point of intersection. + * The computed point is guaranteed to lie in the intersection of the + * bounding rectangles defined by each edge. + */ +{ + GLdouble z1, z2; + + /* This is certainly not the most efficient way to find the intersection + * of two line segments, but it is very numerically stable. + * + * Strategy: find the two middle vertices in the VertLeq ordering, + * and interpolate the intersection s-value from these. Then repeat + * using the TransLeq ordering to find the intersection t-value. + */ + + if( ! VertLeq( o1, d1 )) { Swap( o1, d1 ); } + if( ! VertLeq( o2, d2 )) { Swap( o2, d2 ); } + if( ! VertLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); } + + if( ! VertLeq( o2, d1 )) { + /* Technically, no intersection -- do our best */ + v->s = (o2->s + d1->s) / 2; + } else if( VertLeq( d1, d2 )) { + /* Interpolate between o2 and d1 */ + z1 = EdgeEval( o1, o2, d1 ); + z2 = EdgeEval( o2, d1, d2 ); + if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } + v->s = Interpolate( z1, o2->s, z2, d1->s ); + } else { + /* Interpolate between o2 and d2 */ + z1 = EdgeSign( o1, o2, d1 ); + z2 = -EdgeSign( o1, d2, d1 ); + if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } + v->s = Interpolate( z1, o2->s, z2, d2->s ); + } + + /* Now repeat the process for t */ + + if( ! TransLeq( o1, d1 )) { Swap( o1, d1 ); } + if( ! TransLeq( o2, d2 )) { Swap( o2, d2 ); } + if( ! TransLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); } + + if( ! TransLeq( o2, d1 )) { + /* Technically, no intersection -- do our best */ + v->t = (o2->t + d1->t) / 2; + } else if( TransLeq( d1, d2 )) { + /* Interpolate between o2 and d1 */ + z1 = TransEval( o1, o2, d1 ); + z2 = TransEval( o2, d1, d2 ); + if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } + v->t = Interpolate( z1, o2->t, z2, d1->t ); + } else { + /* Interpolate between o2 and d2 */ + z1 = TransSign( o1, o2, d1 ); + z2 = -TransSign( o1, d2, d1 ); + if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } + v->t = Interpolate( z1, o2->t, z2, d2->t ); + } +} diff --git a/deps/libtess/geom.h b/deps/libtess/geom.h new file mode 100644 index 0000000000..7e1e86528a --- /dev/null +++ b/deps/libtess/geom.h @@ -0,0 +1,89 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __geom_h_ +#define __geom_h_ + +#include "mesh.h" + +#ifdef NO_BRANCH_CONDITIONS +/* MIPS architecture has special instructions to evaluate boolean + * conditions -- more efficient than branching, IF you can get the + * compiler to generate the right instructions (SGI compiler doesn't) + */ +#define VertEq(u,v) (((u)->s == (v)->s) & ((u)->t == (v)->t)) +#define VertLeq(u,v) (((u)->s < (v)->s) | \ + ((u)->s == (v)->s & (u)->t <= (v)->t)) +#else +#define VertEq(u,v) ((u)->s == (v)->s && (u)->t == (v)->t) +#define VertLeq(u,v) (((u)->s < (v)->s) || \ + ((u)->s == (v)->s && (u)->t <= (v)->t)) +#endif + +#define EdgeEval(u,v,w) __gl_edgeEval(u,v,w) +#define EdgeSign(u,v,w) __gl_edgeSign(u,v,w) + +/* Versions of VertLeq, EdgeSign, EdgeEval with s and t transposed. */ + +#define TransLeq(u,v) (((u)->t < (v)->t) || \ + ((u)->t == (v)->t && (u)->s <= (v)->s)) +#define TransEval(u,v,w) __gl_transEval(u,v,w) +#define TransSign(u,v,w) __gl_transSign(u,v,w) + + +#define EdgeGoesLeft(e) VertLeq( (e)->Dst, (e)->Org ) +#define EdgeGoesRight(e) VertLeq( (e)->Org, (e)->Dst ) + +#undef ABS +#define ABS(x) ((x) < 0 ? -(x) : (x)) +#define VertL1dist(u,v) (ABS(u->s - v->s) + ABS(u->t - v->t)) + +#define VertCCW(u,v,w) __gl_vertCCW(u,v,w) + +int __gl_vertLeq( GLUvertex *u, GLUvertex *v ); +GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1, + GLUvertex *o2, GLUvertex *d2, + GLUvertex *v ); + +#endif diff --git a/deps/libtess/gluos.h b/deps/libtess/gluos.h new file mode 100644 index 0000000000..1d6f5c46f8 --- /dev/null +++ b/deps/libtess/gluos.h @@ -0,0 +1,76 @@ +/* +** gluos.h - operating system dependencies for GLU +** +*/ +#ifdef __VMS +#ifdef __cplusplus +#pragma message disable nocordel +#pragma message disable codeunreachable +#pragma message disable codcauunr +#endif +#endif + +#ifdef __WATCOMC__ +/* Disable *lots* of warnings to get a clean build. I can't be bothered fixing the + * code at the moment, as it is pretty ugly. + */ +#pragma warning 7 10 +#pragma warning 13 10 +#pragma warning 14 10 +#pragma warning 367 10 +#pragma warning 379 10 +#pragma warning 726 10 +#pragma warning 836 10 +#endif + +#ifdef BUILD_FOR_SNAP + +#include +#include +#include + +#elif defined(_WIN32) + +#include /* For _MAX_PATH definition */ +#include +#include + +#define WIN32_LEAN_AND_MEAN +#define NOGDI +#define NOIME +#define NOMINMAX + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 +#endif +#ifndef STRICT + #define STRICT 1 +#endif + +#include + +/* Disable warnings */ +#if defined(_MSC_VER) +#pragma warning(disable : 4101) +#pragma warning(disable : 4244) +#pragma warning(disable : 4761) +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1300 +#pragma comment(linker, "/OPT:NOWIN98") +#endif + +#elif defined(__OS2__) + +#include +#include +#include +#define WINGDIAPI + +#else + +/* Disable Microsoft-specific keywords */ +#define GLAPIENTRY +#define WINGDIAPI + +#endif diff --git a/deps/libtess/glutess.h b/deps/libtess/glutess.h new file mode 100644 index 0000000000..d5616b2096 --- /dev/null +++ b/deps/libtess/glutess.h @@ -0,0 +1,207 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. 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 including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * 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 + * SILICON GRAPHICS, INC. 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. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ + +#ifndef __glutess_h__ +#define __glutess_h__ + +#define GL_LINE_LOOP 0x0002 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 + +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef void GLvoid; +typedef signed char GLbyte; /* 1-byte signed */ +typedef short GLshort; /* 2-byte signed */ +typedef int GLint; /* 4-byte signed */ +typedef unsigned char GLubyte; /* 1-byte unsigned */ +typedef unsigned short GLushort; /* 2-byte unsigned */ +typedef unsigned int GLuint; /* 4-byte unsigned */ +typedef int GLsizei; /* 4-byte signed */ +typedef float GLfloat; /* single precision float */ +typedef float GLclampf; /* single precision float in [0,1] */ +typedef double GLdouble; /* double precision float */ +typedef double GLclampd; /* double precision float in [0,1] */ + +/* Boolean values */ +#define GL_FALSE 0x0 +#define GL_TRUE 0x1 + +#ifndef GLAPIENTRY +#if defined(_MSC_VER) || defined(__MINGW32__) +#define GLAPIENTRY __stdcall +#else +#define GLAPIENTRY +#endif +#endif + +#ifndef GLAPIENTRYP +#define GLAPIENTRYP GLAPIENTRY * +#endif + +#if defined(__WIN32__) +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#if (defined(_MSC_VER) || defined(__MINGW32__)) && defined(BUILD_GLU32) +# undef GLAPI +# define GLAPI __declspec(dllexport) +#elif (defined(_MSC_VER) || defined(__MINGW32__)) && defined(_DLL) +/* tag specifying we're building for DLL runtime support */ +# undef GLAPI +# define GLAPI __declspec(dllimport) +#elif !defined(GLAPI) +/* for use with static link lib build of Win32 edition only */ +# define GLAPI extern +#endif /* _STATIC_MESA support */ + +// do not import/export anything -- we'll use it internally +#undef GLAPI +#define GLAPI + +#ifdef __cplusplus +extern "C" { +#endif + +/*************************************************************/ + +/* Boolean */ +#define GLU_FALSE 0 +#define GLU_TRUE 1 + +/* Version */ +#define GLU_VERSION_1_1 1 +#define GLU_VERSION_1_2 1 +#define GLU_VERSION_1_3 1 + +/* StringName */ +#define GLU_VERSION 100800 +#define GLU_EXTENSIONS 100801 + +/* ErrorCode */ +#define GLU_INVALID_ENUM 100900 +#define GLU_INVALID_VALUE 100901 +#define GLU_OUT_OF_MEMORY 100902 +#define GLU_INCOMPATIBLE_GL_VERSION 100903 +#define GLU_INVALID_OPERATION 100904 + +/* TessCallback */ +#define GLU_TESS_BEGIN 100100 +#define GLU_BEGIN 100100 +#define GLU_TESS_VERTEX 100101 +#define GLU_VERTEX 100101 +#define GLU_TESS_END 100102 +#define GLU_END 100102 +#define GLU_TESS_ERROR 100103 +#define GLU_TESS_EDGE_FLAG 100104 +#define GLU_EDGE_FLAG 100104 +#define GLU_TESS_COMBINE 100105 +#define GLU_TESS_BEGIN_DATA 100106 +#define GLU_TESS_VERTEX_DATA 100107 +#define GLU_TESS_END_DATA 100108 +#define GLU_TESS_ERROR_DATA 100109 +#define GLU_TESS_EDGE_FLAG_DATA 100110 +#define GLU_TESS_COMBINE_DATA 100111 + +/* TessContour */ +#define GLU_CW 100120 +#define GLU_CCW 100121 +#define GLU_INTERIOR 100122 +#define GLU_EXTERIOR 100123 +#define GLU_UNKNOWN 100124 + +/* TessProperty */ +#define GLU_TESS_WINDING_RULE 100140 +#define GLU_TESS_BOUNDARY_ONLY 100141 +#define GLU_TESS_TOLERANCE 100142 + +/* TessError */ +#define GLU_TESS_ERROR1 100151 +#define GLU_TESS_ERROR2 100152 +#define GLU_TESS_ERROR3 100153 +#define GLU_TESS_ERROR4 100154 +#define GLU_TESS_ERROR5 100155 +#define GLU_TESS_ERROR6 100156 +#define GLU_TESS_ERROR7 100157 +#define GLU_TESS_ERROR8 100158 +#define GLU_TESS_MISSING_BEGIN_POLYGON 100151 +#define GLU_TESS_MISSING_BEGIN_CONTOUR 100152 +#define GLU_TESS_MISSING_END_POLYGON 100153 +#define GLU_TESS_MISSING_END_CONTOUR 100154 +#define GLU_TESS_COORD_TOO_LARGE 100155 +#define GLU_TESS_NEED_COMBINE_CALLBACK 100156 + +/* TessWinding */ +#define GLU_TESS_WINDING_ODD 100130 +#define GLU_TESS_WINDING_NONZERO 100131 +#define GLU_TESS_WINDING_POSITIVE 100132 +#define GLU_TESS_WINDING_NEGATIVE 100133 +#define GLU_TESS_WINDING_ABS_GEQ_TWO 100134 + +/*************************************************************/ + + +#ifdef __cplusplus +class GLUtesselator; +#else +typedef struct GLUtesselator GLUtesselator; +#endif + +typedef GLUtesselator GLUtesselatorObj; +typedef GLUtesselator GLUtriangulatorObj; + +#define GLU_TESS_MAX_COORD 1.0e150 + +/* Internal convenience typedefs */ +typedef void (GLAPIENTRYP _GLUfuncptr)(); + +GLAPI void GLAPIENTRY gluBeginPolygon (GLUtesselator* tess); +GLAPI void GLAPIENTRY gluDeleteTess (GLUtesselator* tess); +GLAPI void GLAPIENTRY gluEndPolygon (GLUtesselator* tess); +GLAPI void GLAPIENTRY gluGetTessProperty (GLUtesselator* tess, GLenum which, GLdouble* data); +GLAPI GLUtesselator* GLAPIENTRY gluNewTess (void); +GLAPI void GLAPIENTRY gluNextContour (GLUtesselator* tess, GLenum type); +GLAPI void GLAPIENTRY gluTessBeginContour (GLUtesselator* tess); +GLAPI void GLAPIENTRY gluTessBeginPolygon (GLUtesselator* tess, GLvoid* data); +GLAPI void GLAPIENTRY gluTessCallback (GLUtesselator* tess, GLenum which, _GLUfuncptr CallBackFunc); +GLAPI void GLAPIENTRY gluTessEndContour (GLUtesselator* tess); +GLAPI void GLAPIENTRY gluTessEndPolygon (GLUtesselator* tess); +GLAPI void GLAPIENTRY gluTessNormal (GLUtesselator* tess, GLdouble valueX, GLdouble valueY, GLdouble valueZ); +GLAPI void GLAPIENTRY gluTessProperty (GLUtesselator* tess, GLenum which, GLdouble data); +GLAPI void GLAPIENTRY gluTessVertex (GLUtesselator* tess, GLdouble *location, GLvoid* data); + +#ifdef __cplusplus +} +#endif + +#endif /* __glutess_h__ */ diff --git a/deps/libtess/memalloc.h b/deps/libtess/memalloc.h new file mode 100644 index 0000000000..ec58559ad3 --- /dev/null +++ b/deps/libtess/memalloc.h @@ -0,0 +1,56 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2003/07/24 22:41:17 $ $Revision: 1.4 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/memalloc.h,v 1.4 2003/07/24 22:41:17 brianp Exp $ +*/ + +#ifndef __memalloc_simple_h_ +#define __memalloc_simple_h_ + +#include + +void * eC_malloc(unsigned int size); +void * eC_realloc(void * pointer, unsigned int size); +void eC_free(void * pointer); + +#define memRealloc eC_realloc +#define memFree eC_free +#define memInit +#define memAlloc eC_malloc + +#endif diff --git a/deps/libtess/mesh.c b/deps/libtess/mesh.c new file mode 100644 index 0000000000..045954db91 --- /dev/null +++ b/deps/libtess/mesh.c @@ -0,0 +1,796 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/mesh.c,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#include "gluos.h" +#include +#include +#include "mesh.h" +#include "memalloc.h" + +#define TRUE 1 +#define FALSE 0 + +static GLUvertex *allocVertex() +{ + return (GLUvertex *)memAlloc( sizeof( GLUvertex )); +} + +static GLUface *allocFace() +{ + return (GLUface *)memAlloc( sizeof( GLUface )); +} + +/************************ Utility Routines ************************/ + +/* Allocate and free half-edges in pairs for efficiency. + * The *only* place that should use this fact is allocation/free. + */ +typedef struct { GLUhalfEdge e, eSym; } EdgePair; + +/* MakeEdge creates a new pair of half-edges which form their own loop. + * No vertex or face structures are allocated, but these must be assigned + * before the current edge operation is completed. + */ +static GLUhalfEdge *MakeEdge( GLUhalfEdge *eNext ) +{ + GLUhalfEdge *e; + GLUhalfEdge *eSym; + GLUhalfEdge *ePrev; + EdgePair *pair = (EdgePair *)memAlloc( sizeof( EdgePair )); + if (pair == NULL) return NULL; + + e = &pair->e; + eSym = &pair->eSym; + + /* Make sure eNext points to the first edge of the edge pair */ + if( eNext->Sym < eNext ) { eNext = eNext->Sym; } + + /* Insert in circular doubly-linked list before eNext. + * Note that the prev pointer is stored in Sym->next. + */ + ePrev = eNext->Sym->next; + eSym->next = ePrev; + ePrev->Sym->next = e; + e->next = eNext; + eNext->Sym->next = eSym; + + e->Sym = eSym; + e->Onext = e; + e->Lnext = eSym; + e->Org = NULL; + e->Lface = NULL; + e->winding = 0; + e->activeRegion = NULL; + + eSym->Sym = e; + eSym->Onext = eSym; + eSym->Lnext = e; + eSym->Org = NULL; + eSym->Lface = NULL; + eSym->winding = 0; + eSym->activeRegion = NULL; + + return e; +} + +/* Splice( a, b ) is best described by the Guibas/Stolfi paper or the + * CS348a notes (see mesh.h). Basically it modifies the mesh so that + * a->Onext and b->Onext are exchanged. This can have various effects + * depending on whether a and b belong to different face or vertex rings. + * For more explanation see __gl_meshSplice() below. + */ +static void Splice( GLUhalfEdge *a, GLUhalfEdge *b ) +{ + GLUhalfEdge *aOnext = a->Onext; + GLUhalfEdge *bOnext = b->Onext; + + aOnext->Sym->Lnext = b; + bOnext->Sym->Lnext = a; + a->Onext = bOnext; + b->Onext = aOnext; +} + +/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the + * origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives + * a place to insert the new vertex in the global vertex list. We insert + * the new vertex *before* vNext so that algorithms which walk the vertex + * list will not see the newly created vertices. + */ +static void MakeVertex( GLUvertex *newVertex, + GLUhalfEdge *eOrig, GLUvertex *vNext ) +{ + GLUhalfEdge *e; + GLUvertex *vPrev; + GLUvertex *vNew = newVertex; + + assert(vNew != NULL); + + /* insert in circular doubly-linked list before vNext */ + vPrev = vNext->prev; + vNew->prev = vPrev; + vPrev->next = vNew; + vNew->next = vNext; + vNext->prev = vNew; + + vNew->anEdge = eOrig; + vNew->data = NULL; + /* leave coords, s, t undefined */ + + /* fix other edges on this vertex loop */ + e = eOrig; + do { + e->Org = vNew; + e = e->Onext; + } while( e != eOrig ); +} + +/* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left + * face of all edges in the face loop to which eOrig belongs. "fNext" gives + * a place to insert the new face in the global face list. We insert + * the new face *before* fNext so that algorithms which walk the face + * list will not see the newly created faces. + */ +static void MakeFace( GLUface *newFace, GLUhalfEdge *eOrig, GLUface *fNext ) +{ + GLUhalfEdge *e; + GLUface *fPrev; + GLUface *fNew = newFace; + + assert(fNew != NULL); + + /* insert in circular doubly-linked list before fNext */ + fPrev = fNext->prev; + fNew->prev = fPrev; + fPrev->next = fNew; + fNew->next = fNext; + fNext->prev = fNew; + + fNew->anEdge = eOrig; + fNew->data = NULL; + fNew->trail = NULL; + fNew->marked = FALSE; + + /* The new face is marked "inside" if the old one was. This is a + * convenience for the common case where a face has been split in two. + */ + fNew->inside = fNext->inside; + + /* fix other edges on this face loop */ + e = eOrig; + do { + e->Lface = fNew; + e = e->Lnext; + } while( e != eOrig ); +} + +/* KillEdge( eDel ) destroys an edge (the half-edges eDel and eDel->Sym), + * and removes from the global edge list. + */ +static void KillEdge( GLUhalfEdge *eDel ) +{ + GLUhalfEdge *ePrev, *eNext; + + /* Half-edges are allocated in pairs, see EdgePair above */ + if( eDel->Sym < eDel ) { eDel = eDel->Sym; } + + /* delete from circular doubly-linked list */ + eNext = eDel->next; + ePrev = eDel->Sym->next; + eNext->Sym->next = ePrev; + ePrev->Sym->next = eNext; + + memFree( eDel ); +} + + +/* KillVertex( vDel ) destroys a vertex and removes it from the global + * vertex list. It updates the vertex loop to point to a given new vertex. + */ +static void KillVertex( GLUvertex *vDel, GLUvertex *newOrg ) +{ + GLUhalfEdge *e, *eStart = vDel->anEdge; + GLUvertex *vPrev, *vNext; + + /* change the origin of all affected edges */ + e = eStart; + do { + e->Org = newOrg; + e = e->Onext; + } while( e != eStart ); + + /* delete from circular doubly-linked list */ + vPrev = vDel->prev; + vNext = vDel->next; + vNext->prev = vPrev; + vPrev->next = vNext; + + memFree( vDel ); +} + +/* KillFace( fDel ) destroys a face and removes it from the global face + * list. It updates the face loop to point to a given new face. + */ +static void KillFace( GLUface *fDel, GLUface *newLface ) +{ + GLUhalfEdge *e, *eStart = fDel->anEdge; + GLUface *fPrev, *fNext; + + /* change the left face of all affected edges */ + e = eStart; + do { + e->Lface = newLface; + e = e->Lnext; + } while( e != eStart ); + + /* delete from circular doubly-linked list */ + fPrev = fDel->prev; + fNext = fDel->next; + fNext->prev = fPrev; + fPrev->next = fNext; + + memFree( fDel ); +} + + +/****************** Basic Edge Operations **********************/ + +/* __gl_meshMakeEdge creates one edge, two vertices, and a loop (face). + * The loop consists of the two new half-edges. + */ +GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh ) +{ + GLUvertex *newVertex1= allocVertex(); + GLUvertex *newVertex2= allocVertex(); + GLUface *newFace= allocFace(); + GLUhalfEdge *e; + + /* if any one is null then all get freed */ + if (newVertex1 == NULL || newVertex2 == NULL || newFace == NULL) { + if (newVertex1 != NULL) memFree(newVertex1); + if (newVertex2 != NULL) memFree(newVertex2); + if (newFace != NULL) memFree(newFace); + return NULL; + } + + e = MakeEdge( &mesh->eHead ); + if (e == NULL) return NULL; + + MakeVertex( newVertex1, e, &mesh->vHead ); + MakeVertex( newVertex2, e->Sym, &mesh->vHead ); + MakeFace( newFace, e, &mesh->fHead ); + return e; +} + + +/* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the + * mesh connectivity and topology. It changes the mesh so that + * eOrg->Onext <- OLD( eDst->Onext ) + * eDst->Onext <- OLD( eOrg->Onext ) + * where OLD(...) means the value before the meshSplice operation. + * + * This can have two effects on the vertex structure: + * - if eOrg->Org != eDst->Org, the two vertices are merged together + * - if eOrg->Org == eDst->Org, the origin is split into two vertices + * In both cases, eDst->Org is changed and eOrg->Org is untouched. + * + * Similarly (and independently) for the face structure, + * - if eOrg->Lface == eDst->Lface, one loop is split into two + * - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one + * In both cases, eDst->Lface is changed and eOrg->Lface is unaffected. + * + * Some special cases: + * If eDst == eOrg, the operation has no effect. + * If eDst == eOrg->Lnext, the new face will have a single edge. + * If eDst == eOrg->Lprev, the old face will have a single edge. + * If eDst == eOrg->Onext, the new vertex will have a single edge. + * If eDst == eOrg->Oprev, the old vertex will have a single edge. + */ +int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ) +{ + int joiningLoops = FALSE; + int joiningVertices = FALSE; + + if( eOrg == eDst ) return 1; + + if( eDst->Org != eOrg->Org ) { + /* We are merging two disjoint vertices -- destroy eDst->Org */ + joiningVertices = TRUE; + KillVertex( eDst->Org, eOrg->Org ); + } + if( eDst->Lface != eOrg->Lface ) { + /* We are connecting two disjoint loops -- destroy eDst->Lface */ + joiningLoops = TRUE; + KillFace( eDst->Lface, eOrg->Lface ); + } + + /* Change the edge structure */ + Splice( eDst, eOrg ); + + if( ! joiningVertices ) { + GLUvertex *newVertex= allocVertex(); + if (newVertex == NULL) return 0; + + /* We split one vertex into two -- the new vertex is eDst->Org. + * Make sure the old vertex points to a valid half-edge. + */ + MakeVertex( newVertex, eDst, eOrg->Org ); + eOrg->Org->anEdge = eOrg; + } + if( ! joiningLoops ) { + GLUface *newFace= allocFace(); + if (newFace == NULL) return 0; + + /* We split one loop into two -- the new loop is eDst->Lface. + * Make sure the old face points to a valid half-edge. + */ + MakeFace( newFace, eDst, eOrg->Lface ); + eOrg->Lface->anEdge = eOrg; + } + + return 1; +} + + +/* __gl_meshDelete( eDel ) removes the edge eDel. There are several cases: + * if (eDel->Lface != eDel->Rface), we join two loops into one; the loop + * eDel->Lface is deleted. Otherwise, we are splitting one loop into two; + * the newly created loop will contain eDel->Dst. If the deletion of eDel + * would create isolated vertices, those are deleted as well. + * + * This function could be implemented as two calls to __gl_meshSplice + * plus a few calls to memFree, but this would allocate and delete + * unnecessary vertices and faces. + */ +int __gl_meshDelete( GLUhalfEdge *eDel ) +{ + GLUhalfEdge *eDelSym = eDel->Sym; + int joiningLoops = FALSE; + + /* First step: disconnect the origin vertex eDel->Org. We make all + * changes to get a consistent mesh in this "intermediate" state. + */ + if( eDel->Lface != eDel->Rface ) { + /* We are joining two loops into one -- remove the left face */ + joiningLoops = TRUE; + KillFace( eDel->Lface, eDel->Rface ); + } + + if( eDel->Onext == eDel ) { + KillVertex( eDel->Org, NULL ); + } else { + /* Make sure that eDel->Org and eDel->Rface point to valid half-edges */ + eDel->Rface->anEdge = eDel->Oprev; + eDel->Org->anEdge = eDel->Onext; + + Splice( eDel, eDel->Oprev ); + if( ! joiningLoops ) { + GLUface *newFace= allocFace(); + if (newFace == NULL) return 0; + + /* We are splitting one loop into two -- create a new loop for eDel. */ + MakeFace( newFace, eDel, eDel->Lface ); + } + } + + /* Claim: the mesh is now in a consistent state, except that eDel->Org + * may have been deleted. Now we disconnect eDel->Dst. + */ + if( eDelSym->Onext == eDelSym ) { + KillVertex( eDelSym->Org, NULL ); + KillFace( eDelSym->Lface, NULL ); + } else { + /* Make sure that eDel->Dst and eDel->Lface point to valid half-edges */ + eDel->Lface->anEdge = eDelSym->Oprev; + eDelSym->Org->anEdge = eDelSym->Onext; + Splice( eDelSym, eDelSym->Oprev ); + } + + /* Any isolated vertices or faces have already been freed. */ + KillEdge( eDel ); + + return 1; +} + + +/******************** Other Edge Operations **********************/ + +/* All these routines can be implemented with the basic edge + * operations above. They are provided for convenience and efficiency. + */ + + +/* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that + * eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex. + * eOrg and eNew will have the same left face. + */ +GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg ) +{ + GLUhalfEdge *eNewSym; + GLUhalfEdge *eNew = MakeEdge( eOrg ); + if (eNew == NULL) return NULL; + + eNewSym = eNew->Sym; + + /* Connect the new edge appropriately */ + Splice( eNew, eOrg->Lnext ); + + /* Set the vertex and face information */ + eNew->Org = eOrg->Dst; + { + GLUvertex *newVertex= allocVertex(); + if (newVertex == NULL) return NULL; + + MakeVertex( newVertex, eNewSym, eNew->Org ); + } + eNew->Lface = eNewSym->Lface = eOrg->Lface; + + return eNew; +} + + +/* __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew, + * such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org. + * eOrg and eNew will have the same left face. + */ +GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg ) +{ + GLUhalfEdge *eNew; + GLUhalfEdge *tempHalfEdge= __gl_meshAddEdgeVertex( eOrg ); + if (tempHalfEdge == NULL) return NULL; + + eNew = tempHalfEdge->Sym; + + /* Disconnect eOrg from eOrg->Dst and connect it to eNew->Org */ + Splice( eOrg->Sym, eOrg->Sym->Oprev ); + Splice( eOrg->Sym, eNew ); + + /* Set the vertex and face information */ + eOrg->Dst = eNew->Org; + eNew->Dst->anEdge = eNew->Sym; /* may have pointed to eOrg->Sym */ + eNew->Rface = eOrg->Rface; + eNew->winding = eOrg->winding; /* copy old winding information */ + eNew->Sym->winding = eOrg->Sym->winding; + + return eNew; +} + + +/* __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst + * to eDst->Org, and returns the corresponding half-edge eNew. + * If eOrg->Lface == eDst->Lface, this splits one loop into two, + * and the newly created loop is eNew->Lface. Otherwise, two disjoint + * loops are merged into one, and the loop eDst->Lface is destroyed. + * + * If (eOrg == eDst), the new face will have only two edges. + * If (eOrg->Lnext == eDst), the old face is reduced to a single edge. + * If (eOrg->Lnext->Lnext == eDst), the old face is reduced to two edges. + */ +GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ) +{ + GLUhalfEdge *eNewSym; + int joiningLoops = FALSE; + GLUhalfEdge *eNew = MakeEdge( eOrg ); + if (eNew == NULL) return NULL; + + eNewSym = eNew->Sym; + + if( eDst->Lface != eOrg->Lface ) { + /* We are connecting two disjoint loops -- destroy eDst->Lface */ + joiningLoops = TRUE; + KillFace( eDst->Lface, eOrg->Lface ); + } + + /* Connect the new edge appropriately */ + Splice( eNew, eOrg->Lnext ); + Splice( eNewSym, eDst ); + + /* Set the vertex and face information */ + eNew->Org = eOrg->Dst; + eNewSym->Org = eDst->Org; + eNew->Lface = eNewSym->Lface = eOrg->Lface; + + /* Make sure the old face points to a valid half-edge */ + eOrg->Lface->anEdge = eNewSym; + + if( ! joiningLoops ) { + GLUface *newFace= allocFace(); + if (newFace == NULL) return NULL; + + /* We split one loop into two -- the new loop is eNew->Lface */ + MakeFace( newFace, eNew, eOrg->Lface ); + } + return eNew; +} + + +/******************** Other Operations **********************/ + +/* __gl_meshZapFace( fZap ) destroys a face and removes it from the + * global face list. All edges of fZap will have a NULL pointer as their + * left face. Any edges which also have a NULL pointer as their right face + * are deleted entirely (along with any isolated vertices this produces). + * An entire mesh can be deleted by zapping its faces, one at a time, + * in any order. Zapped faces cannot be used in further mesh operations! + */ +void __gl_meshZapFace( GLUface *fZap ) +{ + GLUhalfEdge *eStart = fZap->anEdge; + GLUhalfEdge *e, *eNext, *eSym; + GLUface *fPrev, *fNext; + + /* walk around face, deleting edges whose right face is also NULL */ + eNext = eStart->Lnext; + do { + e = eNext; + eNext = e->Lnext; + + e->Lface = NULL; + if( e->Rface == NULL ) { + /* delete the edge -- see __gl_MeshDelete above */ + + if( e->Onext == e ) { + KillVertex( e->Org, NULL ); + } else { + /* Make sure that e->Org points to a valid half-edge */ + e->Org->anEdge = e->Onext; + Splice( e, e->Oprev ); + } + eSym = e->Sym; + if( eSym->Onext == eSym ) { + KillVertex( eSym->Org, NULL ); + } else { + /* Make sure that eSym->Org points to a valid half-edge */ + eSym->Org->anEdge = eSym->Onext; + Splice( eSym, eSym->Oprev ); + } + KillEdge( e ); + } + } while( e != eStart ); + + /* delete from circular doubly-linked list */ + fPrev = fZap->prev; + fNext = fZap->next; + fNext->prev = fPrev; + fPrev->next = fNext; + + memFree( fZap ); +} + + +/* __gl_meshNewMesh() creates a new mesh with no edges, no vertices, + * and no loops (what we usually call a "face"). + */ +GLUmesh *__gl_meshNewMesh( void ) +{ + GLUvertex *v; + GLUface *f; + GLUhalfEdge *e; + GLUhalfEdge *eSym; + GLUmesh *mesh = (GLUmesh *)memAlloc( sizeof( GLUmesh )); + if (mesh == NULL) { + return NULL; + } + + v = &mesh->vHead; + f = &mesh->fHead; + e = &mesh->eHead; + eSym = &mesh->eHeadSym; + + v->next = v->prev = v; + v->anEdge = NULL; + v->data = NULL; + + f->next = f->prev = f; + f->anEdge = NULL; + f->data = NULL; + f->trail = NULL; + f->marked = FALSE; + f->inside = FALSE; + + e->next = e; + e->Sym = eSym; + e->Onext = NULL; + e->Lnext = NULL; + e->Org = NULL; + e->Lface = NULL; + e->winding = 0; + e->activeRegion = NULL; + + eSym->next = eSym; + eSym->Sym = e; + eSym->Onext = NULL; + eSym->Lnext = NULL; + eSym->Org = NULL; + eSym->Lface = NULL; + eSym->winding = 0; + eSym->activeRegion = NULL; + + return mesh; +} + + +/* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in + * both meshes, and returns the new mesh (the old meshes are destroyed). + */ +GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 ) +{ + GLUface *f1 = &mesh1->fHead; + GLUvertex *v1 = &mesh1->vHead; + GLUhalfEdge *e1 = &mesh1->eHead; + GLUface *f2 = &mesh2->fHead; + GLUvertex *v2 = &mesh2->vHead; + GLUhalfEdge *e2 = &mesh2->eHead; + + /* Add the faces, vertices, and edges of mesh2 to those of mesh1 */ + if( f2->next != f2 ) { + f1->prev->next = f2->next; + f2->next->prev = f1->prev; + f2->prev->next = f1; + f1->prev = f2->prev; + } + + if( v2->next != v2 ) { + v1->prev->next = v2->next; + v2->next->prev = v1->prev; + v2->prev->next = v1; + v1->prev = v2->prev; + } + + if( e2->next != e2 ) { + e1->Sym->next->Sym->next = e2->next; + e2->next->Sym->next = e1->Sym->next; + e2->Sym->next->Sym->next = e1; + e1->Sym->next = e2->Sym->next; + } + + memFree( mesh2 ); + return mesh1; +} + + +#ifdef DELETE_BY_ZAPPING + +/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. + */ +void __gl_meshDeleteMesh( GLUmesh *mesh ) +{ + GLUface *fHead = &mesh->fHead; + + while( fHead->next != fHead ) { + __gl_meshZapFace( fHead->next ); + } + assert( mesh->vHead.next == &mesh->vHead ); + + memFree( mesh ); +} + +#else + +/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. + */ +void __gl_meshDeleteMesh( GLUmesh *mesh ) +{ + GLUface *f, *fNext; + GLUvertex *v, *vNext; + GLUhalfEdge *e, *eNext; + + for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) { + fNext = f->next; + memFree( f ); + } + + for( v = mesh->vHead.next; v != &mesh->vHead; v = vNext ) { + vNext = v->next; + memFree( v ); + } + + for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) { + /* One call frees both e and e->Sym (see EdgePair above) */ + eNext = e->next; + memFree( e ); + } + + memFree( mesh ); +} + +#endif + +#ifndef NDEBUG + +/* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency. + */ +void __gl_meshCheckMesh( GLUmesh *mesh ) +{ + GLUface *fHead = &mesh->fHead; + GLUvertex *vHead = &mesh->vHead; + GLUhalfEdge *eHead = &mesh->eHead; + GLUface *f, *fPrev; + GLUvertex *v, *vPrev; + GLUhalfEdge *e, *ePrev; + + fPrev = fHead; + for( fPrev = fHead ; (f = fPrev->next) != fHead; fPrev = f) { + assert( f->prev == fPrev ); + e = f->anEdge; + do { + assert( e->Sym != e ); + assert( e->Sym->Sym == e ); + assert( e->Lnext->Onext->Sym == e ); + assert( e->Onext->Sym->Lnext == e ); + assert( e->Lface == f ); + e = e->Lnext; + } while( e != f->anEdge ); + } + assert( f->prev == fPrev && f->anEdge == NULL && f->data == NULL ); + + vPrev = vHead; + for( vPrev = vHead ; (v = vPrev->next) != vHead; vPrev = v) { + assert( v->prev == vPrev ); + e = v->anEdge; + do { + assert( e->Sym != e ); + assert( e->Sym->Sym == e ); + assert( e->Lnext->Onext->Sym == e ); + assert( e->Onext->Sym->Lnext == e ); + assert( e->Org == v ); + e = e->Onext; + } while( e != v->anEdge ); + } + assert( v->prev == vPrev && v->anEdge == NULL && v->data == NULL ); + + ePrev = eHead; + for( ePrev = eHead ; (e = ePrev->next) != eHead; ePrev = e) { + assert( e->Sym->next == ePrev->Sym ); + assert( e->Sym != e ); + assert( e->Sym->Sym == e ); + assert( e->Org != NULL ); + assert( e->Dst != NULL ); + assert( e->Lnext->Onext->Sym == e ); + assert( e->Onext->Sym->Lnext == e ); + } + assert( e->Sym->next == ePrev->Sym + && e->Sym == &mesh->eHeadSym + && e->Sym->Sym == e + && e->Org == NULL && e->Dst == NULL + && e->Lface == NULL && e->Rface == NULL ); +} + +#endif diff --git a/deps/libtess/mesh.h b/deps/libtess/mesh.h new file mode 100644 index 0000000000..3eea9ddc43 --- /dev/null +++ b/deps/libtess/mesh.h @@ -0,0 +1,273 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/mesh.h,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#ifndef __mesh_h_ +#define __mesh_h_ + +#include "glutess.h" + +typedef struct GLUmesh GLUmesh; + +typedef struct GLUvertex GLUvertex; +typedef struct GLUface GLUface; +typedef struct GLUhalfEdge GLUhalfEdge; + +typedef struct ActiveRegion ActiveRegion; /* Internal data */ + +/* The mesh structure is similar in spirit, notation, and operations + * to the "quad-edge" structure (see L. Guibas and J. Stolfi, Primitives + * for the manipulation of general subdivisions and the computation of + * Voronoi diagrams, ACM Transactions on Graphics, 4(2):74-123, April 1985). + * For a simplified description, see the course notes for CS348a, + * "Mathematical Foundations of Computer Graphics", available at the + * Stanford bookstore (and taught during the fall quarter). + * The implementation also borrows a tiny subset of the graph-based approach + * use in Mantyla's Geometric Work Bench (see M. Mantyla, An Introduction + * to Sold Modeling, Computer Science Press, Rockville, Maryland, 1988). + * + * The fundamental data structure is the "half-edge". Two half-edges + * go together to make an edge, but they point in opposite directions. + * Each half-edge has a pointer to its mate (the "symmetric" half-edge Sym), + * its origin vertex (Org), the face on its left side (Lface), and the + * adjacent half-edges in the CCW direction around the origin vertex + * (Onext) and around the left face (Lnext). There is also a "next" + * pointer for the global edge list (see below). + * + * The notation used for mesh navigation: + * Sym = the mate of a half-edge (same edge, but opposite direction) + * Onext = edge CCW around origin vertex (keep same origin) + * Dnext = edge CCW around destination vertex (keep same dest) + * Lnext = edge CCW around left face (dest becomes new origin) + * Rnext = edge CCW around right face (origin becomes new dest) + * + * "prev" means to substitute CW for CCW in the definitions above. + * + * The mesh keeps global lists of all vertices, faces, and edges, + * stored as doubly-linked circular lists with a dummy header node. + * The mesh stores pointers to these dummy headers (vHead, fHead, eHead). + * + * The circular edge list is special; since half-edges always occur + * in pairs (e and e->Sym), each half-edge stores a pointer in only + * one direction. Starting at eHead and following the e->next pointers + * will visit each *edge* once (ie. e or e->Sym, but not both). + * e->Sym stores a pointer in the opposite direction, thus it is + * always true that e->Sym->next->Sym->next == e. + * + * Each vertex has a pointer to next and previous vertices in the + * circular list, and a pointer to a half-edge with this vertex as + * the origin (NULL if this is the dummy header). There is also a + * field "data" for client data. + * + * Each face has a pointer to the next and previous faces in the + * circular list, and a pointer to a half-edge with this face as + * the left face (NULL if this is the dummy header). There is also + * a field "data" for client data. + * + * Note that what we call a "face" is really a loop; faces may consist + * of more than one loop (ie. not simply connected), but there is no + * record of this in the data structure. The mesh may consist of + * several disconnected regions, so it may not be possible to visit + * the entire mesh by starting at a half-edge and traversing the edge + * structure. + * + * The mesh does NOT support isolated vertices; a vertex is deleted along + * with its last edge. Similarly when two faces are merged, one of the + * faces is deleted (see __gl_meshDelete below). For mesh operations, + * all face (loop) and vertex pointers must not be NULL. However, once + * mesh manipulation is finished, __gl_MeshZapFace can be used to delete + * faces of the mesh, one at a time. All external faces can be "zapped" + * before the mesh is returned to the client; then a NULL face indicates + * a region which is not part of the output polygon. + */ + +struct GLUvertex { + GLUvertex *next; /* next vertex (never NULL) */ + GLUvertex *prev; /* previous vertex (never NULL) */ + GLUhalfEdge *anEdge; /* a half-edge with this origin */ + void *data; /* client's data */ + + /* Internal data (keep hidden) */ + GLdouble coords[3]; /* vertex location in 3D */ + GLdouble s, t; /* projection onto the sweep plane */ + long pqHandle; /* to allow deletion from priority queue */ +}; + +struct GLUface { + GLUface *next; /* next face (never NULL) */ + GLUface *prev; /* previous face (never NULL) */ + GLUhalfEdge *anEdge; /* a half edge with this left face */ + void *data; /* room for client's data */ + + /* Internal data (keep hidden) */ + GLUface *trail; /* "stack" for conversion to strips */ + GLboolean marked; /* flag for conversion to strips */ + GLboolean inside; /* this face is in the polygon interior */ +}; + +struct GLUhalfEdge { + GLUhalfEdge *next; /* doubly-linked list (prev==Sym->next) */ + GLUhalfEdge *Sym; /* same edge, opposite direction */ + GLUhalfEdge *Onext; /* next edge CCW around origin */ + GLUhalfEdge *Lnext; /* next edge CCW around left face */ + GLUvertex *Org; /* origin vertex (Overtex too long) */ + GLUface *Lface; /* left face */ + + /* Internal data (keep hidden) */ + ActiveRegion *activeRegion; /* a region with this upper edge (sweep.c) */ + int winding; /* change in winding number when crossing + from the right face to the left face */ +}; + +#define Rface Sym->Lface +#define Dst Sym->Org + +#define Oprev Sym->Lnext +#define Lprev Onext->Sym +#define Dprev Lnext->Sym +#define Rprev Sym->Onext +#define Dnext Rprev->Sym /* 3 pointers */ +#define Rnext Oprev->Sym /* 3 pointers */ + + +struct GLUmesh { + GLUvertex vHead; /* dummy header for vertex list */ + GLUface fHead; /* dummy header for face list */ + GLUhalfEdge eHead; /* dummy header for edge list */ + GLUhalfEdge eHeadSym; /* and its symmetric counterpart */ +}; + +/* The mesh operations below have three motivations: completeness, + * convenience, and efficiency. The basic mesh operations are MakeEdge, + * Splice, and Delete. All the other edge operations can be implemented + * in terms of these. The other operations are provided for convenience + * and/or efficiency. + * + * When a face is split or a vertex is added, they are inserted into the + * global list *before* the existing vertex or face (ie. e->Org or e->Lface). + * This makes it easier to process all vertices or faces in the global lists + * without worrying about processing the same data twice. As a convenience, + * when a face is split, the "inside" flag is copied from the old face. + * Other internal data (v->data, v->activeRegion, f->data, f->marked, + * f->trail, e->winding) is set to zero. + * + * ********************** Basic Edge Operations ************************** + * + * __gl_meshMakeEdge( mesh ) creates one edge, two vertices, and a loop. + * The loop (face) consists of the two new half-edges. + * + * __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the + * mesh connectivity and topology. It changes the mesh so that + * eOrg->Onext <- OLD( eDst->Onext ) + * eDst->Onext <- OLD( eOrg->Onext ) + * where OLD(...) means the value before the meshSplice operation. + * + * This can have two effects on the vertex structure: + * - if eOrg->Org != eDst->Org, the two vertices are merged together + * - if eOrg->Org == eDst->Org, the origin is split into two vertices + * In both cases, eDst->Org is changed and eOrg->Org is untouched. + * + * Similarly (and independently) for the face structure, + * - if eOrg->Lface == eDst->Lface, one loop is split into two + * - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one + * In both cases, eDst->Lface is changed and eOrg->Lface is unaffected. + * + * __gl_meshDelete( eDel ) removes the edge eDel. There are several cases: + * if (eDel->Lface != eDel->Rface), we join two loops into one; the loop + * eDel->Lface is deleted. Otherwise, we are splitting one loop into two; + * the newly created loop will contain eDel->Dst. If the deletion of eDel + * would create isolated vertices, those are deleted as well. + * + * ********************** Other Edge Operations ************************** + * + * __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that + * eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex. + * eOrg and eNew will have the same left face. + * + * __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew, + * such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org. + * eOrg and eNew will have the same left face. + * + * __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst + * to eDst->Org, and returns the corresponding half-edge eNew. + * If eOrg->Lface == eDst->Lface, this splits one loop into two, + * and the newly created loop is eNew->Lface. Otherwise, two disjoint + * loops are merged into one, and the loop eDst->Lface is destroyed. + * + * ************************ Other Operations ***************************** + * + * __gl_meshNewMesh() creates a new mesh with no edges, no vertices, + * and no loops (what we usually call a "face"). + * + * __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in + * both meshes, and returns the new mesh (the old meshes are destroyed). + * + * __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. + * + * __gl_meshZapFace( fZap ) destroys a face and removes it from the + * global face list. All edges of fZap will have a NULL pointer as their + * left face. Any edges which also have a NULL pointer as their right face + * are deleted entirely (along with any isolated vertices this produces). + * An entire mesh can be deleted by zapping its faces, one at a time, + * in any order. Zapped faces cannot be used in further mesh operations! + * + * __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency. + */ + +GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh ); +int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ); +int __gl_meshDelete( GLUhalfEdge *eDel ); + +GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg ); +GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg ); +GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ); + +GLUmesh *__gl_meshNewMesh( void ); +GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 ); +void __gl_meshDeleteMesh( GLUmesh *mesh ); +void __gl_meshZapFace( GLUface *fZap ); + +#ifdef NDEBUG +#define __gl_meshCheckMesh( mesh ) +#else +void __gl_meshCheckMesh( GLUmesh *mesh ); +#endif + +#endif diff --git a/deps/libtess/normal.c b/deps/libtess/normal.c new file mode 100644 index 0000000000..f69544618c --- /dev/null +++ b/deps/libtess/normal.c @@ -0,0 +1,258 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include "mesh.h" +#include "tess.h" +#include "normal.h" +#include +#include + +#define TRUE 1 +#define FALSE 0 + +#define Dot(u,v) (u[0]*v[0] + u[1]*v[1] + u[2]*v[2]) + +#if 0 +static void Normalize( GLdouble v[3] ) +{ + GLdouble len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; + + assert( len > 0 ); + len = sqrt( len ); + v[0] /= len; + v[1] /= len; + v[2] /= len; +} +#endif + +#undef ABS +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +static int LongAxis( GLdouble v[3] ) +{ + int i = 0; + + if( ABS(v[1]) > ABS(v[0]) ) { i = 1; } + if( ABS(v[2]) > ABS(v[i]) ) { i = 2; } + return i; +} + +static void ComputeNormal( GLUtesselator *tess, GLdouble norm[3] ) +{ + GLUvertex *v, *v1, *v2; + GLdouble c, tLen2, maxLen2; + GLdouble maxVal[3], minVal[3], d1[3], d2[3], tNorm[3]; + GLUvertex *maxVert[3], *minVert[3]; + GLUvertex *vHead = &tess->mesh->vHead; + int i; + + maxVal[0] = maxVal[1] = maxVal[2] = -2 * GLU_TESS_MAX_COORD; + minVal[0] = minVal[1] = minVal[2] = 2 * GLU_TESS_MAX_COORD; + + for( v = vHead->next; v != vHead; v = v->next ) { + for( i = 0; i < 3; ++i ) { + c = v->coords[i]; + if( c < minVal[i] ) { minVal[i] = c; minVert[i] = v; } + if( c > maxVal[i] ) { maxVal[i] = c; maxVert[i] = v; } + } + } + + /* Find two vertices separated by at least 1/sqrt(3) of the maximum + * distance between any two vertices + */ + i = 0; + if( maxVal[1] - minVal[1] > maxVal[0] - minVal[0] ) { i = 1; } + if( maxVal[2] - minVal[2] > maxVal[i] - minVal[i] ) { i = 2; } + if( minVal[i] >= maxVal[i] ) { + /* All vertices are the same -- normal doesn't matter */ + norm[0] = 0; norm[1] = 0; norm[2] = 1; + return; + } + + /* Look for a third vertex which forms the triangle with maximum area + * (Length of normal == twice the triangle area) + */ + maxLen2 = 0; + v1 = minVert[i]; + v2 = maxVert[i]; + d1[0] = v1->coords[0] - v2->coords[0]; + d1[1] = v1->coords[1] - v2->coords[1]; + d1[2] = v1->coords[2] - v2->coords[2]; + for( v = vHead->next; v != vHead; v = v->next ) { + d2[0] = v->coords[0] - v2->coords[0]; + d2[1] = v->coords[1] - v2->coords[1]; + d2[2] = v->coords[2] - v2->coords[2]; + tNorm[0] = d1[1]*d2[2] - d1[2]*d2[1]; + tNorm[1] = d1[2]*d2[0] - d1[0]*d2[2]; + tNorm[2] = d1[0]*d2[1] - d1[1]*d2[0]; + tLen2 = tNorm[0]*tNorm[0] + tNorm[1]*tNorm[1] + tNorm[2]*tNorm[2]; + if( tLen2 > maxLen2 ) { + maxLen2 = tLen2; + norm[0] = tNorm[0]; + norm[1] = tNorm[1]; + norm[2] = tNorm[2]; + } + } + + if( maxLen2 <= 0 ) { + /* All points lie on a single line -- any decent normal will do */ + norm[0] = norm[1] = norm[2] = 0; + norm[LongAxis(d1)] = 1; + } +} + + +static void CheckOrientation( GLUtesselator *tess ) +{ + GLdouble area; + GLUface *f, *fHead = &tess->mesh->fHead; + GLUvertex *v, *vHead = &tess->mesh->vHead; + GLUhalfEdge *e; + + /* When we compute the normal automatically, we choose the orientation + * so that the the sum of the signed areas of all contours is non-negative. + */ + area = 0; + for( f = fHead->next; f != fHead; f = f->next ) { + e = f->anEdge; + if( e->winding <= 0 ) continue; + do { + area += (e->Org->s - e->Dst->s) * (e->Org->t + e->Dst->t); + e = e->Lnext; + } while( e != f->anEdge ); + } + if( area < 0 ) { + /* Reverse the orientation by flipping all the t-coordinates */ + for( v = vHead->next; v != vHead; v = v->next ) { + v->t = - v->t; + } + tess->tUnit[0] = - tess->tUnit[0]; + tess->tUnit[1] = - tess->tUnit[1]; + tess->tUnit[2] = - tess->tUnit[2]; + } +} + +#ifdef FOR_TRITE_TEST_PROGRAM +#include +extern int RandomSweep; +#define S_UNIT_X (RandomSweep ? (2*drand48()-1) : 1.0) +#define S_UNIT_Y (RandomSweep ? (2*drand48()-1) : 0.0) +#else +#if defined(SLANTED_SWEEP) +/* The "feature merging" is not intended to be complete. There are + * special cases where edges are nearly parallel to the sweep line + * which are not implemented. The algorithm should still behave + * robustly (ie. produce a reasonable tesselation) in the presence + * of such edges, however it may miss features which could have been + * merged. We could minimize this effect by choosing the sweep line + * direction to be something unusual (ie. not parallel to one of the + * coordinate axes). + */ +#define S_UNIT_X 0.50941539564955385 /* Pre-normalized */ +#define S_UNIT_Y 0.86052074622010633 +#else +#define S_UNIT_X 1.0 +#define S_UNIT_Y 0.0 +#endif +#endif + +/* Determine the polygon normal and project vertices onto the plane + * of the polygon. + */ +void __gl_projectPolygon( GLUtesselator *tess ) +{ + GLUvertex *v, *vHead = &tess->mesh->vHead; + GLdouble norm[3]; + GLdouble *sUnit, *tUnit; + int i, computedNormal = FALSE; + + norm[0] = tess->normal[0]; + norm[1] = tess->normal[1]; + norm[2] = tess->normal[2]; + if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) { + ComputeNormal( tess, norm ); + computedNormal = TRUE; + } + sUnit = tess->sUnit; + tUnit = tess->tUnit; + i = LongAxis( norm ); + +#if defined(FOR_TRITE_TEST_PROGRAM) || defined(TRUE_PROJECT) + /* Choose the initial sUnit vector to be approximately perpendicular + * to the normal. + */ + Normalize( norm ); + + sUnit[i] = 0; + sUnit[(i+1)%3] = S_UNIT_X; + sUnit[(i+2)%3] = S_UNIT_Y; + + /* Now make it exactly perpendicular */ + w = Dot( sUnit, norm ); + sUnit[0] -= w * norm[0]; + sUnit[1] -= w * norm[1]; + sUnit[2] -= w * norm[2]; + Normalize( sUnit ); + + /* Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame */ + tUnit[0] = norm[1]*sUnit[2] - norm[2]*sUnit[1]; + tUnit[1] = norm[2]*sUnit[0] - norm[0]*sUnit[2]; + tUnit[2] = norm[0]*sUnit[1] - norm[1]*sUnit[0]; + Normalize( tUnit ); +#else + /* Project perpendicular to a coordinate axis -- better numerically */ + sUnit[i] = 0; + sUnit[(i+1)%3] = S_UNIT_X; + sUnit[(i+2)%3] = S_UNIT_Y; + + tUnit[i] = 0; + tUnit[(i+1)%3] = (norm[i] > 0) ? -S_UNIT_Y : S_UNIT_Y; + tUnit[(i+2)%3] = (norm[i] > 0) ? S_UNIT_X : -S_UNIT_X; +#endif + + /* Project the vertices onto the sweep plane */ + for( v = vHead->next; v != vHead; v = v->next ) { + v->s = Dot( v->coords, sUnit ); + v->t = Dot( v->coords, tUnit ); + } + if( computedNormal ) { + CheckOrientation( tess ); + } +} diff --git a/deps/libtess/normal.h b/deps/libtess/normal.h new file mode 100644 index 0000000000..4e99a1b015 --- /dev/null +++ b/deps/libtess/normal.h @@ -0,0 +1,52 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/normal.h,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#ifndef __normal_h_ +#define __normal_h_ + +#include "tess.h" + +/* __gl_projectPolygon( tess ) determines the polygon normal + * and project vertices onto the plane of the polygon. + */ +void __gl_projectPolygon( struct GLUtesselator *tess ); + +#endif diff --git a/deps/libtess/priorityq.c b/deps/libtess/priorityq.c new file mode 100644 index 0000000000..ec7ba623e2 --- /dev/null +++ b/deps/libtess/priorityq.c @@ -0,0 +1,267 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/priorityq.c,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#include "gluos.h" +#include +#include +#include /* LONG_MAX */ +#include "memalloc.h" + +/* Include all the code for the regular heap-based queue here. */ + +#include "priorityqHeap.c" + +/* Now redefine all the function names to map to their "Sort" versions. */ + +#include "priorityqSort.h" + +/* really __gl_pqSortNewPriorityQ */ +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ) +{ + PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ )); + if (pq == NULL) return NULL; + + pq->heap = __gl_pqHeapNewPriorityQ( leq ); + if (pq->heap == NULL) { + memFree(pq); + return NULL; + } + + pq->keys = (PQHeapKey *)memAlloc( INIT_SIZE * sizeof(pq->keys[0]) ); + if (pq->keys == NULL) { + __gl_pqHeapDeletePriorityQ(pq->heap); + memFree(pq); + return NULL; + } + + pq->size = 0; + pq->max = INIT_SIZE; + pq->initialized = FALSE; + pq->leq = leq; + return pq; +} + +/* really __gl_pqSortDeletePriorityQ */ +void pqDeletePriorityQ( PriorityQ *pq ) +{ + assert(pq != NULL); + if (pq->heap != NULL) __gl_pqHeapDeletePriorityQ( pq->heap ); + if (pq->order != NULL) memFree( pq->order ); + if (pq->keys != NULL) memFree( pq->keys ); + memFree( pq ); +} + + +#define LT(x,y) (! LEQ(y,x)) +#define GT(x,y) (! LEQ(x,y)) +#define Swap(a,b) if(1){PQkey *tmp = *a; *a = *b; *b = tmp;}else + +/* really __gl_pqSortInit */ +int pqInit( PriorityQ *pq ) +{ + PQkey **p, **r, **i, **j, *piv; + struct { PQkey **p, **r; } Stack[50], *top = Stack; + unsigned long seed = 2016473283; + + /* Create an array of indirect pointers to the keys, so that we + * the handles we have returned are still valid. + */ +/* + pq->order = (PQHeapKey **)memAlloc( (size_t) + (pq->size * sizeof(pq->order[0])) ); +*/ + pq->order = (PQHeapKey **)memAlloc( (size_t) + ((pq->size+1) * sizeof(pq->order[0])) ); +/* the previous line is a patch to compensate for the fact that IBM */ +/* machines return a null on a malloc of zero bytes (unlike SGI), */ +/* so we have to put in this defense to guard against a memory */ +/* fault four lines down. from fossum@austin.ibm.com. */ + if (pq->order == NULL) return 0; + + p = pq->order; + r = p + pq->size - 1; + for( piv = pq->keys, i = p; i <= r; ++piv, ++i ) { + *i = piv; + } + + /* Sort the indirect pointers in descending order, + * using randomized Quicksort + */ + top->p = p; top->r = r; ++top; + while( --top >= Stack ) { + p = top->p; + r = top->r; + while( r > p + 10 ) { + seed = seed * 1539415821 + 1; + i = p + seed % (r - p + 1); + piv = *i; + *i = *p; + *p = piv; + i = p - 1; + j = r + 1; + do { + do { ++i; } while( GT( **i, *piv )); + do { --j; } while( LT( **j, *piv )); + Swap( i, j ); + } while( i < j ); + Swap( i, j ); /* Undo last swap */ + if( i - p < r - j ) { + top->p = j+1; top->r = r; ++top; + r = i-1; + } else { + top->p = p; top->r = i-1; ++top; + p = j+1; + } + } + /* Insertion sort small lists */ + for( i = p+1; i <= r; ++i ) { + piv = *i; + for( j = i; j > p && LT( **(j-1), *piv ); --j ) { + *j = *(j-1); + } + *j = piv; + } + } + pq->max = pq->size; + pq->initialized = TRUE; + __gl_pqHeapInit( pq->heap ); /* always succeeds */ + +#ifndef NDEBUG + p = pq->order; + r = p + pq->size - 1; + for( i = p; i < r; ++i ) { + assert( LEQ( **(i+1), **i )); + } +#endif + + return 1; +} + +/* really __gl_pqSortInsert */ +/* returns LONG_MAX iff out of memory */ +PQhandle pqInsert( PriorityQ *pq, PQkey keyNew ) +{ + long curr; + + if( pq->initialized ) { + return __gl_pqHeapInsert( pq->heap, keyNew ); + } + curr = pq->size; + if( ++ pq->size >= pq->max ) { + PQkey *saveKey= pq->keys; + + /* If the heap overflows, double its size. */ + pq->max <<= 1; + pq->keys = (PQHeapKey *)memRealloc( pq->keys, + (size_t) + (pq->max * sizeof( pq->keys[0] ))); + if (pq->keys == NULL) { + pq->keys = saveKey; /* restore ptr to free upon return */ + return LONG_MAX; + } + } + assert(curr != LONG_MAX); + pq->keys[curr] = keyNew; + + /* Negative handles index the sorted array. */ + return -(curr+1); +} + +/* really __gl_pqSortExtractMin */ +PQkey pqExtractMin( PriorityQ *pq ) +{ + PQkey sortMin, heapMin; + + if( pq->size == 0 ) { + return __gl_pqHeapExtractMin( pq->heap ); + } + sortMin = *(pq->order[pq->size-1]); + if( ! __gl_pqHeapIsEmpty( pq->heap )) { + heapMin = __gl_pqHeapMinimum( pq->heap ); + if( LEQ( heapMin, sortMin )) { + return __gl_pqHeapExtractMin( pq->heap ); + } + } + do { + -- pq->size; + } while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ); + return sortMin; +} + +/* really __gl_pqSortMinimum */ +PQkey pqMinimum( PriorityQ *pq ) +{ + PQkey sortMin, heapMin; + + if( pq->size == 0 ) { + return __gl_pqHeapMinimum( pq->heap ); + } + sortMin = *(pq->order[pq->size-1]); + if( ! __gl_pqHeapIsEmpty( pq->heap )) { + heapMin = __gl_pqHeapMinimum( pq->heap ); + if( LEQ( heapMin, sortMin )) { + return heapMin; + } + } + return sortMin; +} + +/* really __gl_pqSortIsEmpty */ +int pqIsEmpty( PriorityQ *pq ) +{ + return (pq->size == 0) && __gl_pqHeapIsEmpty( pq->heap ); +} + +/* really __gl_pqSortDelete */ +void pqDelete( PriorityQ *pq, PQhandle curr ) +{ + if( curr >= 0 ) { + __gl_pqHeapDelete( pq->heap, curr ); + return; + } + curr = -(curr+1); + assert( curr < pq->max && pq->keys[curr] != NULL ); + + pq->keys[curr] = NULL; + while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ) { + -- pq->size; + } +} diff --git a/deps/libtess/priorityq.h b/deps/libtess/priorityq.h new file mode 100644 index 0000000000..8f1a39c13d --- /dev/null +++ b/deps/libtess/priorityq.h @@ -0,0 +1,124 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/priorityq.h,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#ifndef __priorityq_sort_h_ +#define __priorityq_sort_h_ + +#include "priorityqHeap.h" + +#undef PQkey +#undef PQhandle +#undef PriorityQ +#undef pqNewPriorityQ +#undef pqDeletePriorityQ +#undef pqInit +#undef pqInsert +#undef pqMinimum +#undef pqExtractMin +#undef pqDelete +#undef pqIsEmpty + +/* Use #define's so that another heap implementation can use this one */ + +#define PQkey PQSortKey +#define PQhandle PQSortHandle +#define PriorityQ PriorityQSort + +#define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq) +#define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq) + +/* The basic operations are insertion of a new key (pqInsert), + * and examination/extraction of a key whose value is minimum + * (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete); + * for this purpose pqInsert returns a "handle" which is supplied + * as the argument. + * + * An initial heap may be created efficiently by calling pqInsert + * repeatedly, then calling pqInit. In any case pqInit must be called + * before any operations other than pqInsert are used. + * + * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key. + * This may also be tested with pqIsEmpty. + */ +#define pqInit(pq) __gl_pqSortInit(pq) +#define pqInsert(pq,key) __gl_pqSortInsert(pq,key) +#define pqMinimum(pq) __gl_pqSortMinimum(pq) +#define pqExtractMin(pq) __gl_pqSortExtractMin(pq) +#define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle) +#define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq) + + +/* Since we support deletion the data structure is a little more + * complicated than an ordinary heap. "nodes" is the heap itself; + * active nodes are stored in the range 1..pq->size. When the + * heap exceeds its allocated size (pq->max), its size doubles. + * The children of node i are nodes 2i and 2i+1. + * + * Each node stores an index into an array "handles". Each handle + * stores a key, plus a pointer back to the node which currently + * represents that key (ie. nodes[handles[i].node].handle == i). + */ + +typedef PQHeapKey PQkey; +typedef PQHeapHandle PQhandle; +typedef struct PriorityQ PriorityQ; + +struct PriorityQ { + PriorityQHeap *heap; + PQkey *keys; + PQkey **order; + PQhandle size, max; + int initialized; + int (*leq)(PQkey key1, PQkey key2); +}; + +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ); +void pqDeletePriorityQ( PriorityQ *pq ); + +int pqInit( PriorityQ *pq ); +PQhandle pqInsert( PriorityQ *pq, PQkey key ); +PQkey pqExtractMin( PriorityQ *pq ); +void pqDelete( PriorityQ *pq, PQhandle handle ); + +PQkey pqMinimum( PriorityQ *pq ); +int pqIsEmpty( PriorityQ *pq ); + +#endif diff --git a/deps/libtess/priorityqHeap.c b/deps/libtess/priorityqHeap.c new file mode 100644 index 0000000000..758db4d3c4 --- /dev/null +++ b/deps/libtess/priorityqHeap.c @@ -0,0 +1,259 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/priorityq-heap.c,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#include +#include +#include "priorityqHeap.h" +#include "memalloc.h" + +#define INIT_SIZE 32 + +#define TRUE 1 +#define FALSE 0 + +#ifdef FOR_TRITE_TEST_PROGRAM +#define LEQ(x,y) (*pq->leq)(x,y) +#else +/* Violates modularity, but a little faster */ +#include "geom.h" +#define LEQ(x,y) VertLeq((GLUvertex *)x, (GLUvertex *)y) +#endif + +/* really __gl_pqHeapNewPriorityQ */ +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ) +{ + PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ )); + if (pq == NULL) return NULL; + + pq->size = 0; + pq->max = INIT_SIZE; + pq->nodes = (PQnode *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->nodes[0]) ); + if (pq->nodes == NULL) { + memFree(pq); + return NULL; + } + + pq->handles = (PQhandleElem *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->handles[0]) ); + if (pq->handles == NULL) { + memFree(pq->nodes); + memFree(pq); + return NULL; + } + + pq->initialized = FALSE; + pq->freeList = 0; + pq->leq = leq; + + pq->nodes[1].handle = 1; /* so that Minimum() returns NULL */ + pq->handles[1].key = NULL; + return pq; +} + +/* really __gl_pqHeapDeletePriorityQ */ +void pqDeletePriorityQ( PriorityQ *pq ) +{ + memFree( pq->handles ); + memFree( pq->nodes ); + memFree( pq ); +} + + +static void FloatDown( PriorityQ *pq, long curr ) +{ + PQnode *n = pq->nodes; + PQhandleElem *h = pq->handles; + PQhandle hCurr, hChild; + long child; + + hCurr = n[curr].handle; + for( ;; ) { + child = curr << 1; + if( child < pq->size && LEQ( h[n[child+1].handle].key, + h[n[child].handle].key )) { + ++child; + } + + assert(child <= pq->max); + + hChild = n[child].handle; + if( child > pq->size || LEQ( h[hCurr].key, h[hChild].key )) { + n[curr].handle = hCurr; + h[hCurr].node = curr; + break; + } + n[curr].handle = hChild; + h[hChild].node = curr; + curr = child; + } +} + + +static void FloatUp( PriorityQ *pq, long curr ) +{ + PQnode *n = pq->nodes; + PQhandleElem *h = pq->handles; + PQhandle hCurr, hParent; + long parent; + + hCurr = n[curr].handle; + for( ;; ) { + parent = curr >> 1; + hParent = n[parent].handle; + if( parent == 0 || LEQ( h[hParent].key, h[hCurr].key )) { + n[curr].handle = hCurr; + h[hCurr].node = curr; + break; + } + n[curr].handle = hParent; + h[hParent].node = curr; + curr = parent; + } +} + +/* really __gl_pqHeapInit */ +void pqInit( PriorityQ *pq ) +{ + long i; + + /* This method of building a heap is O(n), rather than O(n lg n). */ + + for( i = pq->size; i >= 1; --i ) { + FloatDown( pq, i ); + } + pq->initialized = TRUE; +} + +/* really __gl_pqHeapInsert */ +/* returns LONG_MAX iff out of memory */ +PQhandle pqInsert( PriorityQ *pq, PQkey keyNew ) +{ + long curr; + PQhandle free; + + curr = ++ pq->size; + if( (curr*2) > pq->max ) { + PQnode *saveNodes= pq->nodes; + PQhandleElem *saveHandles= pq->handles; + + /* If the heap overflows, double its size. */ + pq->max <<= 1; + pq->nodes = (PQnode *)memRealloc( pq->nodes, + (size_t) + ((pq->max + 1) * sizeof( pq->nodes[0] ))); + if (pq->nodes == NULL) { + pq->nodes = saveNodes; /* restore ptr to free upon return */ + return ((int)0x7fffffff); //LONG_MAX; + } + pq->handles = (PQhandleElem *)memRealloc( pq->handles, + (size_t) + ((pq->max + 1) * + sizeof( pq->handles[0] ))); + if (pq->handles == NULL) { + pq->handles = saveHandles; /* restore ptr to free upon return */ + return ((int)0x7fffffff);//LONG_MAX; + } + } + + if( pq->freeList == 0 ) { + free = curr; + } else { + free = pq->freeList; + pq->freeList = pq->handles[free].node; + } + + pq->nodes[curr].handle = free; + pq->handles[free].node = curr; + pq->handles[free].key = keyNew; + + if( pq->initialized ) { + FloatUp( pq, curr ); + } + assert(free != ((int)0x7fffffff)); + return free; +} + +/* really __gl_pqHeapExtractMin */ +PQkey pqExtractMin( PriorityQ *pq ) +{ + PQnode *n = pq->nodes; + PQhandleElem *h = pq->handles; + PQhandle hMin = n[1].handle; + PQkey min = h[hMin].key; + + if( pq->size > 0 ) { + n[1].handle = n[pq->size].handle; + h[n[1].handle].node = 1; + + h[hMin].key = NULL; + h[hMin].node = pq->freeList; + pq->freeList = hMin; + + if( -- pq->size > 0 ) { + FloatDown( pq, 1 ); + } + } + return min; +} + +/* really __gl_pqHeapDelete */ +void pqDelete( PriorityQ *pq, PQhandle hCurr ) +{ + PQnode *n = pq->nodes; + PQhandleElem *h = pq->handles; + long curr; + + assert( hCurr >= 1 && hCurr <= pq->max && h[hCurr].key != NULL ); + + curr = h[hCurr].node; + n[curr].handle = n[pq->size].handle; + h[n[curr].handle].node = curr; + + if( curr <= -- pq->size ) { + if( curr <= 1 || LEQ( h[n[curr>>1].handle].key, h[n[curr].handle].key )) { + FloatDown( pq, curr ); + } else { + FloatUp( pq, curr ); + } + } + h[hCurr].key = NULL; + h[hCurr].node = pq->freeList; + pq->freeList = hCurr; +} diff --git a/deps/libtess/priorityqHeap.h b/deps/libtess/priorityqHeap.h new file mode 100644 index 0000000000..39c33c3921 --- /dev/null +++ b/deps/libtess/priorityqHeap.h @@ -0,0 +1,114 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/priorityq-heap.h,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#ifndef __priorityq_heap_h_ +#define __priorityq_heap_h_ + +/* Use #define's so that another heap implementation can use this one */ + +#define PQkey PQHeapKey +#define PQhandle PQHeapHandle +#define PriorityQ PriorityQHeap + +#define pqNewPriorityQ(leq) __gl_pqHeapNewPriorityQ(leq) +#define pqDeletePriorityQ(pq) __gl_pqHeapDeletePriorityQ(pq) + +/* The basic operations are insertion of a new key (pqInsert), + * and examination/extraction of a key whose value is minimum + * (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete); + * for this purpose pqInsert returns a "handle" which is supplied + * as the argument. + * + * An initial heap may be created efficiently by calling pqInsert + * repeatedly, then calling pqInit. In any case pqInit must be called + * before any operations other than pqInsert are used. + * + * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key. + * This may also be tested with pqIsEmpty. + */ +#define pqInit(pq) __gl_pqHeapInit(pq) +#define pqInsert(pq,key) __gl_pqHeapInsert(pq,key) +#define pqMinimum(pq) __gl_pqHeapMinimum(pq) +#define pqExtractMin(pq) __gl_pqHeapExtractMin(pq) +#define pqDelete(pq,handle) __gl_pqHeapDelete(pq,handle) +#define pqIsEmpty(pq) __gl_pqHeapIsEmpty(pq) + + +/* Since we support deletion the data structure is a little more + * complicated than an ordinary heap. "nodes" is the heap itself; + * active nodes are stored in the range 1..pq->size. When the + * heap exceeds its allocated size (pq->max), its size doubles. + * The children of node i are nodes 2i and 2i+1. + * + * Each node stores an index into an array "handles". Each handle + * stores a key, plus a pointer back to the node which currently + * represents that key (ie. nodes[handles[i].node].handle == i). + */ + +typedef void *PQkey; +typedef long PQhandle; +typedef struct PriorityQ PriorityQ; + +typedef struct { PQhandle handle; } PQnode; +typedef struct { PQkey key; PQhandle node; } PQhandleElem; + +struct PriorityQ { + PQnode *nodes; + PQhandleElem *handles; + long size, max; + PQhandle freeList; + int initialized; + int (*leq)(PQkey key1, PQkey key2); +}; + +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ); +void pqDeletePriorityQ( PriorityQ *pq ); + +void pqInit( PriorityQ *pq ); +PQhandle pqInsert( PriorityQ *pq, PQkey key ); +PQkey pqExtractMin( PriorityQ *pq ); +void pqDelete( PriorityQ *pq, PQhandle handle ); + + +#define __gl_pqHeapMinimum(pq) ((pq)->handles[(pq)->nodes[1].handle].key) +#define __gl_pqHeapIsEmpty(pq) ((pq)->size == 0) + +#endif diff --git a/deps/libtess/priorityqSort.h b/deps/libtess/priorityqSort.h new file mode 100644 index 0000000000..47475ee44c --- /dev/null +++ b/deps/libtess/priorityqSort.h @@ -0,0 +1,124 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/priorityq-sort.h,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#ifndef __priorityq_sort_h_ +#define __priorityq_sort_h_ + +#include "priorityqHeap.h" + +#undef PQkey +#undef PQhandle +#undef PriorityQ +#undef pqNewPriorityQ +#undef pqDeletePriorityQ +#undef pqInit +#undef pqInsert +#undef pqMinimum +#undef pqExtractMin +#undef pqDelete +#undef pqIsEmpty + +/* Use #define's so that another heap implementation can use this one */ + +#define PQkey PQSortKey +#define PQhandle PQSortHandle +#define PriorityQ PriorityQSort + +#define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq) +#define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq) + +/* The basic operations are insertion of a new key (pqInsert), + * and examination/extraction of a key whose value is minimum + * (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete); + * for this purpose pqInsert returns a "handle" which is supplied + * as the argument. + * + * An initial heap may be created efficiently by calling pqInsert + * repeatedly, then calling pqInit. In any case pqInit must be called + * before any operations other than pqInsert are used. + * + * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key. + * This may also be tested with pqIsEmpty. + */ +#define pqInit(pq) __gl_pqSortInit(pq) +#define pqInsert(pq,key) __gl_pqSortInsert(pq,key) +#define pqMinimum(pq) __gl_pqSortMinimum(pq) +#define pqExtractMin(pq) __gl_pqSortExtractMin(pq) +#define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle) +#define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq) + + +/* Since we support deletion the data structure is a little more + * complicated than an ordinary heap. "nodes" is the heap itself; + * active nodes are stored in the range 1..pq->size. When the + * heap exceeds its allocated size (pq->max), its size doubles. + * The children of node i are nodes 2i and 2i+1. + * + * Each node stores an index into an array "handles". Each handle + * stores a key, plus a pointer back to the node which currently + * represents that key (ie. nodes[handles[i].node].handle == i). + */ + +typedef PQHeapKey PQkey; +typedef PQHeapHandle PQhandle; +typedef struct PriorityQ PriorityQ; + +struct PriorityQ { + PriorityQHeap *heap; + PQkey *keys; + PQkey **order; + PQhandle size, max; + int initialized; + int (*leq)(PQkey key1, PQkey key2); +}; + +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ); +void pqDeletePriorityQ( PriorityQ *pq ); + +int pqInit( PriorityQ *pq ); +PQhandle pqInsert( PriorityQ *pq, PQkey key ); +PQkey pqExtractMin( PriorityQ *pq ); +void pqDelete( PriorityQ *pq, PQhandle handle ); + +PQkey pqMinimum( PriorityQ *pq ); +int pqIsEmpty( PriorityQ *pq ); + +#endif diff --git a/deps/libtess/render.c b/deps/libtess/render.c new file mode 100644 index 0000000000..97751dc810 --- /dev/null +++ b/deps/libtess/render.c @@ -0,0 +1,505 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/render.c,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#include "gluos.h" +#include +#include +#include "mesh.h" +#include "tess.h" +#include "render.h" + +#define TRUE 1 +#define FALSE 0 + +/* This structure remembers the information we need about a primitive + * to be able to render it later, once we have determined which + * primitive is able to use the most triangles. + */ +struct FaceCount { + long size; /* number of triangles used */ + GLUhalfEdge *eStart; /* edge where this primitive starts */ + void (*render)(GLUtesselator *, GLUhalfEdge *, long); + /* routine to render this primitive */ +}; + +static struct FaceCount MaximumFan( GLUhalfEdge *eOrig ); +static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig ); + +static void RenderFan( GLUtesselator *tess, GLUhalfEdge *eStart, long size ); +static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *eStart, long size ); +static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *eStart, + long size ); + +static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig ); +static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *head ); + + + +/************************ Strips and Fans decomposition ******************/ + +/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle + * fans, strips, and separate triangles. A substantial effort is made + * to use as few rendering primitives as possible (ie. to make the fans + * and strips as large as possible). + * + * The rendering output is provided as callbacks (see the api). + */ +void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh ) +{ + GLUface *f; + + /* Make a list of separate triangles so we can render them all at once */ + tess->lonelyTriList = NULL; + + for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) { + f->marked = FALSE; + } + for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) { + + /* We examine all faces in an arbitrary order. Whenever we find + * an unprocessed face F, we output a group of faces including F + * whose size is maximum. + */ + if( f->inside && ! f->marked ) { + RenderMaximumFaceGroup( tess, f ); + assert( f->marked ); + } + } + if( tess->lonelyTriList != NULL ) { + RenderLonelyTriangles( tess, tess->lonelyTriList ); + tess->lonelyTriList = NULL; + } +} + + +static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig ) +{ + /* We want to find the largest triangle fan or strip of unmarked faces + * which includes the given face fOrig. There are 3 possible fans + * passing through fOrig (one centered at each vertex), and 3 possible + * strips (one for each CCW permutation of the vertices). Our strategy + * is to try all of these, and take the primitive which uses the most + * triangles (a greedy approach). + */ + GLUhalfEdge *e = fOrig->anEdge; + struct FaceCount max, newFace; + + max.size = 1; + max.eStart = e; + max.render = &RenderTriangle; + + if( ! tess->flagBoundary ) { + newFace = MaximumFan( e ); if( newFace.size > max.size ) { max = newFace; } + newFace = MaximumFan( e->Lnext ); if( newFace.size > max.size ) { max = newFace; } + newFace = MaximumFan( e->Lprev ); if( newFace.size > max.size ) { max = newFace; } + + newFace = MaximumStrip( e ); if( newFace.size > max.size ) { max = newFace; } + newFace = MaximumStrip( e->Lnext ); if( newFace.size > max.size ) { max = newFace; } + newFace = MaximumStrip( e->Lprev ); if( newFace.size > max.size ) { max = newFace; } + } + (*(max.render))( tess, max.eStart, max.size ); +} + + +/* Macros which keep track of faces we have marked temporarily, and allow + * us to backtrack when necessary. With triangle fans, this is not + * really necessary, since the only awkward case is a loop of triangles + * around a single origin vertex. However with strips the situation is + * more complicated, and we need a general tracking method like the + * one here. + */ +#define Marked(f) (! (f)->inside || (f)->marked) + +#define AddToTrail(f,t) ((f)->trail = (t), (t) = (f), (f)->marked = TRUE) + +#define FreeTrail(t) if( 1 ) { \ + while( (t) != NULL ) { \ + (t)->marked = FALSE; t = (t)->trail; \ + } \ + } else /* absorb trailing semicolon */ + + + +static struct FaceCount MaximumFan( GLUhalfEdge *eOrig ) +{ + /* eOrig->Lface is the face we want to render. We want to find the size + * of a maximal fan around eOrig->Org. To do this we just walk around + * the origin vertex as far as possible in both directions. + */ + struct FaceCount newFace = { 0, NULL, &RenderFan }; + GLUface *trail = NULL; + GLUhalfEdge *e; + + for( e = eOrig; ! Marked( e->Lface ); e = e->Onext ) { + AddToTrail( e->Lface, trail ); + ++newFace.size; + } + for( e = eOrig; ! Marked( e->Rface ); e = e->Oprev ) { + AddToTrail( e->Rface, trail ); + ++newFace.size; + } + newFace.eStart = e; + /*LINTED*/ + FreeTrail( trail ); + return newFace; +} + + +#define IsEven(n) (((n) & 1) == 0) + +static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig ) +{ + /* Here we are looking for a maximal strip that contains the vertices + * eOrig->Org, eOrig->Dst, eOrig->Lnext->Dst (in that order or the + * reverse, such that all triangles are oriented CCW). + * + * Again we walk forward and backward as far as possible. However for + * strips there is a twist: to get CCW orientations, there must be + * an *even* number of triangles in the strip on one side of eOrig. + * We walk the strip starting on a side with an even number of triangles; + * if both side have an odd number, we are forced to shorten one side. + */ + struct FaceCount newFace = { 0, NULL, &RenderStrip }; + long headSize = 0, tailSize = 0; + GLUface *trail = NULL; + GLUhalfEdge *e, *eTail, *eHead; + + for( e = eOrig; ! Marked( e->Lface ); ++tailSize, e = e->Onext ) { + AddToTrail( e->Lface, trail ); + ++tailSize; + e = e->Dprev; + if( Marked( e->Lface )) break; + AddToTrail( e->Lface, trail ); + } + eTail = e; + + for( e = eOrig; ! Marked( e->Rface ); ++headSize, e = e->Dnext ) { + AddToTrail( e->Rface, trail ); + ++headSize; + e = e->Oprev; + if( Marked( e->Rface )) break; + AddToTrail( e->Rface, trail ); + } + eHead = e; + + newFace.size = tailSize + headSize; + if( IsEven( tailSize )) { + newFace.eStart = eTail->Sym; + } else if( IsEven( headSize )) { + newFace.eStart = eHead; + } else { + /* Both sides have odd length, we must shorten one of them. In fact, + * we must start from eHead to guarantee inclusion of eOrig->Lface. + */ + --newFace.size; + newFace.eStart = eHead->Onext; + } + /*LINTED*/ + FreeTrail( trail ); + return newFace; +} + + +static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *e, long size ) +{ + /* Just add the triangle to a triangle list, so we can render all + * the separate triangles at once. + */ + assert( size == 1 ); + AddToTrail( e->Lface, tess->lonelyTriList ); +} + + +static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *f ) +{ + /* Now we render all the separate triangles which could not be + * grouped into a triangle fan or strip. + */ + GLUhalfEdge *e; + int newState; + int edgeState = -1; /* force edge state output for first vertex */ + + CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLES ); + + for( ; f != NULL; f = f->trail ) { + /* Loop once for each edge (there will always be 3 edges) */ + + e = f->anEdge; + do { + if( tess->flagBoundary ) { + /* Set the "edge state" to TRUE just before we output the + * first vertex of each edge on the polygon boundary. + */ + newState = ! e->Rface->inside; + if( edgeState != newState ) { + edgeState = newState; + CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA( edgeState ); + } + } + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + + e = e->Lnext; + } while( e != f->anEdge ); + } + CALL_END_OR_END_DATA(); +} + + +static void RenderFan( GLUtesselator *tess, GLUhalfEdge *e, long size ) +{ + /* Render as many CCW triangles as possible in a fan starting from + * edge "e". The fan *should* contain exactly "size" triangles + * (otherwise we've goofed up somewhere). + */ + CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_FAN ); + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); + + while( ! Marked( e->Lface )) { + e->Lface->marked = TRUE; + --size; + e = e->Onext; + CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); + } + + assert( size == 0 ); + CALL_END_OR_END_DATA(); +} + + +static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *e, long size ) +{ + /* Render as many CCW triangles as possible in a strip starting from + * edge "e". The strip *should* contain exactly "size" triangles + * (otherwise we've goofed up somewhere). + */ + CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_STRIP ); + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); + + while( ! Marked( e->Lface )) { + e->Lface->marked = TRUE; + --size; + e = e->Dprev; + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + if( Marked( e->Lface )) break; + + e->Lface->marked = TRUE; + --size; + e = e->Onext; + CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); + } + + assert( size == 0 ); + CALL_END_OR_END_DATA(); +} + + +/************************ Boundary contour decomposition ******************/ + +/* __gl_renderBoundary( tess, mesh ) takes a mesh, and outputs one + * contour for each face marked "inside". The rendering output is + * provided as callbacks (see the api). + */ +void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh ) +{ + GLUface *f; + GLUhalfEdge *e; + + for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) { + if( f->inside ) { + CALL_BEGIN_OR_BEGIN_DATA( GL_LINE_LOOP ); + e = f->anEdge; + do { + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + e = e->Lnext; + } while( e != f->anEdge ); + CALL_END_OR_END_DATA(); + } + } +} + + +/************************ Quick-and-dirty decomposition ******************/ + +#define SIGN_INCONSISTENT 2 + +static int ComputeNormal( GLUtesselator *tess, GLdouble norm[3], int check ) +/* + * If check==FALSE, we compute the polygon normal and place it in norm[]. + * If check==TRUE, we check that each triangle in the fan from v0 has a + * consistent orientation with respect to norm[]. If triangles are + * consistently oriented CCW, return 1; if CW, return -1; if all triangles + * are degenerate return 0; otherwise (no consistent orientation) return + * SIGN_INCONSISTENT. + */ +{ + CachedVertex *v0 = tess->cache; + CachedVertex *vn = v0 + tess->cacheCount; + CachedVertex *vc; + GLdouble dot, xc, yc, zc, xp, yp, zp, n[3]; + int sign = 0; + + /* Find the polygon normal. It is important to get a reasonable + * normal even when the polygon is self-intersecting (eg. a bowtie). + * Otherwise, the computed normal could be very tiny, but perpendicular + * to the true plane of the polygon due to numerical noise. Then all + * the triangles would appear to be degenerate and we would incorrectly + * decompose the polygon as a fan (or simply not render it at all). + * + * We use a sum-of-triangles normal algorithm rather than the more + * efficient sum-of-trapezoids method (used in CheckOrientation() + * in normal.c). This lets us explicitly reverse the signed area + * of some triangles to get a reasonable normal in the self-intersecting + * case. + */ + if( ! check ) { + norm[0] = norm[1] = norm[2] = 0.0; + } + + vc = v0 + 1; + xc = vc->coords[0] - v0->coords[0]; + yc = vc->coords[1] - v0->coords[1]; + zc = vc->coords[2] - v0->coords[2]; + while( ++vc < vn ) { + xp = xc; yp = yc; zp = zc; + xc = vc->coords[0] - v0->coords[0]; + yc = vc->coords[1] - v0->coords[1]; + zc = vc->coords[2] - v0->coords[2]; + + /* Compute (vp - v0) cross (vc - v0) */ + n[0] = yp*zc - zp*yc; + n[1] = zp*xc - xp*zc; + n[2] = xp*yc - yp*xc; + + dot = n[0]*norm[0] + n[1]*norm[1] + n[2]*norm[2]; + if( ! check ) { + /* Reverse the contribution of back-facing triangles to get + * a reasonable normal for self-intersecting polygons (see above) + */ + if( dot >= 0 ) { + norm[0] += n[0]; norm[1] += n[1]; norm[2] += n[2]; + } else { + norm[0] -= n[0]; norm[1] -= n[1]; norm[2] -= n[2]; + } + } else if( dot != 0 ) { + /* Check the new orientation for consistency with previous triangles */ + if( dot > 0 ) { + if( sign < 0 ) return SIGN_INCONSISTENT; + sign = 1; + } else { + if( sign > 0 ) return SIGN_INCONSISTENT; + sign = -1; + } + } + } + return sign; +} + +/* __gl_renderCache( tess ) takes a single contour and tries to render it + * as a triangle fan. This handles convex polygons, as well as some + * non-convex polygons if we get lucky. + * + * Returns TRUE if the polygon was successfully rendered. The rendering + * output is provided as callbacks (see the api). + */ +GLboolean __gl_renderCache( GLUtesselator *tess ) +{ + CachedVertex *v0 = tess->cache; + CachedVertex *vn = v0 + tess->cacheCount; + CachedVertex *vc; + GLdouble norm[3]; + int sign; + + if( tess->cacheCount < 3 ) { + /* Degenerate contour -- no output */ + return TRUE; + } + + norm[0] = tess->normal[0]; + norm[1] = tess->normal[1]; + norm[2] = tess->normal[2]; + if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) { + ComputeNormal( tess, norm, FALSE ); + } + + sign = ComputeNormal( tess, norm, TRUE ); + if( sign == SIGN_INCONSISTENT ) { + /* Fan triangles did not have a consistent orientation */ + return FALSE; + } + if( sign == 0 ) { + /* All triangles were degenerate */ + return TRUE; + } + + /* Make sure we do the right thing for each winding rule */ + switch( tess->windingRule ) { + case GLU_TESS_WINDING_ODD: + case GLU_TESS_WINDING_NONZERO: + break; + case GLU_TESS_WINDING_POSITIVE: + if( sign < 0 ) return TRUE; + break; + case GLU_TESS_WINDING_NEGATIVE: + if( sign > 0 ) return TRUE; + break; + case GLU_TESS_WINDING_ABS_GEQ_TWO: + return TRUE; + } + + CALL_BEGIN_OR_BEGIN_DATA( tess->boundaryOnly ? GL_LINE_LOOP + : (tess->cacheCount > 3) ? GL_TRIANGLE_FAN + : GL_TRIANGLES ); + + CALL_VERTEX_OR_VERTEX_DATA( v0->data ); + if( sign > 0 ) { + for( vc = v0+1; vc < vn; ++vc ) { + CALL_VERTEX_OR_VERTEX_DATA( vc->data ); + } + } else { + for( vc = vn-1; vc > v0; --vc ) { + CALL_VERTEX_OR_VERTEX_DATA( vc->data ); + } + } + CALL_END_OR_END_DATA(); + return TRUE; +} diff --git a/deps/libtess/render.h b/deps/libtess/render.h new file mode 100644 index 0000000000..956569bb77 --- /dev/null +++ b/deps/libtess/render.h @@ -0,0 +1,59 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/render.h,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#ifndef __render_h_ +#define __render_h_ + +#include "mesh.h" + +/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle + * fans, strips, and separate triangles. A substantial effort is made + * to use as few rendering primitives as possible (ie. to make the fans + * and strips as large as possible). + * + * The rendering output is provided as callbacks (see the api). + */ +void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh ); +void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh ); + +GLboolean __gl_renderCache( GLUtesselator *tess ); + +#endif diff --git a/deps/libtess/sweep.c b/deps/libtess/sweep.c new file mode 100644 index 0000000000..1cfd7788f7 --- /dev/null +++ b/deps/libtess/sweep.c @@ -0,0 +1,1362 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include +#include /* longjmp */ +#include /* LONG_MAX */ + +#include "mesh.h" +#include "geom.h" +#include "tess.h" +#include "dict.h" +#include "priorityq.h" +#include "memalloc.h" +#include "sweep.h" + +#define TRUE 1 +#define FALSE 0 + +#ifdef FOR_TRITE_TEST_PROGRAM +extern void DebugEvent( GLUtesselator *tess ); +#else +#define DebugEvent( tess ) +#endif + +/* + * Invariants for the Edge Dictionary. + * - each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2) + * at any valid location of the sweep event + * - if EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2 + * share a common endpoint + * - for each e, e->Dst has been processed, but not e->Org + * - each edge e satisfies VertLeq(e->Dst,event) && VertLeq(event,e->Org) + * where "event" is the current sweep line event. + * - no edge e has zero length + * + * Invariants for the Mesh (the processed portion). + * - the portion of the mesh left of the sweep line is a planar graph, + * ie. there is *some* way to embed it in the plane + * - no processed edge has zero length + * - no two processed vertices have identical coordinates + * - each "inside" region is monotone, ie. can be broken into two chains + * of monotonically increasing vertices according to VertLeq(v1,v2) + * - a non-invariant: these chains may intersect (very slightly) + * + * Invariants for the Sweep. + * - if none of the edges incident to the event vertex have an activeRegion + * (ie. none of these edges are in the edge dictionary), then the vertex + * has only right-going edges. + * - if an edge is marked "fixUpperEdge" (it is a temporary edge introduced + * by ConnectRightVertex), then it is the only right-going edge from + * its associated vertex. (This says that these edges exist only + * when it is necessary.) + */ + +#undef MAX +#undef MIN +#define MAX(x,y) ((x) >= (y) ? (x) : (y)) +#define MIN(x,y) ((x) <= (y) ? (x) : (y)) + +/* When we merge two edges into one, we need to compute the combined + * winding of the new edge. + */ +#define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \ + eDst->Sym->winding += eSrc->Sym->winding) + +static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent ); +static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp ); +static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp ); + +static int EdgeLeq( GLUtesselator *tess, ActiveRegion *reg1, + ActiveRegion *reg2 ) +/* + * Both edges must be directed from right to left (this is the canonical + * direction for the upper edge of each region). + * + * The strategy is to evaluate a "t" value for each edge at the + * current sweep line position, given by tess->event. The calculations + * are designed to be very stable, but of course they are not perfect. + * + * Special case: if both edge destinations are at the sweep event, + * we sort the edges by slope (they would otherwise compare equally). + */ +{ + GLUvertex *event = tess->event; + GLUhalfEdge *e1, *e2; + GLdouble t1, t2; + + e1 = reg1->eUp; + e2 = reg2->eUp; + + if( e1->Dst == event ) { + if( e2->Dst == event ) { + /* Two edges right of the sweep line which meet at the sweep event. + * Sort them by slope. + */ + if( VertLeq( e1->Org, e2->Org )) { + return EdgeSign( e2->Dst, e1->Org, e2->Org ) <= 0; + } + return EdgeSign( e1->Dst, e2->Org, e1->Org ) >= 0; + } + return EdgeSign( e2->Dst, event, e2->Org ) <= 0; + } + if( e2->Dst == event ) { + return EdgeSign( e1->Dst, event, e1->Org ) >= 0; + } + + /* General case - compute signed distance *from* e1, e2 to event */ + t1 = EdgeEval( e1->Dst, event, e1->Org ); + t2 = EdgeEval( e2->Dst, event, e2->Org ); + return (t1 >= t2); +} + + +static void DeleteRegion( GLUtesselator *tess, ActiveRegion *reg ) +{ + if( reg->fixUpperEdge ) { + /* It was created with zero winding number, so it better be + * deleted with zero winding number (ie. it better not get merged + * with a real edge). + */ + assert( reg->eUp->winding == 0 ); + } + reg->eUp->activeRegion = NULL; + dictDelete( tess->dict, reg->nodeUp ); /* __gl_dictListDelete */ + memFree( reg ); +} + + +static int FixUpperEdge( ActiveRegion *reg, GLUhalfEdge *newEdge ) +/* + * Replace an upper edge which needs fixing (see ConnectRightVertex). + */ +{ + assert( reg->fixUpperEdge ); + if ( !__gl_meshDelete( reg->eUp ) ) return 0; + reg->fixUpperEdge = FALSE; + reg->eUp = newEdge; + newEdge->activeRegion = reg; + + return 1; +} + +static ActiveRegion *TopLeftRegion( ActiveRegion *reg ) +{ + GLUvertex *org = reg->eUp->Org; + GLUhalfEdge *e; + + /* Find the region above the uppermost edge with the same origin */ + do { + reg = RegionAbove( reg ); + } while( reg->eUp->Org == org ); + + /* If the edge above was a temporary edge introduced by ConnectRightVertex, + * now is the time to fix it. + */ + if( reg->fixUpperEdge ) { + e = __gl_meshConnect( RegionBelow(reg)->eUp->Sym, reg->eUp->Lnext ); + if (e == NULL) return NULL; + if ( !FixUpperEdge( reg, e ) ) return NULL; + reg = RegionAbove( reg ); + } + return reg; +} + +static ActiveRegion *TopRightRegion( ActiveRegion *reg ) +{ + GLUvertex *dst = reg->eUp->Dst; + + /* Find the region above the uppermost edge with the same destination */ + do { + reg = RegionAbove( reg ); + } while( reg->eUp->Dst == dst ); + return reg; +} + +static ActiveRegion *AddRegionBelow( GLUtesselator *tess, + ActiveRegion *regAbove, + GLUhalfEdge *eNewUp ) +/* + * Add a new active region to the sweep line, *somewhere* below "regAbove" + * (according to where the new edge belongs in the sweep-line dictionary). + * The upper edge of the new region will be "eNewUp". + * Winding number and "inside" flag are not updated. + */ +{ + ActiveRegion *regNew = (ActiveRegion *)memAlloc( sizeof( ActiveRegion )); + if (regNew == NULL) longjmp(tess->env,1); + + regNew->eUp = eNewUp; + /* __gl_dictListInsertBefore */ + regNew->nodeUp = dictInsertBefore( tess->dict, regAbove->nodeUp, regNew ); + if (regNew->nodeUp == NULL) longjmp(tess->env,1); + regNew->fixUpperEdge = FALSE; + regNew->sentinel = FALSE; + regNew->dirty = FALSE; + + eNewUp->activeRegion = regNew; + return regNew; +} + +static GLboolean IsWindingInside( GLUtesselator *tess, int n ) +{ + switch( tess->windingRule ) { + case GLU_TESS_WINDING_ODD: + return (n & 1); + case GLU_TESS_WINDING_NONZERO: + return (n != 0); + case GLU_TESS_WINDING_POSITIVE: + return (n > 0); + case GLU_TESS_WINDING_NEGATIVE: + return (n < 0); + case GLU_TESS_WINDING_ABS_GEQ_TWO: + return (n >= 2) || (n <= -2); + } + /*LINTED*/ + assert( FALSE ); + /*NOTREACHED*/ + return GL_FALSE; /* avoid compiler complaints */ +} + + +static void ComputeWinding( GLUtesselator *tess, ActiveRegion *reg ) +{ + reg->windingNumber = RegionAbove(reg)->windingNumber + reg->eUp->winding; + reg->inside = IsWindingInside( tess, reg->windingNumber ); +} + + +static void FinishRegion( GLUtesselator *tess, ActiveRegion *reg ) +/* + * Delete a region from the sweep line. This happens when the upper + * and lower chains of a region meet (at a vertex on the sweep line). + * The "inside" flag is copied to the appropriate mesh face (we could + * not do this before -- since the structure of the mesh is always + * changing, this face may not have even existed until now). + */ +{ + GLUhalfEdge *e = reg->eUp; + GLUface *f = e->Lface; + + f->inside = reg->inside; + f->anEdge = e; /* optimization for __gl_meshTessellateMonoRegion() */ + DeleteRegion( tess, reg ); +} + + +static GLUhalfEdge *FinishLeftRegions( GLUtesselator *tess, + ActiveRegion *regFirst, ActiveRegion *regLast ) +/* + * We are given a vertex with one or more left-going edges. All affected + * edges should be in the edge dictionary. Starting at regFirst->eUp, + * we walk down deleting all regions where both edges have the same + * origin vOrg. At the same time we copy the "inside" flag from the + * active region to the face, since at this point each face will belong + * to at most one region (this was not necessarily true until this point + * in the sweep). The walk stops at the region above regLast; if regLast + * is NULL we walk as far as possible. At the same time we relink the + * mesh if necessary, so that the ordering of edges around vOrg is the + * same as in the dictionary. + */ +{ + ActiveRegion *reg, *regPrev; + GLUhalfEdge *e, *ePrev; + + regPrev = regFirst; + ePrev = regFirst->eUp; + while( regPrev != regLast ) { + regPrev->fixUpperEdge = FALSE; /* placement was OK */ + reg = RegionBelow( regPrev ); + e = reg->eUp; + if( e->Org != ePrev->Org ) { + if( ! reg->fixUpperEdge ) { + /* Remove the last left-going edge. Even though there are no further + * edges in the dictionary with this origin, there may be further + * such edges in the mesh (if we are adding left edges to a vertex + * that has already been processed). Thus it is important to call + * FinishRegion rather than just DeleteRegion. + */ + FinishRegion( tess, regPrev ); + break; + } + /* If the edge below was a temporary edge introduced by + * ConnectRightVertex, now is the time to fix it. + */ + e = __gl_meshConnect( ePrev->Lprev, e->Sym ); + if (e == NULL) longjmp(tess->env,1); + if ( !FixUpperEdge( reg, e ) ) longjmp(tess->env,1); + } + + /* Relink edges so that ePrev->Onext == e */ + if( ePrev->Onext != e ) { + if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1); + if ( !__gl_meshSplice( ePrev, e ) ) longjmp(tess->env,1); + } + FinishRegion( tess, regPrev ); /* may change reg->eUp */ + ePrev = reg->eUp; + regPrev = reg; + } + return ePrev; +} + + +static void AddRightEdges( GLUtesselator *tess, ActiveRegion *regUp, + GLUhalfEdge *eFirst, GLUhalfEdge *eLast, GLUhalfEdge *eTopLeft, + GLboolean cleanUp ) +/* + * Purpose: insert right-going edges into the edge dictionary, and update + * winding numbers and mesh connectivity appropriately. All right-going + * edges share a common origin vOrg. Edges are inserted CCW starting at + * eFirst; the last edge inserted is eLast->Oprev. If vOrg has any + * left-going edges already processed, then eTopLeft must be the edge + * such that an imaginary upward vertical segment from vOrg would be + * contained between eTopLeft->Oprev and eTopLeft; otherwise eTopLeft + * should be NULL. + */ +{ + ActiveRegion *reg, *regPrev; + GLUhalfEdge *e, *ePrev; + int firstTime = TRUE; + + /* Insert the new right-going edges in the dictionary */ + e = eFirst; + do { + assert( VertLeq( e->Org, e->Dst )); + AddRegionBelow( tess, regUp, e->Sym ); + e = e->Onext; + } while ( e != eLast ); + + /* Walk *all* right-going edges from e->Org, in the dictionary order, + * updating the winding numbers of each region, and re-linking the mesh + * edges to match the dictionary ordering (if necessary). + */ + if( eTopLeft == NULL ) { + eTopLeft = RegionBelow( regUp )->eUp->Rprev; + } + regPrev = regUp; + ePrev = eTopLeft; + for( ;; ) { + reg = RegionBelow( regPrev ); + e = reg->eUp->Sym; + if( e->Org != ePrev->Org ) break; + + if( e->Onext != ePrev ) { + /* Unlink e from its current position, and relink below ePrev */ + if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1); + if ( !__gl_meshSplice( ePrev->Oprev, e ) ) longjmp(tess->env,1); + } + /* Compute the winding number and "inside" flag for the new regions */ + reg->windingNumber = regPrev->windingNumber - e->winding; + reg->inside = IsWindingInside( tess, reg->windingNumber ); + + /* Check for two outgoing edges with same slope -- process these + * before any intersection tests (see example in __gl_computeInterior). + */ + regPrev->dirty = TRUE; + if( ! firstTime && CheckForRightSplice( tess, regPrev )) { + AddWinding( e, ePrev ); + DeleteRegion( tess, regPrev ); + if ( !__gl_meshDelete( ePrev ) ) longjmp(tess->env,1); + } + firstTime = FALSE; + regPrev = reg; + ePrev = e; + } + regPrev->dirty = TRUE; + assert( regPrev->windingNumber - e->winding == reg->windingNumber ); + + if( cleanUp ) { + /* Check for intersections between newly adjacent edges. */ + WalkDirtyRegions( tess, regPrev ); + } +} + + +static void CallCombine( GLUtesselator *tess, GLUvertex *isect, + void *data[4], GLfloat weights[4], int needed ) +{ + GLdouble coords[3]; + + /* Copy coord data in case the callback changes it. */ + coords[0] = isect->coords[0]; + coords[1] = isect->coords[1]; + coords[2] = isect->coords[2]; + + isect->data = NULL; + CALL_COMBINE_OR_COMBINE_DATA( coords, data, weights, &isect->data ); + if( isect->data == NULL ) { + if( ! needed ) { + isect->data = data[0]; + } else if( ! tess->fatalError ) { + /* The only way fatal error is when two edges are found to intersect, + * but the user has not provided the callback necessary to handle + * generated intersection points. + */ + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_NEED_COMBINE_CALLBACK ); + tess->fatalError = TRUE; + } + } +} + +static void SpliceMergeVertices( GLUtesselator *tess, GLUhalfEdge *e1, + GLUhalfEdge *e2 ) +/* + * Two vertices with idential coordinates are combined into one. + * e1->Org is kept, while e2->Org is discarded. + */ +{ + void *data[4] = { NULL, NULL, NULL, NULL }; + GLfloat weights[4] = { 0.5, 0.5, 0.0, 0.0 }; + + data[0] = e1->Org->data; + data[1] = e2->Org->data; + CallCombine( tess, e1->Org, data, weights, FALSE ); + if ( !__gl_meshSplice( e1, e2 ) ) longjmp(tess->env,1); +} + +static void VertexWeights( GLUvertex *isect, GLUvertex *org, GLUvertex *dst, + GLfloat *weights ) +/* + * Find some weights which describe how the intersection vertex is + * a linear combination of "org" and "dest". Each of the two edges + * which generated "isect" is allocated 50% of the weight; each edge + * splits the weight between its org and dst according to the + * relative distance to "isect". + */ +{ + GLdouble t1 = VertL1dist( org, isect ); + GLdouble t2 = VertL1dist( dst, isect ); + + weights[0] = 0.5 * t2 / (t1 + t2); + weights[1] = 0.5 * t1 / (t1 + t2); + isect->coords[0] += weights[0]*org->coords[0] + weights[1]*dst->coords[0]; + isect->coords[1] += weights[0]*org->coords[1] + weights[1]*dst->coords[1]; + isect->coords[2] += weights[0]*org->coords[2] + weights[1]*dst->coords[2]; +} + + +static void GetIntersectData( GLUtesselator *tess, GLUvertex *isect, + GLUvertex *orgUp, GLUvertex *dstUp, + GLUvertex *orgLo, GLUvertex *dstLo ) +/* + * We've computed a new intersection point, now we need a "data" pointer + * from the user so that we can refer to this new vertex in the + * rendering callbacks. + */ +{ + void *data[4]; + GLfloat weights[4]; + + data[0] = orgUp->data; + data[1] = dstUp->data; + data[2] = orgLo->data; + data[3] = dstLo->data; + + isect->coords[0] = isect->coords[1] = isect->coords[2] = 0; + VertexWeights( isect, orgUp, dstUp, &weights[0] ); + VertexWeights( isect, orgLo, dstLo, &weights[2] ); + + CallCombine( tess, isect, data, weights, TRUE ); +} + +static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp ) +/* + * Check the upper and lower edge of "regUp", to make sure that the + * eUp->Org is above eLo, or eLo->Org is below eUp (depending on which + * origin is leftmost). + * + * The main purpose is to splice right-going edges with the same + * dest vertex and nearly identical slopes (ie. we can't distinguish + * the slopes numerically). However the splicing can also help us + * to recover from numerical errors. For example, suppose at one + * point we checked eUp and eLo, and decided that eUp->Org is barely + * above eLo. Then later, we split eLo into two edges (eg. from + * a splice operation like this one). This can change the result of + * our test so that now eUp->Org is incident to eLo, or barely below it. + * We must correct this condition to maintain the dictionary invariants. + * + * One possibility is to check these edges for intersection again + * (ie. CheckForIntersect). This is what we do if possible. However + * CheckForIntersect requires that tess->event lies between eUp and eLo, + * so that it has something to fall back on when the intersection + * calculation gives us an unusable answer. So, for those cases where + * we can't check for intersection, this routine fixes the problem + * by just splicing the offending vertex into the other edge. + * This is a guaranteed solution, no matter how degenerate things get. + * Basically this is a combinatorial solution to a numerical problem. + */ +{ + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp = regUp->eUp; + GLUhalfEdge *eLo = regLo->eUp; + + if( VertLeq( eUp->Org, eLo->Org )) { + if( EdgeSign( eLo->Dst, eUp->Org, eLo->Org ) > 0 ) return FALSE; + + /* eUp->Org appears to be below eLo */ + if( ! VertEq( eUp->Org, eLo->Org )) { + /* Splice eUp->Org into eLo */ + if ( __gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eUp, eLo->Oprev ) ) longjmp(tess->env,1); + regUp->dirty = regLo->dirty = TRUE; + + } else if( eUp->Org != eLo->Org ) { + /* merge the two vertices, discarding eUp->Org */ + pqDelete( tess->pq, eUp->Org->pqHandle ); /* __gl_pqSortDelete */ + SpliceMergeVertices( tess, eLo->Oprev, eUp ); + } + } else { + if( EdgeSign( eUp->Dst, eLo->Org, eUp->Org ) < 0 ) return FALSE; + + /* eLo->Org appears to be above eUp, so splice eLo->Org into eUp */ + RegionAbove(regUp)->dirty = regUp->dirty = TRUE; + if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1); + } + return TRUE; +} + +static int CheckForLeftSplice( GLUtesselator *tess, ActiveRegion *regUp ) +/* + * Check the upper and lower edge of "regUp", to make sure that the + * eUp->Dst is above eLo, or eLo->Dst is below eUp (depending on which + * destination is rightmost). + * + * Theoretically, this should always be true. However, splitting an edge + * into two pieces can change the results of previous tests. For example, + * suppose at one point we checked eUp and eLo, and decided that eUp->Dst + * is barely above eLo. Then later, we split eLo into two edges (eg. from + * a splice operation like this one). This can change the result of + * the test so that now eUp->Dst is incident to eLo, or barely below it. + * We must correct this condition to maintain the dictionary invariants + * (otherwise new edges might get inserted in the wrong place in the + * dictionary, and bad stuff will happen). + * + * We fix the problem by just splicing the offending vertex into the + * other edge. + */ +{ + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp = regUp->eUp; + GLUhalfEdge *eLo = regLo->eUp; + GLUhalfEdge *e; + + assert( ! VertEq( eUp->Dst, eLo->Dst )); + + if( VertLeq( eUp->Dst, eLo->Dst )) { + if( EdgeSign( eUp->Dst, eLo->Dst, eUp->Org ) < 0 ) return FALSE; + + /* eLo->Dst is above eUp, so splice eLo->Dst into eUp */ + RegionAbove(regUp)->dirty = regUp->dirty = TRUE; + e = __gl_meshSplitEdge( eUp ); + if (e == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eLo->Sym, e ) ) longjmp(tess->env,1); + e->Lface->inside = regUp->inside; + } else { + if( EdgeSign( eLo->Dst, eUp->Dst, eLo->Org ) > 0 ) return FALSE; + + /* eUp->Dst is below eLo, so splice eUp->Dst into eLo */ + regUp->dirty = regLo->dirty = TRUE; + e = __gl_meshSplitEdge( eLo ); + if (e == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eUp->Lnext, eLo->Sym ) ) longjmp(tess->env,1); + e->Rface->inside = regUp->inside; + } + return TRUE; +} + + +static int CheckForIntersect( GLUtesselator *tess, ActiveRegion *regUp ) +/* + * Check the upper and lower edges of the given region to see if + * they intersect. If so, create the intersection and add it + * to the data structures. + * + * Returns TRUE if adding the new intersection resulted in a recursive + * call to AddRightEdges(); in this case all "dirty" regions have been + * checked for intersections, and possibly regUp has been deleted. + */ +{ + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp = regUp->eUp; + GLUhalfEdge *eLo = regLo->eUp; + GLUvertex *orgUp = eUp->Org; + GLUvertex *orgLo = eLo->Org; + GLUvertex *dstUp = eUp->Dst; + GLUvertex *dstLo = eLo->Dst; + GLdouble tMinUp, tMaxLo; + GLUvertex isect, *orgMin; + GLUhalfEdge *e; + + assert( ! VertEq( dstLo, dstUp )); + assert( EdgeSign( dstUp, tess->event, orgUp ) <= 0 ); + assert( EdgeSign( dstLo, tess->event, orgLo ) >= 0 ); + assert( orgUp != tess->event && orgLo != tess->event ); + assert( ! regUp->fixUpperEdge && ! regLo->fixUpperEdge ); + + if( orgUp == orgLo ) return FALSE; /* right endpoints are the same */ + + tMinUp = MIN( orgUp->t, dstUp->t ); + tMaxLo = MAX( orgLo->t, dstLo->t ); + if( tMinUp > tMaxLo ) return FALSE; /* t ranges do not overlap */ + + if( VertLeq( orgUp, orgLo )) { + if( EdgeSign( dstLo, orgUp, orgLo ) > 0 ) return FALSE; + } else { + if( EdgeSign( dstUp, orgLo, orgUp ) < 0 ) return FALSE; + } + + /* At this point the edges intersect, at least marginally */ + DebugEvent( tess ); + + __gl_edgeIntersect( dstUp, orgUp, dstLo, orgLo, &isect ); + /* The following properties are guaranteed: */ + assert( MIN( orgUp->t, dstUp->t ) <= isect.t ); + assert( isect.t <= MAX( orgLo->t, dstLo->t )); + assert( MIN( dstLo->s, dstUp->s ) <= isect.s ); + assert( isect.s <= MAX( orgLo->s, orgUp->s )); + + if( VertLeq( &isect, tess->event )) { + /* The intersection point lies slightly to the left of the sweep line, + * so move it until it''s slightly to the right of the sweep line. + * (If we had perfect numerical precision, this would never happen + * in the first place). The easiest and safest thing to do is + * replace the intersection by tess->event. + */ + isect.s = tess->event->s; + isect.t = tess->event->t; + } + /* Similarly, if the computed intersection lies to the right of the + * rightmost origin (which should rarely happen), it can cause + * unbelievable inefficiency on sufficiently degenerate inputs. + * (If you have the test program, try running test54.d with the + * "X zoom" option turned on). + */ + orgMin = VertLeq( orgUp, orgLo ) ? orgUp : orgLo; + if( VertLeq( orgMin, &isect )) { + isect.s = orgMin->s; + isect.t = orgMin->t; + } + + if( VertEq( &isect, orgUp ) || VertEq( &isect, orgLo )) { + /* Easy case -- intersection at one of the right endpoints */ + (void) CheckForRightSplice( tess, regUp ); + return FALSE; + } + + if( (! VertEq( dstUp, tess->event ) + && EdgeSign( dstUp, tess->event, &isect ) >= 0) + || (! VertEq( dstLo, tess->event ) + && EdgeSign( dstLo, tess->event, &isect ) <= 0 )) + { + /* Very unusual -- the new upper or lower edge would pass on the + * wrong side of the sweep event, or through it. This can happen + * due to very small numerical errors in the intersection calculation. + */ + if( dstLo == tess->event ) { + /* Splice dstLo into eUp, and process the new region(s) */ + if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eLo->Sym, eUp ) ) longjmp(tess->env,1); + regUp = TopLeftRegion( regUp ); + if (regUp == NULL) longjmp(tess->env,1); + eUp = RegionBelow(regUp)->eUp; + FinishLeftRegions( tess, RegionBelow(regUp), regLo ); + AddRightEdges( tess, regUp, eUp->Oprev, eUp, eUp, TRUE ); + return TRUE; + } + if( dstUp == tess->event ) { + /* Splice dstUp into eLo, and process the new region(s) */ + if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eUp->Lnext, eLo->Oprev ) ) longjmp(tess->env,1); + regLo = regUp; + regUp = TopRightRegion( regUp ); + e = RegionBelow(regUp)->eUp->Rprev; + regLo->eUp = eLo->Oprev; + eLo = FinishLeftRegions( tess, regLo, NULL ); + AddRightEdges( tess, regUp, eLo->Onext, eUp->Rprev, e, TRUE ); + return TRUE; + } + /* Special case: called from ConnectRightVertex. If either + * edge passes on the wrong side of tess->event, split it + * (and wait for ConnectRightVertex to splice it appropriately). + */ + if( EdgeSign( dstUp, tess->event, &isect ) >= 0 ) { + RegionAbove(regUp)->dirty = regUp->dirty = TRUE; + if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); + eUp->Org->s = tess->event->s; + eUp->Org->t = tess->event->t; + } + if( EdgeSign( dstLo, tess->event, &isect ) <= 0 ) { + regUp->dirty = regLo->dirty = TRUE; + if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); + eLo->Org->s = tess->event->s; + eLo->Org->t = tess->event->t; + } + /* leave the rest for ConnectRightVertex */ + return FALSE; + } + + /* General case -- split both edges, splice into new vertex. + * When we do the splice operation, the order of the arguments is + * arbitrary as far as correctness goes. However, when the operation + * creates a new face, the work done is proportional to the size of + * the new face. We expect the faces in the processed part of + * the mesh (ie. eUp->Lface) to be smaller than the faces in the + * unprocessed original contours (which will be eLo->Oprev->Lface). + */ + if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); + if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1); + eUp->Org->s = isect.s; + eUp->Org->t = isect.t; + eUp->Org->pqHandle = pqInsert( tess->pq, eUp->Org ); /* __gl_pqSortInsert */ + if (eUp->Org->pqHandle == LONG_MAX) { + pqDeletePriorityQ(tess->pq); /* __gl_pqSortDeletePriorityQ */ + tess->pq = NULL; + longjmp(tess->env,1); + } + GetIntersectData( tess, eUp->Org, orgUp, dstUp, orgLo, dstLo ); + RegionAbove(regUp)->dirty = regUp->dirty = regLo->dirty = TRUE; + return FALSE; +} + +static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp ) +/* + * When the upper or lower edge of any region changes, the region is + * marked "dirty". This routine walks through all the dirty regions + * and makes sure that the dictionary invariants are satisfied + * (see the comments at the beginning of this file). Of course + * new dirty regions can be created as we make changes to restore + * the invariants. + */ +{ + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp, *eLo; + + for( ;; ) { + /* Find the lowest dirty region (we walk from the bottom up). */ + while( regLo->dirty ) { + regUp = regLo; + regLo = RegionBelow(regLo); + } + if( ! regUp->dirty ) { + regLo = regUp; + regUp = RegionAbove( regUp ); + if( regUp == NULL || ! regUp->dirty ) { + /* We've walked all the dirty regions */ + return; + } + } + regUp->dirty = FALSE; + eUp = regUp->eUp; + eLo = regLo->eUp; + + if( eUp->Dst != eLo->Dst ) { + /* Check that the edge ordering is obeyed at the Dst vertices. */ + if( CheckForLeftSplice( tess, regUp )) { + + /* If the upper or lower edge was marked fixUpperEdge, then + * we no longer need it (since these edges are needed only for + * vertices which otherwise have no right-going edges). + */ + if( regLo->fixUpperEdge ) { + DeleteRegion( tess, regLo ); + if ( !__gl_meshDelete( eLo ) ) longjmp(tess->env,1); + regLo = RegionBelow( regUp ); + eLo = regLo->eUp; + } else if( regUp->fixUpperEdge ) { + DeleteRegion( tess, regUp ); + if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1); + regUp = RegionAbove( regLo ); + eUp = regUp->eUp; + } + } + } + if( eUp->Org != eLo->Org ) { + if( eUp->Dst != eLo->Dst + && ! regUp->fixUpperEdge && ! regLo->fixUpperEdge + && (eUp->Dst == tess->event || eLo->Dst == tess->event) ) + { + /* When all else fails in CheckForIntersect(), it uses tess->event + * as the intersection location. To make this possible, it requires + * that tess->event lie between the upper and lower edges, and also + * that neither of these is marked fixUpperEdge (since in the worst + * case it might splice one of these edges into tess->event, and + * violate the invariant that fixable edges are the only right-going + * edge from their associated vertex). + */ + if( CheckForIntersect( tess, regUp )) { + /* WalkDirtyRegions() was called recursively; we're done */ + return; + } + } else { + /* Even though we can't use CheckForIntersect(), the Org vertices + * may violate the dictionary edge ordering. Check and correct this. + */ + (void) CheckForRightSplice( tess, regUp ); + } + } + if( eUp->Org == eLo->Org && eUp->Dst == eLo->Dst ) { + /* A degenerate loop consisting of only two edges -- delete it. */ + AddWinding( eLo, eUp ); + DeleteRegion( tess, regUp ); + if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1); + regUp = RegionAbove( regLo ); + } + } +} + + +static void ConnectRightVertex( GLUtesselator *tess, ActiveRegion *regUp, + GLUhalfEdge *eBottomLeft ) +/* + * Purpose: connect a "right" vertex vEvent (one where all edges go left) + * to the unprocessed portion of the mesh. Since there are no right-going + * edges, two regions (one above vEvent and one below) are being merged + * into one. "regUp" is the upper of these two regions. + * + * There are two reasons for doing this (adding a right-going edge): + * - if the two regions being merged are "inside", we must add an edge + * to keep them separated (the combined region would not be monotone). + * - in any case, we must leave some record of vEvent in the dictionary, + * so that we can merge vEvent with features that we have not seen yet. + * For example, maybe there is a vertical edge which passes just to + * the right of vEvent; we would like to splice vEvent into this edge. + * + * However, we don't want to connect vEvent to just any vertex. We don''t + * want the new edge to cross any other edges; otherwise we will create + * intersection vertices even when the input data had no self-intersections. + * (This is a bad thing; if the user's input data has no intersections, + * we don't want to generate any false intersections ourselves.) + * + * Our eventual goal is to connect vEvent to the leftmost unprocessed + * vertex of the combined region (the union of regUp and regLo). + * But because of unseen vertices with all right-going edges, and also + * new vertices which may be created by edge intersections, we don''t + * know where that leftmost unprocessed vertex is. In the meantime, we + * connect vEvent to the closest vertex of either chain, and mark the region + * as "fixUpperEdge". This flag says to delete and reconnect this edge + * to the next processed vertex on the boundary of the combined region. + * Quite possibly the vertex we connected to will turn out to be the + * closest one, in which case we won''t need to make any changes. + */ +{ + GLUhalfEdge *eNew; + GLUhalfEdge *eTopLeft = eBottomLeft->Onext; + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp = regUp->eUp; + GLUhalfEdge *eLo = regLo->eUp; + int degenerate = FALSE; + + if( eUp->Dst != eLo->Dst ) { + (void) CheckForIntersect( tess, regUp ); + } + + /* Possible new degeneracies: upper or lower edge of regUp may pass + * through vEvent, or may coincide with new intersection vertex + */ + if( VertEq( eUp->Org, tess->event )) { + if ( !__gl_meshSplice( eTopLeft->Oprev, eUp ) ) longjmp(tess->env,1); + regUp = TopLeftRegion( regUp ); + if (regUp == NULL) longjmp(tess->env,1); + eTopLeft = RegionBelow( regUp )->eUp; + FinishLeftRegions( tess, RegionBelow(regUp), regLo ); + degenerate = TRUE; + } + if( VertEq( eLo->Org, tess->event )) { + if ( !__gl_meshSplice( eBottomLeft, eLo->Oprev ) ) longjmp(tess->env,1); + eBottomLeft = FinishLeftRegions( tess, regLo, NULL ); + degenerate = TRUE; + } + if( degenerate ) { + AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE ); + return; + } + + /* Non-degenerate situation -- need to add a temporary, fixable edge. + * Connect to the closer of eLo->Org, eUp->Org. + */ + if( VertLeq( eLo->Org, eUp->Org )) { + eNew = eLo->Oprev; + } else { + eNew = eUp; + } + eNew = __gl_meshConnect( eBottomLeft->Lprev, eNew ); + if (eNew == NULL) longjmp(tess->env,1); + + /* Prevent cleanup, otherwise eNew might disappear before we've even + * had a chance to mark it as a temporary edge. + */ + AddRightEdges( tess, regUp, eNew, eNew->Onext, eNew->Onext, FALSE ); + eNew->Sym->activeRegion->fixUpperEdge = TRUE; + WalkDirtyRegions( tess, regUp ); +} + +/* Because vertices at exactly the same location are merged together + * before we process the sweep event, some degenerate cases can't occur. + * However if someone eventually makes the modifications required to + * merge features which are close together, the cases below marked + * TOLERANCE_NONZERO will be useful. They were debugged before the + * code to merge identical vertices in the main loop was added. + */ +#define TOLERANCE_NONZERO FALSE + +static void ConnectLeftDegenerate( GLUtesselator *tess, + ActiveRegion *regUp, GLUvertex *vEvent ) +/* + * The event vertex lies exacty on an already-processed edge or vertex. + * Adding the new vertex involves splicing it into the already-processed + * part of the mesh. + */ +{ + GLUhalfEdge *e, *eTopLeft, *eTopRight, *eLast; + ActiveRegion *reg; + + e = regUp->eUp; + if( VertEq( e->Org, vEvent )) { + /* e->Org is an unprocessed vertex - just combine them, and wait + * for e->Org to be pulled from the queue + */ + assert( TOLERANCE_NONZERO ); + SpliceMergeVertices( tess, e, vEvent->anEdge ); + return; + } + + if( ! VertEq( e->Dst, vEvent )) { + /* General case -- splice vEvent into edge e which passes through it */ + if (__gl_meshSplitEdge( e->Sym ) == NULL) longjmp(tess->env,1); + if( regUp->fixUpperEdge ) { + /* This edge was fixable -- delete unused portion of original edge */ + if ( !__gl_meshDelete( e->Onext ) ) longjmp(tess->env,1); + regUp->fixUpperEdge = FALSE; + } + if ( !__gl_meshSplice( vEvent->anEdge, e ) ) longjmp(tess->env,1); + SweepEvent( tess, vEvent ); /* recurse */ + return; + } + + /* vEvent coincides with e->Dst, which has already been processed. + * Splice in the additional right-going edges. + */ + assert( TOLERANCE_NONZERO ); + regUp = TopRightRegion( regUp ); + reg = RegionBelow( regUp ); + eTopRight = reg->eUp->Sym; + eTopLeft = eLast = eTopRight->Onext; + if( reg->fixUpperEdge ) { + /* Here e->Dst has only a single fixable edge going right. + * We can delete it since now we have some real right-going edges. + */ + assert( eTopLeft != eTopRight ); /* there are some left edges too */ + DeleteRegion( tess, reg ); + if ( !__gl_meshDelete( eTopRight ) ) longjmp(tess->env,1); + eTopRight = eTopLeft->Oprev; + } + if ( !__gl_meshSplice( vEvent->anEdge, eTopRight ) ) longjmp(tess->env,1); + if( ! EdgeGoesLeft( eTopLeft )) { + /* e->Dst had no left-going edges -- indicate this to AddRightEdges() */ + eTopLeft = NULL; + } + AddRightEdges( tess, regUp, eTopRight->Onext, eLast, eTopLeft, TRUE ); +} + + +static void ConnectLeftVertex( GLUtesselator *tess, GLUvertex *vEvent ) +/* + * Purpose: connect a "left" vertex (one where both edges go right) + * to the processed portion of the mesh. Let R be the active region + * containing vEvent, and let U and L be the upper and lower edge + * chains of R. There are two possibilities: + * + * - the normal case: split R into two regions, by connecting vEvent to + * the rightmost vertex of U or L lying to the left of the sweep line + * + * - the degenerate case: if vEvent is close enough to U or L, we + * merge vEvent into that edge chain. The subcases are: + * - merging with the rightmost vertex of U or L + * - merging with the active edge of U or L + * - merging with an already-processed portion of U or L + */ +{ + ActiveRegion *regUp, *regLo, *reg; + GLUhalfEdge *eUp, *eLo, *eNew; + ActiveRegion tmp; + + /* assert( vEvent->anEdge->Onext->Onext == vEvent->anEdge ); */ + + /* Get a pointer to the active region containing vEvent */ + tmp.eUp = vEvent->anEdge->Sym; + /* __GL_DICTLISTKEY */ /* __gl_dictListSearch */ + regUp = (ActiveRegion *)dictKey( dictSearch( tess->dict, &tmp )); + regLo = RegionBelow( regUp ); + eUp = regUp->eUp; + eLo = regLo->eUp; + + /* Try merging with U or L first */ + if( EdgeSign( eUp->Dst, vEvent, eUp->Org ) == 0 ) { + ConnectLeftDegenerate( tess, regUp, vEvent ); + return; + } + + /* Connect vEvent to rightmost processed vertex of either chain. + * e->Dst is the vertex that we will connect to vEvent. + */ + reg = VertLeq( eLo->Dst, eUp->Dst ) ? regUp : regLo; + + if( regUp->inside || reg->fixUpperEdge) { + if( reg == regUp ) { + eNew = __gl_meshConnect( vEvent->anEdge->Sym, eUp->Lnext ); + if (eNew == NULL) longjmp(tess->env,1); + } else { + GLUhalfEdge *tempHalfEdge= __gl_meshConnect( eLo->Dnext, vEvent->anEdge); + if (tempHalfEdge == NULL) longjmp(tess->env,1); + + eNew = tempHalfEdge->Sym; + } + if( reg->fixUpperEdge ) { + if ( !FixUpperEdge( reg, eNew ) ) longjmp(tess->env,1); + } else { + ComputeWinding( tess, AddRegionBelow( tess, regUp, eNew )); + } + SweepEvent( tess, vEvent ); + } else { + /* The new vertex is in a region which does not belong to the polygon. + * We don''t need to connect this vertex to the rest of the mesh. + */ + AddRightEdges( tess, regUp, vEvent->anEdge, vEvent->anEdge, NULL, TRUE ); + } +} + + +static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent ) +/* + * Does everything necessary when the sweep line crosses a vertex. + * Updates the mesh and the edge dictionary. + */ +{ + ActiveRegion *regUp, *reg; + GLUhalfEdge *e, *eTopLeft, *eBottomLeft; + + tess->event = vEvent; /* for access in EdgeLeq() */ + DebugEvent( tess ); + + /* Check if this vertex is the right endpoint of an edge that is + * already in the dictionary. In this case we don't need to waste + * time searching for the location to insert new edges. + */ + e = vEvent->anEdge; + while( e->activeRegion == NULL ) { + e = e->Onext; + if( e == vEvent->anEdge ) { + /* All edges go right -- not incident to any processed edges */ + ConnectLeftVertex( tess, vEvent ); + return; + } + } + + /* Processing consists of two phases: first we "finish" all the + * active regions where both the upper and lower edges terminate + * at vEvent (ie. vEvent is closing off these regions). + * We mark these faces "inside" or "outside" the polygon according + * to their winding number, and delete the edges from the dictionary. + * This takes care of all the left-going edges from vEvent. + */ + regUp = TopLeftRegion( e->activeRegion ); + if (regUp == NULL) longjmp(tess->env,1); + reg = RegionBelow( regUp ); + eTopLeft = reg->eUp; + eBottomLeft = FinishLeftRegions( tess, reg, NULL ); + + /* Next we process all the right-going edges from vEvent. This + * involves adding the edges to the dictionary, and creating the + * associated "active regions" which record information about the + * regions between adjacent dictionary edges. + */ + if( eBottomLeft->Onext == eTopLeft ) { + /* No right-going edges -- add a temporary "fixable" edge */ + ConnectRightVertex( tess, regUp, eBottomLeft ); + } else { + AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE ); + } +} + + +/* Make the sentinel coordinates big enough that they will never be + * merged with real input features. (Even with the largest possible + * input contour and the maximum tolerance of 1.0, no merging will be + * done with coordinates larger than 3 * GLU_TESS_MAX_COORD). + */ +#define SENTINEL_COORD (4 * GLU_TESS_MAX_COORD) + +static void AddSentinel( GLUtesselator *tess, GLdouble t ) +/* + * We add two sentinel edges above and below all other edges, + * to avoid special cases at the top and bottom. + */ +{ + GLUhalfEdge *e; + ActiveRegion *reg = (ActiveRegion *)memAlloc( sizeof( ActiveRegion )); + if (reg == NULL) longjmp(tess->env,1); + + e = __gl_meshMakeEdge( tess->mesh ); + if (e == NULL) longjmp(tess->env,1); + + e->Org->s = SENTINEL_COORD; + e->Org->t = t; + e->Dst->s = -SENTINEL_COORD; + e->Dst->t = t; + tess->event = e->Dst; /* initialize it */ + + reg->eUp = e; + reg->windingNumber = 0; + reg->inside = FALSE; + reg->fixUpperEdge = FALSE; + reg->sentinel = TRUE; + reg->dirty = FALSE; + reg->nodeUp = dictInsert( tess->dict, reg ); /* __gl_dictListInsertBefore */ + if (reg->nodeUp == NULL) longjmp(tess->env,1); +} + + +static void InitEdgeDict( GLUtesselator *tess ) +/* + * We maintain an ordering of edge intersections with the sweep line. + * This order is maintained in a dynamic dictionary. + */ +{ + /* __gl_dictListNewDict */ + tess->dict = dictNewDict( tess, (int (*)(void *, DictKey, DictKey)) EdgeLeq ); + if (tess->dict == NULL) longjmp(tess->env,1); + + AddSentinel( tess, -SENTINEL_COORD ); + AddSentinel( tess, SENTINEL_COORD ); +} + + +static void DoneEdgeDict( GLUtesselator *tess ) +{ + ActiveRegion *reg; +#ifndef NDEBUG + int fixedEdges = 0; +#endif + + /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ + while( (reg = (ActiveRegion *)dictKey( dictMin( tess->dict ))) != NULL ) { + /* + * At the end of all processing, the dictionary should contain + * only the two sentinel edges, plus at most one "fixable" edge + * created by ConnectRightVertex(). + */ + if( ! reg->sentinel ) { + assert( reg->fixUpperEdge ); + assert( ++fixedEdges == 1 ); + } + assert( reg->windingNumber == 0 ); + DeleteRegion( tess, reg ); +/* __gl_meshDelete( reg->eUp );*/ + } + dictDeleteDict( tess->dict ); /* __gl_dictListDeleteDict */ +} + + +static void RemoveDegenerateEdges( GLUtesselator *tess ) +/* + * Remove zero-length edges, and contours with fewer than 3 vertices. + */ +{ + GLUhalfEdge *e, *eNext, *eLnext; + GLUhalfEdge *eHead = &tess->mesh->eHead; + + /*LINTED*/ + for( e = eHead->next; e != eHead; e = eNext ) { + eNext = e->next; + eLnext = e->Lnext; + + if( VertEq( e->Org, e->Dst ) && e->Lnext->Lnext != e ) { + /* Zero-length edge, contour has at least 3 edges */ + + SpliceMergeVertices( tess, eLnext, e ); /* deletes e->Org */ + if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1); /* e is a self-loop */ + e = eLnext; + eLnext = e->Lnext; + } + if( eLnext->Lnext == e ) { + /* Degenerate contour (one or two edges) */ + + if( eLnext != e ) { + if( eLnext == eNext || eLnext == eNext->Sym ) { eNext = eNext->next; } + if ( !__gl_meshDelete( eLnext ) ) longjmp(tess->env,1); + } + if( e == eNext || e == eNext->Sym ) { eNext = eNext->next; } + if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1); + } + } +} + +static int InitPriorityQ( GLUtesselator *tess ) +/* + * Insert all vertices into the priority queue which determines the + * order in which vertices cross the sweep line. + */ +{ + PriorityQ *pq; + GLUvertex *v, *vHead; + + /* __gl_pqSortNewPriorityQ */ + pq = tess->pq = pqNewPriorityQ( (int (*)(PQkey, PQkey)) __gl_vertLeq ); + if (pq == NULL) return 0; + + vHead = &tess->mesh->vHead; + for( v = vHead->next; v != vHead; v = v->next ) { + v->pqHandle = pqInsert( pq, v ); /* __gl_pqSortInsert */ + if (v->pqHandle == LONG_MAX) break; + } + if (v != vHead || !pqInit( pq ) ) { /* __gl_pqSortInit */ + pqDeletePriorityQ(tess->pq); /* __gl_pqSortDeletePriorityQ */ + tess->pq = NULL; + return 0; + } + + return 1; +} + + +static void DonePriorityQ( GLUtesselator *tess ) +{ + pqDeletePriorityQ( tess->pq ); /* __gl_pqSortDeletePriorityQ */ +} + + +static int RemoveDegenerateFaces( GLUmesh *mesh ) +/* + * Delete any degenerate faces with only two edges. WalkDirtyRegions() + * will catch almost all of these, but it won't catch degenerate faces + * produced by splice operations on already-processed edges. + * The two places this can happen are in FinishLeftRegions(), when + * we splice in a "temporary" edge produced by ConnectRightVertex(), + * and in CheckForLeftSplice(), where we splice already-processed + * edges to ensure that our dictionary invariants are not violated + * by numerical errors. + * + * In both these cases it is *very* dangerous to delete the offending + * edge at the time, since one of the routines further up the stack + * will sometimes be keeping a pointer to that edge. + */ +{ + GLUface *f, *fNext; + GLUhalfEdge *e; + + /*LINTED*/ + for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) { + fNext = f->next; + e = f->anEdge; + assert( e->Lnext != e ); + + if( e->Lnext->Lnext == e ) { + /* A face with only two edges */ + AddWinding( e->Onext, e ); + if ( !__gl_meshDelete( e ) ) return 0; + } + } + return 1; +} + +int __gl_computeInterior( GLUtesselator *tess ) +/* + * __gl_computeInterior( tess ) computes the planar arrangement specified + * by the given contours, and further subdivides this arrangement + * into regions. Each region is marked "inside" if it belongs + * to the polygon, according to the rule given by tess->windingRule. + * Each interior region is guaranteed be monotone. + */ +{ + GLUvertex *v, *vNext; + + tess->fatalError = FALSE; + + /* Each vertex defines an event for our sweep line. Start by inserting + * all the vertices in a priority queue. Events are processed in + * lexicographic order, ie. + * + * e1 < e2 iff e1.x < e2.x || (e1.x == e2.x && e1.y < e2.y) + */ + RemoveDegenerateEdges( tess ); + if ( !InitPriorityQ( tess ) ) return 0; /* if error */ + InitEdgeDict( tess ); + + /* __gl_pqSortExtractMin */ + while( (v = (GLUvertex *)pqExtractMin( tess->pq )) != NULL ) { + for( ;; ) { + vNext = (GLUvertex *)pqMinimum( tess->pq ); /* __gl_pqSortMinimum */ + if( vNext == NULL || ! VertEq( vNext, v )) break; + + /* Merge together all vertices at exactly the same location. + * This is more efficient than processing them one at a time, + * simplifies the code (see ConnectLeftDegenerate), and is also + * important for correct handling of certain degenerate cases. + * For example, suppose there are two identical edges A and B + * that belong to different contours (so without this code they would + * be processed by separate sweep events). Suppose another edge C + * crosses A and B from above. When A is processed, we split it + * at its intersection point with C. However this also splits C, + * so when we insert B we may compute a slightly different + * intersection point. This might leave two edges with a small + * gap between them. This kind of error is especially obvious + * when using boundary extraction (GLU_TESS_BOUNDARY_ONLY). + */ + vNext = (GLUvertex *)pqExtractMin( tess->pq ); /* __gl_pqSortExtractMin*/ + SpliceMergeVertices( tess, v->anEdge, vNext->anEdge ); + } + SweepEvent( tess, v ); + } + + /* Set tess->event for debugging purposes */ + /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ + tess->event = ((ActiveRegion *) dictKey( dictMin( tess->dict )))->eUp->Org; + DebugEvent( tess ); + DoneEdgeDict( tess ); + DonePriorityQ( tess ); + + if ( !RemoveDegenerateFaces( tess->mesh ) ) return 0; + __gl_meshCheckMesh( tess->mesh ); + + return 1; +} diff --git a/deps/libtess/sweep.h b/deps/libtess/sweep.h new file mode 100644 index 0000000000..2223f52f59 --- /dev/null +++ b/deps/libtess/sweep.h @@ -0,0 +1,84 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/sweep.h,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#ifndef __sweep_h_ +#define __sweep_h_ + +#include "mesh.h" + +/* __gl_computeInterior( tess ) computes the planar arrangement specified + * by the given contours, and further subdivides this arrangement + * into regions. Each region is marked "inside" if it belongs + * to the polygon, according to the rule given by tess->windingRule. + * Each interior region is guaranteed be monotone. + */ +int __gl_computeInterior( GLUtesselator *tess ); + + +/* The following is here *only* for access by debugging routines */ + +#include "dict.h" + +/* For each pair of adjacent edges crossing the sweep line, there is + * an ActiveRegion to represent the region between them. The active + * regions are kept in sorted order in a dynamic dictionary. As the + * sweep line crosses each vertex, we update the affected regions. + */ + +struct ActiveRegion { + GLUhalfEdge *eUp; /* upper edge, directed right to left */ + DictNode *nodeUp; /* dictionary node corresponding to eUp */ + int windingNumber; /* used to determine which regions are + * inside the polygon */ + GLboolean inside; /* is this region inside the polygon? */ + GLboolean sentinel; /* marks fake edges at t = +/-infinity */ + GLboolean dirty; /* marks regions where the upper or lower + * edge has changed, but we haven't checked + * whether they intersect yet */ + GLboolean fixUpperEdge; /* marks temporary edges introduced when + * we process a "right vertex" (one without + * any edges leaving to the right) */ +}; + +#define RegionBelow(r) ((ActiveRegion *) dictKey(dictPred((r)->nodeUp))) +#define RegionAbove(r) ((ActiveRegion *) dictKey(dictSucc((r)->nodeUp))) + +#endif diff --git a/deps/libtess/tess.c b/deps/libtess/tess.c new file mode 100644 index 0000000000..acc0b5b40c --- /dev/null +++ b/deps/libtess/tess.c @@ -0,0 +1,632 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include +#include +#include "memalloc.h" +#include "tess.h" +#include "mesh.h" +#include "normal.h" +#include "sweep.h" +#include "tessmono.h" +#include "render.h" + +#define GLU_TESS_DEFAULT_TOLERANCE 0.0 +#define GLU_TESS_MESH 100112 /* void (*)(GLUmesh *mesh) */ + +#define TRUE 1 +#define FALSE 0 + +/*ARGSUSED*/ static void GLAPIENTRY noBegin( GLenum type ) {} +/*ARGSUSED*/ static void GLAPIENTRY noEdgeFlag( GLboolean boundaryEdge ) {} +/*ARGSUSED*/ static void GLAPIENTRY noVertex( void *data ) {} +/*ARGSUSED*/ static void GLAPIENTRY noEnd( void ) {} +/*ARGSUSED*/ static void GLAPIENTRY noError( GLenum errnum ) {} +/*ARGSUSED*/ static void GLAPIENTRY noCombine( GLdouble coords[3], void *data[4], + GLfloat weight[4], void **dataOut ) {} +/*ARGSUSED*/ static void GLAPIENTRY noMesh( GLUmesh *mesh ) {} + + +/*ARGSUSED*/ void GLAPIENTRY __gl_noBeginData( GLenum type, + void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge, + void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noVertexData( void *data, + void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noEndData( void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noErrorData( GLenum errnum, + void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noCombineData( GLdouble coords[3], + void *data[4], + GLfloat weight[4], + void **outData, + void *polygonData ) {} + +/* Half-edges are allocated in pairs (see mesh.c) */ +typedef struct { GLUhalfEdge e, eSym; } EdgePair; + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define MAX_FAST_ALLOC (MAX(sizeof(EdgePair), \ + MAX(sizeof(GLUvertex),sizeof(GLUface)))) + + +GLUtesselator * GLAPIENTRY +gluNewTess( void ) +{ + GLUtesselator *tess; + + /* Only initialize fields which can be changed by the api. Other fields + * are initialized where they are used. + */ + + tess = (GLUtesselator *)memAlloc( sizeof( GLUtesselator )); + if (tess == NULL) { + return 0; /* out of memory */ + } + + tess->state = T_DORMANT; + + tess->normal[0] = 0; + tess->normal[1] = 0; + tess->normal[2] = 0; + + tess->relTolerance = GLU_TESS_DEFAULT_TOLERANCE; + tess->windingRule = GLU_TESS_WINDING_ODD; + tess->flagBoundary = FALSE; + tess->boundaryOnly = FALSE; + + tess->callBegin = &noBegin; + tess->callEdgeFlag = &noEdgeFlag; + tess->callVertex = &noVertex; + tess->callEnd = &noEnd; + + tess->callError = &noError; + tess->callCombine = &noCombine; + tess->callMesh = &noMesh; + + tess->callBeginData= &__gl_noBeginData; + tess->callEdgeFlagData= &__gl_noEdgeFlagData; + tess->callVertexData= &__gl_noVertexData; + tess->callEndData= &__gl_noEndData; + tess->callErrorData= &__gl_noErrorData; + tess->callCombineData= &__gl_noCombineData; + + tess->polygonData= NULL; + + return tess; +} + +static void MakeDormant( GLUtesselator *tess ) +{ + /* Return the tessellator to its original dormant state. */ + + if( tess->mesh != NULL ) { + __gl_meshDeleteMesh( tess->mesh ); + } + tess->state = T_DORMANT; + tess->lastEdge = NULL; + tess->mesh = NULL; +} + +#define RequireState( tess, s ) if( tess->state != s ) GotoState(tess,s) + +static void GotoState( GLUtesselator *tess, enum TessState newState ) +{ + while( tess->state != newState ) { + /* We change the current state one level at a time, to get to + * the desired state. + */ + if( tess->state < newState ) { + switch( tess->state ) { + case T_DORMANT: + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_POLYGON ); + gluTessBeginPolygon( tess, NULL ); + break; + case T_IN_POLYGON: + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_CONTOUR ); + gluTessBeginContour( tess ); + break; + default: + ; + } + } else { + switch( tess->state ) { + case T_IN_CONTOUR: + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_CONTOUR ); + gluTessEndContour( tess ); + break; + case T_IN_POLYGON: + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_POLYGON ); + /* gluTessEndPolygon( tess ) is too much work! */ + MakeDormant( tess ); + break; + default: + ; + } + } + } +} + + +void GLAPIENTRY +gluDeleteTess( GLUtesselator *tess ) +{ + RequireState( tess, T_DORMANT ); + memFree( tess ); +} + + +void GLAPIENTRY +gluTessProperty( GLUtesselator *tess, GLenum which, GLdouble value ) +{ + GLenum windingRule; + + switch( which ) { + case GLU_TESS_TOLERANCE: + if( value < 0.0 || value > 1.0 ) break; + tess->relTolerance = value; + return; + + case GLU_TESS_WINDING_RULE: + windingRule = (GLenum) value; + if( windingRule != value ) break; /* not an integer */ + + switch( windingRule ) { + case GLU_TESS_WINDING_ODD: + case GLU_TESS_WINDING_NONZERO: + case GLU_TESS_WINDING_POSITIVE: + case GLU_TESS_WINDING_NEGATIVE: + case GLU_TESS_WINDING_ABS_GEQ_TWO: + tess->windingRule = windingRule; + return; + default: + break; + } + + case GLU_TESS_BOUNDARY_ONLY: + tess->boundaryOnly = (value != 0); + return; + + default: + CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM ); + return; + } + CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_VALUE ); +} + +/* Returns tessellator property */ +void GLAPIENTRY +gluGetTessProperty( GLUtesselator *tess, GLenum which, GLdouble *value ) +{ + switch (which) { + case GLU_TESS_TOLERANCE: + /* tolerance should be in range [0..1] */ + assert(0.0 <= tess->relTolerance && tess->relTolerance <= 1.0); + *value= tess->relTolerance; + break; + case GLU_TESS_WINDING_RULE: + assert(tess->windingRule == GLU_TESS_WINDING_ODD || + tess->windingRule == GLU_TESS_WINDING_NONZERO || + tess->windingRule == GLU_TESS_WINDING_POSITIVE || + tess->windingRule == GLU_TESS_WINDING_NEGATIVE || + tess->windingRule == GLU_TESS_WINDING_ABS_GEQ_TWO); + *value= tess->windingRule; + break; + case GLU_TESS_BOUNDARY_ONLY: + assert(tess->boundaryOnly == TRUE || tess->boundaryOnly == FALSE); + *value= tess->boundaryOnly; + break; + default: + *value= 0.0; + CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM ); + break; + } +} /* gluGetTessProperty() */ + +void GLAPIENTRY +gluTessNormal( GLUtesselator *tess, GLdouble x, GLdouble y, GLdouble z ) +{ + tess->normal[0] = x; + tess->normal[1] = y; + tess->normal[2] = z; +} + +void GLAPIENTRY +gluTessCallback( GLUtesselator *tess, GLenum which, _GLUfuncptr fn) +{ + switch( which ) { + case GLU_TESS_BEGIN: + tess->callBegin = (fn == NULL) ? &noBegin : (void (GLAPIENTRY *)(GLenum)) fn; + return; + case GLU_TESS_BEGIN_DATA: + tess->callBeginData = (fn == NULL) ? + &__gl_noBeginData : (void (GLAPIENTRY *)(GLenum, void *)) fn; + return; + case GLU_TESS_EDGE_FLAG: + tess->callEdgeFlag = (fn == NULL) ? &noEdgeFlag : + (void (GLAPIENTRY *)(GLboolean)) fn; + /* If the client wants boundary edges to be flagged, + * we render everything as separate triangles (no strips or fans). + */ + tess->flagBoundary = (fn != NULL); + return; + case GLU_TESS_EDGE_FLAG_DATA: + tess->callEdgeFlagData= (fn == NULL) ? + &__gl_noEdgeFlagData : (void (GLAPIENTRY *)(GLboolean, void *)) fn; + /* If the client wants boundary edges to be flagged, + * we render everything as separate triangles (no strips or fans). + */ + tess->flagBoundary = (fn != NULL); + return; + case GLU_TESS_VERTEX: + tess->callVertex = (fn == NULL) ? &noVertex : + (void (GLAPIENTRY *)(void *)) fn; + return; + case GLU_TESS_VERTEX_DATA: + tess->callVertexData = (fn == NULL) ? + &__gl_noVertexData : (void (GLAPIENTRY *)(void *, void *)) fn; + return; + case GLU_TESS_END: + tess->callEnd = (fn == NULL) ? &noEnd : (void (GLAPIENTRY *)(void)) fn; + return; + case GLU_TESS_END_DATA: + tess->callEndData = (fn == NULL) ? &__gl_noEndData : + (void (GLAPIENTRY *)(void *)) fn; + return; + case GLU_TESS_ERROR: + tess->callError = (fn == NULL) ? &noError : (void (GLAPIENTRY *)(GLenum)) fn; + return; + case GLU_TESS_ERROR_DATA: + tess->callErrorData = (fn == NULL) ? + &__gl_noErrorData : (void (GLAPIENTRY *)(GLenum, void *)) fn; + return; + case GLU_TESS_COMBINE: + tess->callCombine = (fn == NULL) ? &noCombine : + (void (GLAPIENTRY *)(GLdouble [3],void *[4], GLfloat [4], void ** )) fn; + return; + case GLU_TESS_COMBINE_DATA: + tess->callCombineData = (fn == NULL) ? &__gl_noCombineData : + (void (GLAPIENTRY *)(GLdouble [3], + void *[4], + GLfloat [4], + void **, + void *)) fn; + return; + case GLU_TESS_MESH: + tess->callMesh = (fn == NULL) ? &noMesh : (void (GLAPIENTRY *)(GLUmesh *)) fn; + return; + default: + CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM ); + return; + } +} + +static int AddVertex( GLUtesselator *tess, GLdouble coords[3], void *data ) +{ + GLUhalfEdge *e; + + e = tess->lastEdge; + if( e == NULL ) { + /* Make a self-loop (one vertex, one edge). */ + + e = __gl_meshMakeEdge( tess->mesh ); + if (e == NULL) return 0; + if ( !__gl_meshSplice( e, e->Sym ) ) return 0; + } else { + /* Create a new vertex and edge which immediately follow e + * in the ordering around the left face. + */ + if (__gl_meshSplitEdge( e ) == NULL) return 0; + e = e->Lnext; + } + + /* The new vertex is now e->Org. */ + e->Org->data = data; + e->Org->coords[0] = coords[0]; + e->Org->coords[1] = coords[1]; + e->Org->coords[2] = coords[2]; + + /* The winding of an edge says how the winding number changes as we + * cross from the edge''s right face to its left face. We add the + * vertices in such an order that a CCW contour will add +1 to + * the winding number of the region inside the contour. + */ + e->winding = 1; + e->Sym->winding = -1; + + tess->lastEdge = e; + + return 1; +} + + +static void CacheVertex( GLUtesselator *tess, GLdouble coords[3], void *data ) +{ + CachedVertex *v = &tess->cache[tess->cacheCount]; + + v->data = data; + v->coords[0] = coords[0]; + v->coords[1] = coords[1]; + v->coords[2] = coords[2]; + ++tess->cacheCount; +} + + +static int EmptyCache( GLUtesselator *tess ) +{ + CachedVertex *v = tess->cache; + CachedVertex *vLast; + + tess->mesh = __gl_meshNewMesh(); + if (tess->mesh == NULL) return 0; + + for( vLast = v + tess->cacheCount; v < vLast; ++v ) { + if ( !AddVertex( tess, v->coords, v->data ) ) return 0; + } + tess->cacheCount = 0; + tess->emptyCache = FALSE; + + return 1; +} + + +void GLAPIENTRY +gluTessVertex( GLUtesselator *tess, GLdouble coords[3], void *data ) +{ + int i, tooLarge = FALSE; + GLdouble x, clamped[3]; + + RequireState( tess, T_IN_CONTOUR ); + + if( tess->emptyCache ) { + if ( !EmptyCache( tess ) ) { + CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); + return; + } + tess->lastEdge = NULL; + } + for( i = 0; i < 3; ++i ) { + x = coords[i]; + if( x < - GLU_TESS_MAX_COORD ) { + x = - GLU_TESS_MAX_COORD; + tooLarge = TRUE; + } + if( x > GLU_TESS_MAX_COORD ) { + x = GLU_TESS_MAX_COORD; + tooLarge = TRUE; + } + clamped[i] = x; + } + if( tooLarge ) { + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_COORD_TOO_LARGE ); + } + + if( tess->mesh == NULL ) { + if( tess->cacheCount < TESS_MAX_CACHE ) { + CacheVertex( tess, clamped, data ); + return; + } + if ( !EmptyCache( tess ) ) { + CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); + return; + } + } + if ( !AddVertex( tess, clamped, data ) ) { + CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); + } +} + + +void GLAPIENTRY +gluTessBeginPolygon( GLUtesselator *tess, void *data ) +{ + RequireState( tess, T_DORMANT ); + + tess->state = T_IN_POLYGON; + tess->cacheCount = 0; + tess->emptyCache = FALSE; + tess->mesh = NULL; + + tess->polygonData= data; +} + + +void GLAPIENTRY +gluTessBeginContour( GLUtesselator *tess ) +{ + RequireState( tess, T_IN_POLYGON ); + + tess->state = T_IN_CONTOUR; + tess->lastEdge = NULL; + if( tess->cacheCount > 0 ) { + /* Just set a flag so we don't get confused by empty contours + * -- these can be generated accidentally with the obsolete + * NextContour() interface. + */ + tess->emptyCache = TRUE; + } +} + + +void GLAPIENTRY +gluTessEndContour( GLUtesselator *tess ) +{ + RequireState( tess, T_IN_CONTOUR ); + tess->state = T_IN_POLYGON; +} + +void GLAPIENTRY +gluTessEndPolygon( GLUtesselator *tess ) +{ + GLUmesh *mesh; + + /* No idea why, but setjmp() has suddenly started to crash... + if (setjmp(tess->env) != 0) { + // come back here if out of memory + CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); + return; + } + */ + + RequireState( tess, T_IN_POLYGON ); + tess->state = T_DORMANT; + + if( tess->mesh == NULL ) { + if( ! tess->flagBoundary && tess->callMesh == &noMesh ) { + + /* Try some special code to make the easy cases go quickly + * (eg. convex polygons). This code does NOT handle multiple contours, + * intersections, edge flags, and of course it does not generate + * an explicit mesh either. + */ + if( __gl_renderCache( tess )) { + tess->polygonData= NULL; + return; + } + } + if ( !EmptyCache( tess ) ) longjmp(tess->env,1); /* could've used a label*/ + } + + /* Determine the polygon normal and project vertices onto the plane + * of the polygon. + */ + __gl_projectPolygon( tess ); + + /* __gl_computeInterior( tess ) computes the planar arrangement specified + * by the given contours, and further subdivides this arrangement + * into regions. Each region is marked "inside" if it belongs + * to the polygon, according to the rule given by tess->windingRule. + * Each interior region is guaranteed be monotone. + */ + if ( !__gl_computeInterior( tess ) ) { + longjmp(tess->env,1); /* could've used a label */ + } + + mesh = tess->mesh; + if( ! tess->fatalError ) { + int rc = 1; + + /* If the user wants only the boundary contours, we throw away all edges + * except those which separate the interior from the exterior. + * Otherwise we tessellate all the regions marked "inside". + */ + if( tess->boundaryOnly ) { + rc = __gl_meshSetWindingNumber( mesh, 1, TRUE ); + } else { + rc = __gl_meshTessellateInterior( mesh ); + } + if (rc == 0) longjmp(tess->env,1); /* could've used a label */ + + __gl_meshCheckMesh( mesh ); + + if( tess->callBegin != &noBegin || tess->callEnd != &noEnd + || tess->callVertex != &noVertex || tess->callEdgeFlag != &noEdgeFlag + || tess->callBeginData != &__gl_noBeginData + || tess->callEndData != &__gl_noEndData + || tess->callVertexData != &__gl_noVertexData + || tess->callEdgeFlagData != &__gl_noEdgeFlagData ) + { + if( tess->boundaryOnly ) { + __gl_renderBoundary( tess, mesh ); /* output boundary contours */ + } else { + __gl_renderMesh( tess, mesh ); /* output strips and fans */ + } + } + if( tess->callMesh != &noMesh ) { + + /* Throw away the exterior faces, so that all faces are interior. + * This way the user doesn't have to check the "inside" flag, + * and we don't need to even reveal its existence. It also leaves + * the freedom for an implementation to not generate the exterior + * faces in the first place. + */ + __gl_meshDiscardExterior( mesh ); + (*tess->callMesh)( mesh ); /* user wants the mesh itself */ + tess->mesh = NULL; + tess->polygonData= NULL; + return; + } + } + __gl_meshDeleteMesh( mesh ); + tess->polygonData= NULL; + tess->mesh = NULL; +} + + +/*XXXblythe unused function*/ +#if 0 +void GLAPIENTRY +gluDeleteMesh( GLUmesh *mesh ) +{ + __gl_meshDeleteMesh( mesh ); +} +#endif + + + +/*******************************************************/ + +/* Obsolete calls -- for backward compatibility */ + +void GLAPIENTRY +gluBeginPolygon( GLUtesselator *tess ) +{ + gluTessBeginPolygon( tess, NULL ); + gluTessBeginContour( tess ); +} + + +/*ARGSUSED*/ +void GLAPIENTRY +gluNextContour( GLUtesselator *tess, GLenum type ) +{ + gluTessEndContour( tess ); + gluTessBeginContour( tess ); +} + + +void GLAPIENTRY +gluEndPolygon( GLUtesselator *tess ) +{ + gluTessEndContour( tess ); + gluTessEndPolygon( tess ); +} diff --git a/deps/libtess/tess.h b/deps/libtess/tess.h new file mode 100644 index 0000000000..061dd760c5 --- /dev/null +++ b/deps/libtess/tess.h @@ -0,0 +1,164 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. 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 including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * 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 + * SILICON GRAPHICS, INC. 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. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __tess_h_ +#define __tess_h_ + +#include +#include "mesh.h" +#include "dict.h" +#include "priorityq.h" + +/* The begin/end calls must be properly nested. We keep track of + * the current state to enforce the ordering. + */ +enum TessState { T_DORMANT, T_IN_POLYGON, T_IN_CONTOUR }; + +/* We cache vertex data for single-contour polygons so that we can + * try a quick-and-dirty decomposition first. + */ +#define TESS_MAX_CACHE 100 + +typedef struct CachedVertex { + GLdouble coords[3]; + void *data; +} CachedVertex; + +struct GLUtesselator { + + /*** state needed for collecting the input data ***/ + + enum TessState state; /* what begin/end calls have we seen? */ + + GLUhalfEdge *lastEdge; /* lastEdge->Org is the most recent vertex */ + GLUmesh *mesh; /* stores the input contours, and eventually + the tessellation itself */ + + void (GLAPIENTRY *callError)( GLenum errnum ); + + /*** state needed for projecting onto the sweep plane ***/ + + GLdouble normal[3]; /* user-specified normal (if provided) */ + GLdouble sUnit[3]; /* unit vector in s-direction (debugging) */ + GLdouble tUnit[3]; /* unit vector in t-direction (debugging) */ + + /*** state needed for the line sweep ***/ + + GLdouble relTolerance; /* tolerance for merging features */ + GLenum windingRule; /* rule for determining polygon interior */ + GLboolean fatalError; /* fatal error: needed combine callback */ + + Dict *dict; /* edge dictionary for sweep line */ + PriorityQ *pq; /* priority queue of vertex events */ + GLUvertex *event; /* current sweep event being processed */ + + void (GLAPIENTRY *callCombine)( GLdouble coords[3], void *data[4], + GLfloat weight[4], void **outData ); + + /*** state needed for rendering callbacks (see render.c) ***/ + + GLboolean flagBoundary; /* mark boundary edges (use EdgeFlag) */ + GLboolean boundaryOnly; /* Extract contours, not triangles */ + GLUface *lonelyTriList; + /* list of triangles which could not be rendered as strips or fans */ + + void (GLAPIENTRY *callBegin)( GLenum type ); + void (GLAPIENTRY *callEdgeFlag)( GLboolean boundaryEdge ); + void (GLAPIENTRY *callVertex)( void *data ); + void (GLAPIENTRY *callEnd)( void ); + void (GLAPIENTRY *callMesh)( GLUmesh *mesh ); + + + /*** state needed to cache single-contour polygons for renderCache() */ + + GLboolean emptyCache; /* empty cache on next vertex() call */ + int cacheCount; /* number of cached vertices */ + CachedVertex cache[TESS_MAX_CACHE]; /* the vertex data */ + + /*** rendering callbacks that also pass polygon data ***/ + void (GLAPIENTRY *callBeginData)( GLenum type, void *polygonData ); + void (GLAPIENTRY *callEdgeFlagData)( GLboolean boundaryEdge, + void *polygonData ); + void (GLAPIENTRY *callVertexData)( void *data, void *polygonData ); + void (GLAPIENTRY *callEndData)( void *polygonData ); + void (GLAPIENTRY *callErrorData)( GLenum errnum, void *polygonData ); + void (GLAPIENTRY *callCombineData)( GLdouble coords[3], void *data[4], + GLfloat weight[4], void **outData, + void *polygonData ); + + jmp_buf env; /* place to jump to when memAllocs fail */ + + void *polygonData; /* client data for current polygon */ +}; + +void GLAPIENTRY __gl_noBeginData( GLenum type, void *polygonData ); +void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge, void *polygonData ); +void GLAPIENTRY __gl_noVertexData( void *data, void *polygonData ); +void GLAPIENTRY __gl_noEndData( void *polygonData ); +void GLAPIENTRY __gl_noErrorData( GLenum errnum, void *polygonData ); +void GLAPIENTRY __gl_noCombineData( GLdouble coords[3], void *data[4], + GLfloat weight[4], void **outData, + void *polygonData ); + +#define CALL_BEGIN_OR_BEGIN_DATA(a) \ + if (tess->callBeginData != &__gl_noBeginData) \ + (*tess->callBeginData)((a),tess->polygonData); \ + else (*tess->callBegin)((a)); + +#define CALL_VERTEX_OR_VERTEX_DATA(a) \ + if (tess->callVertexData != &__gl_noVertexData) \ + (*tess->callVertexData)((a),tess->polygonData); \ + else (*tess->callVertex)((a)); + +#define CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA(a) \ + if (tess->callEdgeFlagData != &__gl_noEdgeFlagData) \ + (*tess->callEdgeFlagData)((a),tess->polygonData); \ + else (*tess->callEdgeFlag)((a)); + +#define CALL_END_OR_END_DATA() \ + if (tess->callEndData != &__gl_noEndData) \ + (*tess->callEndData)(tess->polygonData); \ + else (*tess->callEnd)(); + +#define CALL_COMBINE_OR_COMBINE_DATA(a,b,c,d) \ + if (tess->callCombineData != &__gl_noCombineData) \ + (*tess->callCombineData)((a),(b),(c),(d),tess->polygonData); \ + else (*tess->callCombine)((a),(b),(c),(d)); + +#define CALL_ERROR_OR_ERROR_DATA(a) \ + if (tess->callErrorData != &__gl_noErrorData) \ + (*tess->callErrorData)((a),tess->polygonData); \ + else (*tess->callError)((a)); + +#endif diff --git a/deps/libtess/tessmono.c b/deps/libtess/tessmono.c new file mode 100644 index 0000000000..77fe0ac619 --- /dev/null +++ b/deps/libtess/tessmono.c @@ -0,0 +1,208 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/tessmono.c,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#include "gluos.h" +#include +#include "geom.h" +#include "mesh.h" +#include "tessmono.h" +#include + +#define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \ + eDst->Sym->winding += eSrc->Sym->winding) + +/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region + * (what else would it do??) The region must consist of a single + * loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this + * case means that any vertical line intersects the interior of the + * region in a single interval. + * + * Tessellation consists of adding interior edges (actually pairs of + * half-edges), to split the region into non-overlapping triangles. + * + * The basic idea is explained in Preparata and Shamos (which I don''t + * have handy right now), although their implementation is more + * complicated than this one. The are two edge chains, an upper chain + * and a lower chain. We process all vertices from both chains in order, + * from right to left. + * + * The algorithm ensures that the following invariant holds after each + * vertex is processed: the untessellated region consists of two + * chains, where one chain (say the upper) is a single edge, and + * the other chain is concave. The left vertex of the single edge + * is always to the left of all vertices in the concave chain. + * + * Each step consists of adding the rightmost unprocessed vertex to one + * of the two chains, and forming a fan of triangles from the rightmost + * of two chain endpoints. Determining whether we can add each triangle + * to the fan is a simple orientation test. By making the fan as large + * as possible, we restore the invariant (check it yourself). + */ +int __gl_meshTessellateMonoRegion( GLUface *face ) +{ + GLUhalfEdge *up, *lo; + + /* All edges are oriented CCW around the boundary of the region. + * First, find the half-edge whose origin vertex is rightmost. + * Since the sweep goes from left to right, face->anEdge should + * be close to the edge we want. + */ + up = face->anEdge; + assert( up->Lnext != up && up->Lnext->Lnext != up ); + + for( ; VertLeq( up->Dst, up->Org ); up = up->Lprev ) + ; + for( ; VertLeq( up->Org, up->Dst ); up = up->Lnext ) + ; + lo = up->Lprev; + + while( up->Lnext != lo ) { + if( VertLeq( up->Dst, lo->Org )) { + /* up->Dst is on the left. It is safe to form triangles from lo->Org. + * The EdgeGoesLeft test guarantees progress even when some triangles + * are CW, given that the upper and lower chains are truly monotone. + */ + while( lo->Lnext != up && (EdgeGoesLeft( lo->Lnext ) + || EdgeSign( lo->Org, lo->Dst, lo->Lnext->Dst ) <= 0 )) { + GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo ); + if (tempHalfEdge == NULL) return 0; + lo = tempHalfEdge->Sym; + } + lo = lo->Lprev; + } else { + /* lo->Org is on the left. We can make CCW triangles from up->Dst. */ + while( lo->Lnext != up && (EdgeGoesRight( up->Lprev ) + || EdgeSign( up->Dst, up->Org, up->Lprev->Org ) >= 0 )) { + GLUhalfEdge *tempHalfEdge= __gl_meshConnect( up, up->Lprev ); + if (tempHalfEdge == NULL) return 0; + up = tempHalfEdge->Sym; + } + up = up->Lnext; + } + } + + /* Now lo->Org == up->Dst == the leftmost vertex. The remaining region + * can be tessellated in a fan from this leftmost vertex. + */ + assert( lo->Lnext != up ); + while( lo->Lnext->Lnext != up ) { + GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo ); + if (tempHalfEdge == NULL) return 0; + lo = tempHalfEdge->Sym; + } + + return 1; +} + + +/* __gl_meshTessellateInterior( mesh ) tessellates each region of + * the mesh which is marked "inside" the polygon. Each such region + * must be monotone. + */ +int __gl_meshTessellateInterior( GLUmesh *mesh ) +{ + GLUface *f, *next; + + /*LINTED*/ + for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) { + /* Make sure we don''t try to tessellate the new triangles. */ + next = f->next; + if( f->inside ) { + if ( !__gl_meshTessellateMonoRegion( f ) ) return 0; + } + } + + return 1; +} + + +/* __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces + * which are not marked "inside" the polygon. Since further mesh operations + * on NULL faces are not allowed, the main purpose is to clean up the + * mesh so that exterior loops are not represented in the data structure. + */ +void __gl_meshDiscardExterior( GLUmesh *mesh ) +{ + GLUface *f, *next; + + /*LINTED*/ + for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) { + /* Since f will be destroyed, save its next pointer. */ + next = f->next; + if( ! f->inside ) { + __gl_meshZapFace( f ); + } + } +} + +#define MARKED_FOR_DELETION 0x7fffffff + +/* __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the + * winding numbers on all edges so that regions marked "inside" the + * polygon have a winding number of "value", and regions outside + * have a winding number of 0. + * + * If keepOnlyBoundary is TRUE, it also deletes all edges which do not + * separate an interior region from an exterior one. + */ +int __gl_meshSetWindingNumber( GLUmesh *mesh, int value, + GLboolean keepOnlyBoundary ) +{ + GLUhalfEdge *e, *eNext; + + for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) { + eNext = e->next; + if( e->Rface->inside != e->Lface->inside ) { + + /* This is a boundary edge (one side is interior, one is exterior). */ + e->winding = (e->Lface->inside) ? value : -value; + } else { + + /* Both regions are interior, or both are exterior. */ + if( ! keepOnlyBoundary ) { + e->winding = 0; + } else { + if ( !__gl_meshDelete( e ) ) return 0; + } + } + } + return 1; +} diff --git a/deps/libtess/tessmono.h b/deps/libtess/tessmono.h new file mode 100644 index 0000000000..01f244f6ec --- /dev/null +++ b/deps/libtess/tessmono.h @@ -0,0 +1,78 @@ +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +*/ +/* +** Author: Eric Veach, July 1994. +** +** $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $ +** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libtess/tessmono.h,v 1.1 2001/03/17 00:25:41 brianp Exp $ +*/ + +#ifndef __tessmono_h_ +#define __tessmono_h_ + +/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region + * (what else would it do??) The region must consist of a single + * loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this + * case means that any vertical line intersects the interior of the + * region in a single interval. + * + * Tessellation consists of adding interior edges (actually pairs of + * half-edges), to split the region into non-overlapping triangles. + * + * __gl_meshTessellateInterior( mesh ) tessellates each region of + * the mesh which is marked "inside" the polygon. Each such region + * must be monotone. + * + * __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces + * which are not marked "inside" the polygon. Since further mesh operations + * on NULL faces are not allowed, the main purpose is to clean up the + * mesh so that exterior loops are not represented in the data structure. + * + * __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the + * winding numbers on all edges so that regions marked "inside" the + * polygon have a winding number of "value", and regions outside + * have a winding number of 0. + * + * If keepOnlyBoundary is TRUE, it also deletes all edges which do not + * separate an interior region from an exterior one. + */ + +int __gl_meshTessellateMonoRegion( GLUface *face ); +int __gl_meshTessellateInterior( GLUmesh *mesh ); +void __gl_meshDiscardExterior( GLUmesh *mesh ); +int __gl_meshSetWindingNumber( GLUmesh *mesh, int value, + GLboolean keepOnlyBoundary ); + +#endif diff --git a/deps/libungif-4.1.1/Makefile b/deps/libungif-4.1.1/Makefile index dc9b4a93a9..947ee2772f 100644 --- a/deps/libungif-4.1.1/Makefile +++ b/deps/libungif-4.1.1/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -73,7 +77,7 @@ CECFLAGS += -cpp $(_CPP) all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(TARGET): $(SOURCES) $(RESOURCES) | objdir $(SYMBOLS) $(OBJECTS) $(AR) rcs $(TARGET) $(OBJECTS) $(LIBS) @@ -93,7 +97,7 @@ $(OBJ)gif_err.o: lib/gif_err.c $(OBJ)gifalloc.o: lib/gifalloc.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c lib/gifalloc.c -o $(OBJ)gifalloc.o -cleantarget: objdir +cleantarget: $(call rm,$(TARGET)) clean: cleantarget @@ -102,9 +106,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/deps/libungif-4.1.1/lib/dgif_lib.c b/deps/libungif-4.1.1/lib/dgif_lib.c index 9540437d0c..3a835fa127 100644 --- a/deps/libungif-4.1.1/lib/dgif_lib.c +++ b/deps/libungif-4.1.1/lib/dgif_lib.c @@ -10,6 +10,10 @@ * 3 Sep 90 - Version 1.1 by Gershon Elber (Support for Gif89, Unique names). * ******************************************************************************/ +#if defined(__EMSCRIPTEN__) +#include +#endif + #ifdef HAVE_CONFIG_H #include #endif diff --git a/deps/zlib-1.2.8/Makefile b/deps/zlib-1.2.8/Makefile index dd8f35baa5..37d39aa8b8 100644 --- a/deps/zlib-1.2.8/Makefile +++ b/deps/zlib-1.2.8/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -95,7 +99,7 @@ CECFLAGS += -cpp $(_CPP) all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(SYMBOLS): | objdir $(OBJECTS): | objdir @@ -175,7 +179,7 @@ $(OBJ)gzread.o: gzread.c $(OBJ)gzwrite.o: gzwrite.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(call quote_path,gzwrite.c) -o $(call quote_path,$@) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)objects.lst) $(call rm,$(TARGET)) ifdef SHARED_LIBRARY_TARGET @@ -193,9 +197,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/deps/zlib-1.2.8/gzlib.c b/deps/zlib-1.2.8/gzlib.c index 4880433dbd..23d41cdeda 100644 --- a/deps/zlib-1.2.8/gzlib.c +++ b/deps/zlib-1.2.8/gzlib.c @@ -5,7 +5,9 @@ #include "gzguts.h" +#if !defined(_MSC_VER) #include +#endif #if defined(_WIN32) && !defined(__BORLANDC__) # define LSEEK _lseeki64 diff --git a/deps/zlib-1.2.8/gzread.c b/deps/zlib-1.2.8/gzread.c index 1f526ba459..ea672cf0f8 100644 --- a/deps/zlib-1.2.8/gzread.c +++ b/deps/zlib-1.2.8/gzread.c @@ -3,7 +3,9 @@ * For conditions of distribution and use, see copyright notice in zlib.h */ +#if !defined(_MSC_VER) #include +#endif #include "gzguts.h" diff --git a/deps/zlib-1.2.8/gzwrite.c b/deps/zlib-1.2.8/gzwrite.c index 23d7cf3c5c..aca67b6ca9 100644 --- a/deps/zlib-1.2.8/gzwrite.c +++ b/deps/zlib-1.2.8/gzwrite.c @@ -3,7 +3,9 @@ * For conditions of distribution and use, see copyright notice in zlib.h */ +#if !defined(_MSC_VER) #include +#endif #include "gzguts.h" diff --git a/deps/zlib-1.2.8/inflate.c b/deps/zlib-1.2.8/inflate.c index 870f89bb4d..2997fcd9e1 100644 --- a/deps/zlib-1.2.8/inflate.c +++ b/deps/zlib-1.2.8/inflate.c @@ -1,3 +1,4 @@ +#include /* inflate.c -- zlib decompression * Copyright (C) 1995-2012 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h @@ -1212,8 +1213,13 @@ int flush; ret = Z_STREAM_END; goto inf_leave; case BAD: + { +#ifdef __EMSCRIPTEN__ + printf("Just Having This Printf here Avoids Returning Z_DATA_ERROR\n"); +#endif ret = Z_DATA_ERROR; goto inf_leave; + } case MEM: return Z_MEM_ERROR; case SYNC: diff --git a/doc/ecere/ecere/com/Array.econ b/doc/ecere/ecere/com/Array.econ index 33d80ae9e5..6a18d3c0ff 100644 --- a/doc/ecere/ecere/com/Array.econ +++ b/doc/ecere/ecere/com/Array.econ @@ -1,8 +1,19 @@ { name = "Array", description = "A dynamically resizable array template", - usage = "The array object can be indexed with the [ ] operator.

Elements of the array individually allocated on the heap are not freed unless Free() is invoked.

The array itself is must be instantiated and deleted as it is a ckass type.", - example = "void test()
{
Array points { size = 10 };
points[0] = { 10, 10 };
points.size = 20;
points[19] = { 5, 5 };
delete points;
}", + usage = "The array object can be indexed with the [ ] operator.
" + "
" + "Elements of the array individually allocated on the heap are not freed unless Free() is invoked.
" + "
" + "The array itself is must be instantiated and deleted as it is a ckass type.", + example = "void test()
" + "{
" + " Array points { size = 10 };
" + " points[0] = { 10, 10 };
" + " points.size = 20;
" + " points[19] = { 5, 5 };
" + " delete points;
" + "}", remarks = "The Array container class (like all containr classes) comes with some level of overhead, including an implied extra reference level and the overhead of multiple re-allocations. If dynamic reallocation is not required, stick to regular C arrays (e.g. int array[10]).", also = "Container", fields = [ diff --git a/doc/ecere/ecere/com/Container.econ b/doc/ecere/ecere/com/Container.econ index ddd5d1995b..cd25cafcf1 100644 --- a/doc/ecere/ecere/com/Container.econ +++ b/doc/ecere/ecere/com/Container.econ @@ -61,7 +61,9 @@ "GetAtPosition", { description = "Return an iterator to the element at position 'pos'. If create is true and the position is not currently occupied, an element will be added at that position. If an element was added as a result (with create set to true) and a non-null 'justAdded' pointer is passed, it will be set to true.", - remarks = "Avoid indexing linked lists by index as much as possible.

This method is used to implement the [ ] container indexing operator.", + remarks = "Avoid indexing linked lists by index as much as possible.
" + "
" + "This method is used to implement the [ ] container indexing operator.", parameters = [ { "create", @@ -197,7 +199,9 @@ "TakeOut", { description = "Remove an item from a Container by value (implies a Find and is slower than removing from Iterator). The element is not freed.", - remarks = "Beware of using TakeOut() with 'class' data types and 'class_no_expansion' not set, as OnCompare() may resolve two different instances as equivalent.

Even in containers where values are not guaranteed to be unique, only a single element will be taken out per call.", + remarks = "Beware of using TakeOut() with 'class' data types and 'class_no_expansion' not set, as OnCompare() may resolve two different instances as equivalent.
" + "
" + "Even in containers where values are not guaranteed to be unique, only a single element will be taken out per call.", parameters = [ { "d", diff --git a/doc/ecere/ecere/com/_global-defs.econ b/doc/ecere/ecere/com/_global-defs.econ index 2ef81f21d1..73120bd763 100644 --- a/doc/ecere/ecere/com/_global-defs.econ +++ b/doc/ecere/ecere/com/_global-defs.econ @@ -12,6 +12,38 @@ { description = "Run MemoryGuard checks (typically called on exit)" } + }, + { + "eInstance_StopWatching", + { + description = "Stops a previously set \"watch\"", + usage = "stopwatching(instance, property);", + example = "stopwatching(foo, bag);
" + "stopwatching(foo, box);", + also = "eInstance_Watch()" + } + }, + { + "eInstance_Watch", + { + description = "watch allows one object to watch some property of another object, allowing it to react when that property changes.", + usage = "In classA, add \"watchable\" to the property to be watched.
" + "In classB, add \"watch classA { propertyA { DoStuffHere() } propertyB { DoStuffHere() } delete { DoStuffWhenClassADeleted() }; ", + example = "class Foo
" + "{
" + " int box;
" + " property int box { watchable set { box = value; } get { return box; } };
" + "}
" + "
" + "class Bar
" + "{
" + " watch foo { box { PrintLn(“box changed!â€); } delete { PrintLn(“foo is gone!â€); } };
" + "}
" + "
" + "Foo foo {};
" + "Bar bar {};", + also = "eInstance_WatchDestruction(), eInstance_StopWatching(), eProperty_Watchable()" + } } ] } diff --git a/doc/ecere/ecere/gfx/Bitmap.econ b/doc/ecere/ecere/gfx/Bitmap.econ index a04c0536e3..7febca195b 100644 --- a/doc/ecere/ecere/gfx/Bitmap.econ +++ b/doc/ecere/ecere/gfx/Bitmap.econ @@ -1,50 +1,22 @@ { name = "Bitmap", description = "Class reprsenting a rasterized 2D image in a number of formats. Once made device-dependent, the data is stored as a texture on the GPU for hardware- accelerated drivers.", - fields = [ - { - "height", - { - description = "The Height in pixels of the image." - } - }, - { - "pixelFormat", - { - description = "The format of the color encoding for the pixels of this image." - } - }, - { - "size", - { - description = "The exact size of the image." - } - }, - { - "sizeBytes", - { - description = "The size of the image in bytes." - } - }, - { - "transparent", - { - description = "When true, the transparency layer of the image is recognized. When the image is later blitted to the screen the transparent regions of the image are not drawn." - } - }, - { - "width", - { - description = "The Width in pixels of the image." - } - } - ], - methods = [ - { - "Load", - { - description = "Load the bitmap file from the included resources." - } - } - ] + fields = [ {"height", { + description = "The Height in pixels of the image." + }}, {"pixelFormat", { + description = "The format of the color encoding for the pixels of this image." + }}, {"size", { + description = "The exact size of the image." + }}, {"sizeBytes", { + description = "The size of the image in bytes." + }}, {"transparent", { + description = "When true, the transparency layer of the image is recognized. When the image is later blitted to the screen the transparent regions of the image are not drawn." + }}, {"width", { + description = "The Width in pixels of the image." + }} ], + methods = [ {"Free", { + description = "This doesn't need to be called before the displaySystem is lost, since Bitmaps get associated with the displaySystem, and the displaySystem will Free them automatically when it is lost" + }}, {"Load", { + description = "Load the bitmap file from the included resources." + }} ] } diff --git a/doc/ecere/ecere/gfx/BitmapResource.econ b/doc/ecere/ecere/gfx/BitmapResource.econ index 107ee0ef21..f2d8cf4790 100644 --- a/doc/ecere/ecere/gfx/BitmapResource.econ +++ b/doc/ecere/ecere/gfx/BitmapResource.econ @@ -2,7 +2,19 @@ name = "BitmapResource", description = "A class to automatically manag loading and unloading Bitmap objects", usage = "Originally defined in the declaration section of code, typically within the class that will be using this resource.", - example = "class Form1 : Window
{
text = \"Form1\";
background = activeBorder;
borderStyle = fixed;
hasClose = true;
size = {640, 480};
BitmapResource image { \":image.png\", window = this };
void OnRedraw(Surface surface)
{
surface.Blit( image.bitmap, 0, 0, 0, 0, 20, 20 );
}
}", + example = "class Form1 : Window
" + "{
" + " text = \"Form1\";
" + " background = activeBorder;
" + " borderStyle = fixed;
" + " hasClose = true;
" + " size = {640, 480};
" + " BitmapResource image { \":image.png\", window = this };
" + " void OnRedraw(Surface surface)
" + " {
" + " surface.Blit( image.bitmap, 0, 0, 0, 0, 20, 20 );
" + " }
" + "}", remarks = "Very similar to the Bitmap class. In fact Bitmap could be said to be low level, while BitmapResource could be said to be a high level object.", also = "Bitmap, BitmapFormat", properties = [ diff --git a/doc/ecere/ecere/gfx/Color.econ b/doc/ecere/ecere/gfx/Color.econ index 9ec820b3b7..2afb108ddc 100644 --- a/doc/ecere/ecere/gfx/Color.econ +++ b/doc/ecere/ecere/gfx/Color.econ @@ -5,32 +5,14 @@ example = "Color red = { r = 255 }; // High intensity red.
" "Color yellow = { 0xFFFF00 }; // High intensity yellow. r = 0xFF(255), g = 0xFF(255), b = 0x00(0)", also = "Color444, Color555, Color565, ColorAlpha, ColorCMYK, ColorHSV, ColorKey, ColorLab, ColorRGB, ColorRGBA, DefinedColor, PixelFormat, SystemColor", - fields = [ - { - "b", - { - description = "The green component of the color. A value between 0 and 255(0x0 and 0xFF)." - } - }, - { - "g", - { - description = "The green component of the color. A value between 0 and 255(0x0 and 0xFF)." - } - }, - { - "r", - { - description = "The red component of the color. A value between 0 and 255(0x0 and 0xFF)." - } - } - ], - conversions = [ - { - "ColorRGB", - { - description = "Enables the conversion from the Color type, to the ColorRGB type where each color element is stored as a floating point number." - } - } - ] + fields = [ {"b", { + description = "The green component of the color. A value between 0 and 255(0x0 and 0xFF)." + }}, {"g", { + description = "The green component of the color. A value between 0 and 255(0x0 and 0xFF)." + }}, {"r", { + description = "The red component of the color. A value between 0 and 255(0x0 and 0xFF)." + }} ], + conversions = [ {"ColorRGB", { + description = "Enables the conversion from the Color type, to the ColorRGB type where each color element is stored as a floating point number." + }} ] } diff --git a/doc/ecere/ecere/gfx/Surface.econ b/doc/ecere/ecere/gfx/Surface.econ index a372b70d11..a48148bfb0 100644 --- a/doc/ecere/ecere/gfx/Surface.econ +++ b/doc/ecere/ecere/gfx/Surface.econ @@ -87,6 +87,154 @@ } ] } + }, + { + "Filter", + { + description = "This function copies a section of a srouce Bitmap, scales it, and copies it to a the surface.", + parameters = [ + { + "dx", + { + description = "the starting x position in the destination surface", + position = 2 + } + }, + { + "dy", + { + description = "the starting y position in the destination surface", + position = 3 + } + }, + { + "h", + { + description = "The height of the image on the destination surface", + position = 7 + } + }, + { + "sh", + { + description = "The height of the region of the source image to read", + position = 9 + } + }, + { + "src", + { + description = "The source image to read from", + position = 1 + } + }, + { + "sw", + { + description = "The width of the region of the source image to read", + position = 8 + } + }, + { + "sx", + { + description = "The x position to start reading from in the source image", + position = 4 + } + }, + { + "sy", + { + description = "The y position to start reading from in the source image", + position = 5 + } + }, + { + "w", + { + description = "The width of the image on the destination surface", + position = 6 + } + } + ] + } + }, + { + "Stretch", + { + description = "Draws an image from a source Bitmap to a destination Surface, stretching the image in either dimension in the process.", + example = "// Assuming the Surface is called \"surface\" and there is a Bitmap called \"bitmap\"
" + "
" + "surface.Stretch(bitmap, 0, 0, 0, 0, 64, 64, 32, 32);
" + "
" + "// This should take a 32x32 pixel section of the source Bitmap and draw it onto the Surface, stretching each dimension to 64 pixels in the process.", + also = "Blit(), Filter()", + parameters = [ + { + "dx", + { + description = "The top left x coordinate of the destination.", + position = 2 + } + }, + { + "dy", + { + description = "The top left y coordinate of the destination.", + position = 3 + } + }, + { + "h", + { + description = "The height that the imageselection will be stretched to.", + position = 7 + } + }, + { + "sh", + { + description = "The height of the source image selection.", + position = 9 + } + }, + { + "src", + { + description = "The source Bitmap to be copied from", + position = 1 + } + }, + { + "sw", + { + description = "The width of the source image selection.", + position = 8 + } + }, + { + "sx", + { + description = "The top left x coordinate from the source.", + position = 4 + } + }, + { + "sy", + { + description = "The top left y coordinate from the source.", + position = 5 + } + }, + { + "w", + { + description = "The width that the image selection will be stretched to.", + position = 6 + } + } + ] + } } ] } diff --git a/doc/ecere/ecere/gfx3D/Material.econ b/doc/ecere/ecere/gfx3D/Material.econ new file mode 100644 index 0000000000..d9b66ea826 --- /dev/null +++ b/doc/ecere/ecere/gfx3D/Material.econ @@ -0,0 +1,5 @@ +{ + methods = [ {"Free", { + description = "This only needs to be called if a \"name\" was set; otherwise it can just be deleted when it is done being used" + }} ] +} diff --git a/doc/ecere/ecere/gfx3D/Mesh.econ b/doc/ecere/ecere/gfx3D/Mesh.econ new file mode 100644 index 0000000000..aef3d6de9f --- /dev/null +++ b/doc/ecere/ecere/gfx3D/Mesh.econ @@ -0,0 +1,49 @@ +{ + description = "3D mesh of vertices with applied texture.", + properties = [ + { + "groups", + { + description = "List of primitive groups in the mesh." + } + }, + { + "normals", + { + description = "Array that stores the normals for the mesh. Note that these are stored at each vertex, so the normal vector should be the average of the normals of all faces adjoining the vertex." + } + }, + { + "texCoords", + { + description = "Array that maps coordinates in the texture to verticies on the mesh. Each element in this array corresponds to the element at the same position in the vertices array." + } + }, + { + "vertices", + { + description = "Array that stores the positions of the vertices of the mesh" + } + } + ], + methods = [ + { + "AddPrimitiveGroup", + { + description = "Creates a grouping of verticies within the mesh" + } + }, + { + "Free", + { + description = "Frees the mesh from the display" + } + }, + { + "Unlock", + { + description = "Uploads mesh data to the GPU" + } + } + ] +} diff --git a/doc/ecere/ecere/gfx3D/Object.econ b/doc/ecere/ecere/gfx3D/Object.econ new file mode 100644 index 0000000000..8ec54285bd --- /dev/null +++ b/doc/ecere/ecere/gfx3D/Object.econ @@ -0,0 +1,38 @@ +{ + description = "3D model class, not to be confused with the \"object\" concept in object-oriented programming, which these Objects (note the capitalization) also happen to be.", + properties = [ {"firstChild", { + description = "Reference to the first of the children in the Object hierarchy" + }}, {"material", { + description = "Material that will be applied this Object's surface" + }}, {"mesh", { + description = "The mesh of the Object, composed of vertices, normals, and texture coordinates" + }}, {"name", { + description = "Name of the Object, used for referencing it" + }}, {"next", { + description = "Reference to the next child adjacent to this Object in the hierarchy" + }}, {"numChildren", { + description = "Number of first generation children this Object has in it's hierarchy" + }}, {"parent", { + description = "The parent Object of this Object in the Object hierarchy" + }}, {"tag", { + description = "pointer that can be set to link to through picking" + }}, {"transform", { + description = "Position, scaling, and orientation of the Object" + }} ], + methods = [ {"Add", { + description = "Adds a child Object without needing a name" + }}, {"AddName", { + description = "Adds a child Object with a name to reference it by", + remarks = "The name String will make it's own copy of the String, so the parameter can be deleted afterwards calling this function." + }}, {"Duplicate", { + description = "Copies an Object, but shares the same mesh, saving space and time" + }}, {"Find", { + description = "Looks up and returns a child Object based on its name " + }}, {"Free", { + description = "Frees the Object and all of its children. This should be called on an Object before the displaySystem that the Object is associated with is lost." + }}, {"Load", { + description = "Loads a model file into an Object" + }}, {"UpdateTransform", { + description = "Must be called after modifying the Object transform for the changes to take effect. This is done as a separate step from editing the transform to allow Updating the transform all at once, as opposed to having to call it every single time a small change is made to the transform." + }} ] +} diff --git a/doc/ecere/ecere/gfx3D/ObjectFlags.econ b/doc/ecere/ecere/gfx3D/ObjectFlags.econ new file mode 100644 index 0000000000..b2a06046bc --- /dev/null +++ b/doc/ecere/ecere/gfx3D/ObjectFlags.econ @@ -0,0 +1,16 @@ +{ + fields = [ + { + "root", + { + description = "Whether the Object is the root of the Object hierarchy or not" + } + }, + { + "translucent", + { + description = "Whether this Object is translucent or opaque" + } + } + ] +} diff --git a/doc/ecere/ecere/gui/GuiApplication.econ b/doc/ecere/ecere/gui/GuiApplication.econ new file mode 100644 index 0000000000..4605e246b8 --- /dev/null +++ b/doc/ecere/ecere/gui/GuiApplication.econ @@ -0,0 +1,36 @@ +{ + properties = [ + { + "driver", + { + description = "Graphics driver that will be used by the program" + } + }, + { + "timerResolution", + { + description = "Minimum refresh rate, assuming the hardware allows" + } + } + ], + methods = [ + { + "Cycle", + { + description = "Function that gets called in a constant loop, along with input and OnRedraw, in this order: UpdateDisplay() is called, then ProcessInput(), then Cycle(), and on and on it loops..." + } + }, + { + "GetKeyState", + { + description = "Returns true if the key specified in the parameter is currently depressed" + } + }, + { + "GetMouseState", + { + description = "Fills the passed parameters with the position and clicked state of any mouse buttons" + } + } + ] +} diff --git a/doc/ecere/ecere/gui/Modifiers.econ b/doc/ecere/ecere/gui/Modifiers.econ new file mode 100644 index 0000000000..e1886bc570 --- /dev/null +++ b/doc/ecere/ecere/gui/Modifiers.econ @@ -0,0 +1,55 @@ +{ + example = "Inside of a Button's NotifyClicked function, the Modifier can be used to do something dependent on the Modifiers' values:
" + "
" + " bool NotifyClicked(Button button, int x, int y, Modifiers mods)
" + " {
" + " if(mods.shift) DoMyFunction();
" + " return true;
" + " }
" + "
" + "This will perform \"DoMyFunction()\" only if the shift key had been held while clicking the Button.", + fields = [ + { + "alt", + { + description = "Indicates whether the alt key had been held at the time of the event" + } + }, + { + "cmd", + { + description = "Indicates whether the cmd key had been held at the time of the event" + } + }, + { + "ctrl", + { + description = "Indicates whether the ctrl key had been held at the time of the event" + } + }, + { + "left", + { + description = "Indicates whether the left mouse button had been held at the time of the event" + } + }, + { + "middle", + { + description = "Indicates whether the middle mouse button had been held at the time of the event" + } + }, + { + "right", + { + description = "Indicates whether the right mouse button had been held at the time of the event" + } + }, + { + "shift", + { + description = "Indicates whether the shift key had been held at the time of the event" + } + } + ] +} diff --git a/doc/ecere/ecere/gui/Window.econ b/doc/ecere/ecere/gui/Window.econ index 2127e2906f..b359372ca5 100644 --- a/doc/ecere/ecere/gui/Window.econ +++ b/doc/ecere/ecere/gui/Window.econ @@ -1,360 +1,206 @@ { name = "Window", description = "Base class for all GUI elements (controls, dialogs and forms)", - fields = [ - { - "sbh", - { - description = "This is the instance of the horizontal ScrollBar. Generally only used internally by eC." - } - }, - { - "sbv", - { - description = "This is the instance of the vertical ScrollBar. Generally only used internally by eC." - } - } - ], - properties = [ - { - "alphaBlend", - { - description = "Set to true to blend with the desktop environment's. Support & performance varies." - } - }, - { - "anchor", - { - description = "Very similar to position, only with more versatility. Anchor allows you to set the top, left, bottom, and right settings relative to the top, left, bottom and right of the Window/Controls parent's clientArea." - } - }, - { - "background", - { - description = "Color the client area is filled with" - } - }, - { - "borderStyle", - { - description = "The type of Window frame your Window has." - } - }, - { - "caption", - { - description = "The text which should appear in the caption of this window. For forms/dialogs with title bars, this will appear in the title bar. For controls, this is typically the text which should appear in the label associated with th control through the 'labeledWindow' property." - } - }, - { - "clickThrough", - { - description = "When true, this Window/Control redirects mouse clicks to its parent rather than processing the mouse clicks itself. Essentially, the mouse becomes invisible to this item." - } - }, - { - "clientSize", - { - description = "Sets the size of the interior of the Window; the Client Area.
" - "Example: clientSize = { 190, 290 };" - } - }, - { - "closeButton", - { - description = "This is the control for the CloseButton at the top right of the Window." - } - }, - { - "disabled", - { - description = "Greys out ALL controls within the Window making them unusable, including the controls on the TitleBar. When applied to an individual control, rather than an entire Window, then only that control is greyed out an unusable." - } - }, - { - "dontHideScroll", - { - description = "When true, the Horizontal and Vertical Scroll Bars are not hidden when all the contents are within the viewspace." - } - }, - { - "drawBehind", - { - description = "When true(default), then the contents of the screen behind the Window are still redrawn. If false, then the screen behind is not redrawn, which can leave \"ghosts\" of the Window on the screen when it is moved or resized." - } - }, - { - "font", - { - description = "Default font used for rendering the text within this Window and its controls.." - } - }, - { - "foreground", - { - description = "The Color of the text in the Client Area.
" - "Example: foreground = { r = 0, g = 0, b = 0 };" - } - }, - { - "hasClose", - { - description = "When true, the Window will contain a Close button on the top right side of the Title Bar." - } - }, - { - "hasHorzScroll", - { - description = "When true, the Window has a Horizontal Scroll bar across its bottom. When the contents of the Window exceeds its horizontal borders." - } - }, - { - "hasMaximize", - { - description = "When true, the Window will contain a Maximize button on the right side of the Title Bar." - } - }, - { - "hasMenuBar", - { - description = "When true, the Window will contain a Menu Bar immediately below the Title Bar." - } - }, - { - "hasMinimize", - { - description = "When true, the Window will contain a Minimize button on the right side of the Title Bar." - } - }, - { - "hasStatusBar", - { - description = "When true, the Window will contain a Status Bar at the bottom." - } - }, - { - "hasVertScroll", - { - description = "When true, the Window has a Vertical Scroll bar along the right hand side. When the contents of the Window exceeds its vertical borders." - } - }, - { - "hotKey", - { - description = "The key associated with a particular Window or Control. Pressing this key, or key combination will automatically activate the associated Window or Control.
" - "Note: If the tabCycle property of the parent is not true, then hotKey will not work unless the parent is active." - } - }, - { - "id", - { - description = "An integer id to uniquely identify the window (e.g. when sharing method implementations among multiple controls), or for storing arbitrary user data." - } - }, - { - "isDocument", - { - description = "When true, this Window/Control would be treated as a document, enabling things like having the path show up in the Title Bar.
" - "Note: For best results, set this property true on a Window rather than on a Control." - } - }, - { - "master", - { - description = "The owner of this Window which should receive notifications (e.g. if this Window is a control)." - } - }, - { - "maxClientSize", - { - description = "The Maximum allowable size for the Client Area within the window.
" - "Example: maxClientSize = { 120, 400 };" - } - }, - { - "maximizeButton", - { - description = "This is the control for the MaximizeButton at the top right of the Window." - } - }, - { - "menu", - { - description = "The Menu portion of the Window." - } - }, - { - "minClientSize", - { - description = "The Minimum allowable size for the Client Area within the window.
" - "Example: minClientSize = { 50, 50 };" - } - }, - { - "minimizeButton", - { - description = "This is the control for the MinimizeButton at the top right of the Window." - } - }, - { - "name", - { - description = "(internal field for the Ecere IDE's Object Designer which matches the instance or class name)" - } - }, - { - "noCycle", - { - description = "When true, then the particular control which this is set on becomes exempt from the Tab cycling normally available from it's parents tabCycle property." - } - }, - { - "nonClient", - { - description = "When true, enables the Window/Control to inhabit a region outside of the clientArea, but still constrained to the Parent. Normally this is used for system controls like the buttons in the Title Bar." - } - }, - { - "normalAnchor", - { - description = "The anchor of the Window in its normal state, that is not minimized or maximized." - } - }, - { - "opacity", - { - description = "A value between 0.0f and 1.0f specifying the opacity which wthich the client area's background will be filled using the background color." - } - }, - { - "parent", - { - description = "The Window within whose client area this window is constrained." - } - }, - { - "position", - { - description = "The position of a Window/Control relative to the top left client area of the Parent.
" - "Example: position = { 10, 10 };" - } - }, - { - "size", - { - description = "Sets the size of the Window, including all the decorations.
" - "Example: size = { 200, 300 };" - } - }, - { - "state", - { - description = "Sets the state of the Window, either Normal, Minimized, or Maximized.
" - "state = Normal;" - } - }, - { - "stayOnTop", - { - description = "When true, the Window will stay on top of all other windows. Does not apply to controls." - } - }, - { - "tabCycle", - { - description = "When true, then it becomes possible to use the tab key to cycle through all of the children of this Window/Control. The order of cycling is determined by the order of instantiation. Note: If tabCycle is not true on the parent, then children with hotKeys set, will not be activated by pressing the hotKey, unless the parent is currently active." - } - }, - { - "text", - { - description = "Deprecated. Use 'caption'." - } - }, - { - "visible", - { - description = "When true, the Window/Control is actually visible on the screen." - } - } - ], - methods = [ - { - "Destroy", - { - description = "Destroys the current Window/Control.", - usage = "Used in the statement section of the source code.", - example = "Window
" - "{
" - " text = \"Destroy Example\";
" - " Button
" - " {
" - " text = \"Destroy this Window!\";", - parameters = [ - { - "code", - { - description = "0", - position = 1 - } - } - ] - } - }, - { - "OnCreate", - { - description = "Called any time a Window is created. And can be overridden to perform actions at the time that a window is created.", - returnValue = "When true, the Window creation was successful." - } - }, - { - "OnRedraw", - { - description = "This method is called anytime the Window is redrawn. Usually when it is resized or moved, however can also be called manually by calling Update() to redraw the contents of the window.", - usage = "Use this method by overriding it in your own code, within the Window class that it belongs to.", - example = "class Form1 : Window
" - "{
" - " void OnRedraw(Surface surface)
" - " {
" - " surface.blit( image, xdest, ydest, sourceX, sourceY, sourceW, sourceH );
" - " }
" - "}", - remarks = "Typically this method is responsible for the actual drawing of all the graphics for your program.", - also = "OnApplyGraphics(), OnLoadGraphics(), OnResize(), OnResizing(), Update(), Window", - parameters = [ - { - "surface", - { - description = "The graphical context which is obtained for this window, which will be drawn to.", - position = 1 - } - } - ] - } - }, - { - "Update", - { - description = "Causes the OnRedraw() method of the Window to be called.", - usage = "Used in the statement section of the code.", - example = "class Form1 : Window
" - "{
" - " Update( null ); // update the entire window
" - " Update( { 0, 0, 50, 50 } ); // update the region from 0 to 50 on the x axis, and 0 to 50 on the y axis.
" - "} ", - remarks = "One should only update the necessary areas when possible, as updating the entire window indiscriminately can hog precious CPU cycles.", - also = "UpdateDisplay(), OnRedraw()", - parameters = [ - { - "region", - { - description = "Defines the area of the Window to be updated. If null is specified, then the entire window is updated.", - position = 1 - } - } - ] - } - } - ] + fields = [ {"sbh", { + description = "This is the instance of the horizontal ScrollBar. Generally only used internally by eC." + }}, {"sbv", { + description = "This is the instance of the vertical ScrollBar. Generally only used internally by eC." + }} ], + properties = [ {"alphaBlend", { + description = "Set to true to blend with the desktop environment's. Support & performance varies." + }}, {"anchor", { + description = "Very similar to position, only with more versatility. Anchor allows you to set the top, left, bottom, and right settings relative to the top, left, bottom and right of the Window/Controls parent's clientArea." + }}, {"background", { + description = "Color the client area is filled with" + }}, {"borderStyle", { + description = "The type of Window frame your Window has." + }}, {"caption", { + description = "The text which should appear in the caption of this window. For forms/dialogs with title bars, this will appear in the title bar. For controls, this is typically the text which should appear in the label associated with th control through the 'labeledWindow' property." + }}, {"clickThrough", { + description = "When true, this Window/Control redirects mouse clicks to its parent rather than processing the mouse clicks itself. Essentially, the mouse becomes invisible to this item." + }}, {"clientSize", { + description = "Sets the size of the interior of the Window; the Client Area.
" + "Example: clientSize = { 190, 290 };" + }}, {"closeButton", { + description = "This is the control for the CloseButton at the top right of the Window." + }}, {"disabled", { + description = "Greys out ALL controls within the Window making them unusable, including the controls on the TitleBar. When applied to an individual control, rather than an entire Window, then only that control is greyed out an unusable." + }}, {"dontHideScroll", { + description = "When true, the Horizontal and Vertical Scroll Bars are not hidden when all the contents are within the viewspace." + }}, {"drawBehind", { + description = "When true(default), then the contents of the screen behind the Window are still redrawn. If false, then the screen behind is not redrawn, which can leave \"ghosts\" of the Window on the screen when it is moved or resized." + }}, {"font", { + description = "Default font used for rendering the text within this Window and its controls.." + }}, {"foreground", { + description = "The Color of the text in the Client Area.
" + "Example: foreground = { r = 0, g = 0, b = 0 };" + }}, {"hasClose", { + description = "When true, the Window will contain a Close button on the top right side of the Title Bar." + }}, {"hasHorzScroll", { + description = "When true, the Window has a Horizontal Scroll bar across its bottom. When the contents of the Window exceeds its horizontal borders." + }}, {"hasMaximize", { + description = "When true, the Window will contain a Maximize button on the right side of the Title Bar." + }}, {"hasMenuBar", { + description = "When true, the Window will contain a Menu Bar immediately below the Title Bar." + }}, {"hasMinimize", { + description = "When true, the Window will contain a Minimize button on the right side of the Title Bar." + }}, {"hasStatusBar", { + description = "When true, the Window will contain a Status Bar at the bottom." + }}, {"hasVertScroll", { + description = "When true, the Window has a Vertical Scroll bar along the right hand side. When the contents of the Window exceeds its vertical borders." + }}, {"hotKey", { + description = "The key associated with a particular Window or Control. Pressing this key, or key combination will automatically activate the associated Window or Control.
" + "Note: If the tabCycle property of the parent is not true, then hotKey will not work unless the parent is active." + }}, {"id", { + description = "An integer id to uniquely identify the window (e.g. when sharing method implementations among multiple controls), or for storing arbitrary user data." + }}, {"isDocument", { + description = "When true, this Window/Control would be treated as a document, enabling things like having the path show up in the Title Bar.
" + "Note: For best results, set this property true on a Window rather than on a Control." + }}, {"master", { + description = "The owner of this Window which should receive notifications (e.g. if this Window is a control)." + }}, {"maxClientSize", { + description = "The Maximum allowable size for the Client Area within the window.
" + "Example: maxClientSize = { 120, 400 };" + }}, {"maximizeButton", { + description = "This is the control for the MaximizeButton at the top right of the Window." + }}, {"menu", { + description = "The Menu portion of the Window." + }}, {"minClientSize", { + description = "The Minimum allowable size for the Client Area within the window.
" + "Example: minClientSize = { 50, 50 };" + }}, {"minimizeButton", { + description = "This is the control for the MinimizeButton at the top right of the Window." + }}, {"name", { + description = "(internal field for the Ecere IDE's Object Designer which matches the instance or class name)" + }}, {"noCycle", { + description = "When true, then the particular control which this is set on becomes exempt from the Tab cycling normally available from it's parents tabCycle property." + }}, {"nonClient", { + description = "When true, enables the Window/Control to inhabit a region outside of the clientArea, but still constrained to the Parent. Normally this is used for system controls like the buttons in the Title Bar." + }}, {"normalAnchor", { + description = "The anchor of the Window in its normal state, that is not minimized or maximized." + }}, {"opacity", { + description = "A value between 0.0f and 1.0f specifying the opacity which wthich the client area's background will be filled using the background color." + }}, {"parent", { + description = "The Window within whose client area this window is constrained." + }}, {"position", { + description = "The position of a Window/Control relative to the top left client area of the Parent.
" + "Example: position = { 10, 10 };" + }}, {"size", { + description = "Sets the size of the Window, including all the decorations.
" + "Example: size = { 200, 300 };" + }}, {"state", { + description = "Sets the state of the Window, either Normal, Minimized, or Maximized.
" + "state = Normal;" + }}, {"stayOnTop", { + description = "When true, the Window will stay on top of all other windows. Does not apply to controls." + }}, {"tabCycle", { + description = "When true, then it becomes possible to use the tab key to cycle through all of the children of this Window/Control. The order of cycling is determined by the order of instantiation. Note: If tabCycle is not true on the parent, then children with hotKeys set, will not be activated by pressing the hotKey, unless the parent is currently active." + }}, {"text", { + description = "Deprecated. Use 'caption'." + }}, {"visible", { + description = "When true, the Window/Control is actually visible on the screen." + }} ], + methods = [ {"AcquireInput", { + description = "Acquires the mouse input" + }}, {"Create", { + description = "Creates the Window in the display " + }}, {"Destroy", { + description = "Destroys the current Window/Control.", + usage = "Used in the statement section of the source code.", + example = "Window
" + "{
" + " text = \"Destroy Example\";
" + " Button
" + " {
" + " text = \"Destroy this Window!\";", + parameters = [ {"code", { + description = "0", + position = 1 + }} ] + }}, {"OnClose", { + description = "Called when the window is set to be closed. Making this return false will prevent the window from closing." + }}, {"OnCreate", { + description = "Called any time a Window is created. And can be overridden to perform actions at the time that a window is created.", + returnValue = "When true, the Window creation was successful." + }}, {"OnDestroy", { + description = "Called when the Window is flagged for destruction, but before destruction has completed" + }}, {"OnDestroyed", { + description = "Called after destruction of the Window has completed." + }}, {"OnKeyDown", { + description = "Called when any key is pressed" + }}, {"OnKeyHit", { + description = "Called when any key is pressed and released" + }}, {"OnKeyUp", { + description = "Called when any key is released" + }}, {"OnLeftButtonDown", { + description = "Called when the left mouse button is pressed" + }}, {"OnLeftButtonUp", { + description = "Called when the left mouse button is released" + }}, {"OnLeftDoubleClick", { + description = "Called when the left mouse button is double-clicked" + }}, {"OnLoadGraphics", { + description = "Called when the graphics are loaded" + }}, {"OnMiddleButtonDown", { + description = "Called when the middle mouse button is pressed" + }}, {"OnMiddleButtonUp", { + description = "Called when the middle mouse button is released" + }}, {"OnMiddleDoubleClick", { + description = "Called when the middle mouse button is double-clicked" + }}, {"OnMouseCaptureLost", { + description = "Called when the mouse capture is lost" + }}, {"OnMouseLeave", { + description = "Called when the mouse leaves the Window area" + }}, {"OnMouseMove", { + description = "Called when the mouse moves" + }}, {"OnMouseOver", { + description = "Called when the mouse moves over this Window" + }}, {"OnMultiTouch", { + description = "Used to handle multi-touch events for touch devices" + }}, {"OnRedraw", { + description = "This method is called anytime the Window is redrawn. Usually when it is resized or moved, however can also be called manually by calling Update() to redraw the contents of the window.", + usage = "Use this method by overriding it in your own code, within the Window class that it belongs to.", + example = "class Form1 : Window
" + "{
" + " void OnRedraw(Surface surface)
" + " {
" + " surface.blit( image, xdest, ydest, sourceX, sourceY, sourceW, sourceH );
" + " }
" + "}", + remarks = "Typically this method is responsible for the actual drawing of all the graphics for your program.", + also = "OnApplyGraphics(), OnLoadGraphics(), OnResize(), OnResizing(), Update(), Window", + parameters = [ {"surface", { + description = "The graphical context which is obtained for this window, which will be drawn to.", + position = 1 + }} ] + }}, {"OnResize", { + description = "Called when the Window is resized" + }}, {"OnRightButtonDown", { + description = "Called when the right mouse button is pressed" + }}, {"OnRightButtonUp", { + description = "Called when the right mouse button is released" + }}, {"OnRightDoubleClick", { + description = "Called when the right mouse button is double-clicked" + }}, {"OnSysKeyDown", { + description = "Called when a system key is pressed" + }}, {"OnSysKeyHit", { + description = "Called when a system key is pressed and released" + }}, {"OnSysKeyUp", { + description = "Called when a system key is released" + }}, {"OnUnloadGraphics", { + description = "Called when the graphics are unloaded" + }}, {"Update", { + description = "Causes the OnRedraw() method of the Window to be called.", + usage = "Used in the statement section of the code.", + example = "class Form1 : Window
" + "{
" + " Update( null ); // update the entire window
" + " Update( { 0, 0, 50, 50 } ); // update the region from 0 to 50 on the x axis, and 0 to 50 on the y axis.
" + "} ", + remarks = "One should only update the necessary areas when possible, as updating the entire window indiscriminately can hog precious CPU cycles.", + also = "UpdateDisplay(), OnRedraw()", + parameters = [ {"region", { + description = "Defines the area of the Window to be updated. If null is specified, then the entire window is updated.", + position = 1 + }} ] + }} ] } diff --git a/doc/ecere/ecere/gui/controls/Button.econ b/doc/ecere/ecere/gui/controls/Button.econ new file mode 100644 index 0000000000..8966863f62 --- /dev/null +++ b/doc/ecere/ecere/gui/controls/Button.econ @@ -0,0 +1,78 @@ +{ + properties = [ + { + "bevel", + { + description = "Whether the Button has a default bevel image" + } + }, + { + "bitmap", + { + description = "Image for the Button" + } + }, + { + "checked", + { + description = "Whether the checkbox is checked or not; see isCheckBox flag" + } + }, + { + "isCheckbox", + { + description = "Makes this Button a checkbox" + } + }, + { + "opacity", + { + description = "The opacity of the Button" + } + } + ], + methods = [ + { + "NotifyClicked", + { + description = "Called when the button is clicked. The return value is deprecated, but to be safe, always set it to true. In the future, this will return void." + } + }, + { + "NotifyDoubleClick", + { + description = "Called when the Button is double-clicked." + } + }, + { + "NotifyMouseLeave", + { + description = "Called when the mouse ceases to be over the Button." + } + }, + { + "NotifyMouseMove", + { + description = "Called when the mouse is moved." + } + }, + { + "NotifyMouseOver", + { + description = "Called when the mouse comes over the Button." + } + }, + { + "NotifyPushed", + { + description = "Called when the Button is pressed." + } + }, + { + "NotifyReleased", + { + description = "Called when the Button ceases to be pressed." + } + } + ] +} diff --git a/doc/ecere/ecere/gui/controls/EditBox.econ b/doc/ecere/ecere/gui/controls/EditBox.econ index 3bb08dab7f..16c4b24cf4 100644 --- a/doc/ecere/ecere/gui/controls/EditBox.econ +++ b/doc/ecere/ecere/gui/controls/EditBox.econ @@ -2,137 +2,58 @@ name = "EditBox", description = "An empty box, allowing users to input data.", remarks = "Currently, Word Wrap is not fully implemented.", - properties = [ - { - "allCaps", - { - description = "When true, all data entered will be uppercase characters." - } - }, - { - "caretFollowsScrolling", - { - description = "When true, the Caret does not leave the visible area when it is scrolled." - } - }, - { - "contents", - { - description = "The contents of the Edit Box.
" - "Example: char * contentsOfEditBox = editBox1.contents;" - } - }, - { - "freeCaret", - { - description = "When true, the Caret is placed where the mouse is clicked, whether there is text there or not. The Caret cannot be placed on a line that does not yet have text." - } - }, - { - "maxLineSize", - { - description = "The maximum number of characters that a line can hold.
" - "Example: maxLineSize = 30;" - } - }, - { - "maxNumLines", - { - description = "The maximum number of lines that the Edit Box contains.
" - "Example: maxNumLines = 5;" - } - }, - { - "multiLine", - { - description = "When true, the user can enter input on more than one line in the Edit Box." - } - }, - { - "noCaret", - { - description = "When true, the Caret will not appear in the Edit Box. The property editor will automatically set the Edit Box to read only. This makes it so that the Edit Box can be used for read only information without the distraction or confusion of the Caret." - } - }, - { - "noSelect", - { - description = "When true, selection of data is disabled in the Edit Box." - } - }, - { - "overwrite", - { - description = "Returns true if the Insert key has been pressed, and false if it hasn't. This property cannot be set." - } - }, - { - "readOnly", - { - description = "When true, the text in the edit box cannot be edited and can only be modified using the contents property." - } - }, - { - "syntaxHighlighting", - { - description = "When true, text which is recognized as a key word in eC, a number, or a string, are coloured differently from the foreground colour." - } - }, - { - "tabKey", - { - description = "When true, the Tab Key is processed within the Edit Box." - } - }, - { - "tabSize", - { - description = "The number of spaces to jump when the Tab Key is pressed. Note: tabSize has no meaning if tabKey is not true." - } - }, - { - "textHorzScroll", - { - description = "When true, the text will scroll to the left while it is being entered, so that the caret is always visible. This also enables the user to manually scroll left and right on the line using the left and right arrows.
" - "When false, it is not possible to type past the width of the control." - } - }, - { - "textVertScroll", - { - description = "When true, the text can be scrolled vertically using the up and down arrows. For this to work, multiLine must also be true.
" - "When false, it is not possible to type past the bottom of the control." - } - }, - { - "wrap", - { - description = "When true, automatically wraps the text to the next line of a multiLine Edit Box when the caret encounters the right most side of the Edit Box. This wraps from the beginning of the last word on the line." - } - } - ], - methods = [ - { - "Load", - { - description = "Loads a file into the EditBox.", - usage = "Used in the statements section of code. Once an instantiation of the EditBox control has been made, then Load() would be accessed through the . operator.", - example = "File f = FileOpen(\"C:/fun.txt\", read);
" - "if(f)
" - "{
" - " myEditBox.Load(f);
" - " delete(f);
" - "}", - parameters = [ - { - "f", - { - description = "Any file already open.", - position = 1 - } - } - ] - } - } - ] + properties = [ {"allCaps", { + description = "When true, all data entered will be uppercase characters." + }}, {"caretFollowsScrolling", { + description = "When true, the Caret does not leave the visible area when it is scrolled." + }}, {"contents", { + description = "The contents of the Edit Box.
" + "Example: char * contentsOfEditBox = editBox1.contents;" + }}, {"freeCaret", { + description = "When true, the Caret is placed where the mouse is clicked, whether there is text there or not. The Caret cannot be placed on a line that does not yet have text." + }}, {"maxLineSize", { + description = "The maximum number of characters that a line can hold.
" + "Example: maxLineSize = 30;" + }}, {"maxNumLines", { + description = "The maximum number of lines that the Edit Box contains.
" + "Example: maxNumLines = 5;" + }}, {"multiLine", { + description = "When true, the user can enter input on more than one line in the Edit Box." + }}, {"noCaret", { + description = "When true, the Caret will not appear in the Edit Box. The property editor will automatically set the Edit Box to read only. This makes it so that the Edit Box can be used for read only information without the distraction or confusion of the Caret." + }}, {"noSelect", { + description = "When true, selection of data is disabled in the Edit Box." + }}, {"overwrite", { + description = "Returns true if the Insert key has been pressed, and false if it hasn't. This property cannot be set." + }}, {"readOnly", { + description = "When true, the text in the edit box cannot be edited and can only be modified using the contents property." + }}, {"syntaxHighlighting", { + description = "When true, text which is recognized as a key word in eC, a number, or a string, are coloured differently from the foreground colour." + }}, {"tabKey", { + description = "When true, the Tab Key is processed within the Edit Box." + }}, {"tabSize", { + description = "The number of spaces to jump when the Tab Key is pressed. Note: tabSize has no meaning if tabKey is not true." + }}, {"textHorzScroll", { + description = "When true, the text will scroll to the left while it is being entered, so that the caret is always visible. This also enables the user to manually scroll left and right on the line using the left and right arrows.
" + "When false, it is not possible to type past the width of the control." + }}, {"textVertScroll", { + description = "When true, the text can be scrolled vertically using the up and down arrows. For this to work, multiLine must also be true.
" + "When false, it is not possible to type past the bottom of the control." + }}, {"wrap", { + description = "When true, automatically wraps the text to the next line of a multiLine Edit Box when the caret encounters the right most side of the Edit Box. This wraps from the beginning of the last word on the line." + }} ], + methods = [ {"Load", { + description = "Loads a file into the EditBox.", + usage = "Used in the statements section of code. Once an instantiation of the EditBox control has been made, then Load() would be accessed through the . operator.", + example = "File f = FileOpen(\"C:/fun.txt\", read);
" + "if(f)
" + "{
" + " myEditBox.Load(f);
" + " delete(f);
" + "}", + parameters = [ {"f", { + description = "Any file already open.", + position = 1 + }} ] + }} ] } diff --git a/doc/ecere/ecere/sys/ECONParser.econ b/doc/ecere/ecere/sys/ECONParser.econ new file mode 100644 index 0000000000..deb685c34a --- /dev/null +++ b/doc/ecere/ecere/sys/ECONParser.econ @@ -0,0 +1,13 @@ +{ + description = "The ECONParser is used to read ECON files and parse the data into an object using the GetObject() method of the JSONParser base class", + example = "File f = FileOpen(\"myFile.econ\", read);
" + "if(f)
" + "{
" + " ECONParser { f = f }.GetObject(class(MyClass), myClass);
" + " delete f;
" + "}", + remarks = "This instantiates a new instance, so any existing instantiation for the handle will be leaked.
" + "
" + "Private members will be ignored.", + also = "WriteECONObject (for writing to an ECON file)" +} diff --git a/doc/ecere/ecere/sys/JSONParser.econ b/doc/ecere/ecere/sys/JSONParser.econ new file mode 100644 index 0000000000..7f54153760 --- /dev/null +++ b/doc/ecere/ecere/sys/JSONParser.econ @@ -0,0 +1,48 @@ +{ + description = "The JSONParser is used to read JSON files and parse the data into an object using the GetObject() method", + example = "File f = FileOpen(\"myFile.json\", read);
" + "if(f)
" + "{
" + " JSONParser { f = f }.GetObject(class(MyClass), myObject);
" + " delete f;
" + "}", + also = "WriteJSONObject (for writing to a JSON file)", + fields = [ + { + "f", + { + description = "This is the JSON file that the parser will read from" + } + } + ], + methods = [ + { + "GetObject", + { + description = "This method reads the JSON file and parses it into the object provided in the parameters.", + example = "File f = FileOpen(\"myFile.json\", read);
" + "if(f)
" + "{
" + " JSONParser { f = f }.GetObject(class(MyClass), myObject);
" + " delete f;
" + "}", + parameters = [ + { + "object", + { + description = "pointer to the pointer to the object to for the JSON data to be loaded into", + position = 2 + } + }, + { + "objectType", + { + description = "class type of the object that the JSON data will be loaded into", + position = 1 + } + } + ] + } + } + ] +} diff --git a/doc/ecere/ecere/sys/JSONResult.econ b/doc/ecere/ecere/sys/JSONResult.econ new file mode 100644 index 0000000000..78d9398152 --- /dev/null +++ b/doc/ecere/ecere/sys/JSONResult.econ @@ -0,0 +1,3 @@ +{ + description = "Success/Error code of JSON parse" +} diff --git a/doc/ecere/ecere/sys/_global-defs.econ b/doc/ecere/ecere/sys/_global-defs.econ index ca8923feaa..7375dd397a 100644 --- a/doc/ecere/ecere/sys/_global-defs.econ +++ b/doc/ecere/ecere/sys/_global-defs.econ @@ -1,49 +1,87 @@ { name = "sys", description = "Platform independent System API (Files, Multithreading, Unicode, ...)", - defines = [ - { - "ecere::sys::Pi", - { - description = "Ratio of a circle's circumference to its diameter; Measure of a half-circle angle in radians (180°)" - } - } - ], - functions = [ - { - "GetRandom", - { - description = "Returns a random integer from lo to hi inclusive.", - usage = "Returns a random integer, based on the Random Seed. If no Random Seed is set with RandomSeed(), then GetRandom() returns lo.", - example = " int Rand;
" - " RandomSeed((uint)(GetTime() * 1000));
" - " Rand = GetRandom(1, 10);
" - " printf(\"%d\
\", Rand); ", - also = "GetTime(), RandomSeed()" - } - }, - { - "GetTime", - { - description = "Returns the current system time as a Time object.", - example = " int Rand;
" - " RandomSeed((uint)(GetTime() * 1000));
" - " Rand = GetRandom(1, 10);
" - " printf(\"%d\
\", Rand); ", - also = "RandomSeed()" - } - }, - { - "RandomSeed", - { - description = "This method seeds the random number generator.", - usage = "This method needs to be called before any call to GetRandom(), otherwise GetRandom will not retrieve a random number. The seed can be any unsigned integer, however any constant value will produce predictable results each time. For closer to true results, try using GetTime() as the seed.", - example = " int Rand;
" - " RandomSeed((uint)(GetTime() * 1000));
" - " Rand = GetRandom(1, 10);
" - " printf(\"%d\
\", Rand); ", - also = "GetRandom(), GetTime()" - } - } - ] + defines = [ {"ecere::sys::Pi", { + description = "Ratio of a circle's circumference to its diameter; Measure of a half-circle angle in radians (180°)" + }} ], + functions = [ {"GetRandom", { + description = "Returns a random integer from lo to hi inclusive.", + usage = "Returns a random integer, based on the Random Seed. If no Random Seed is set with RandomSeed(), then GetRandom() returns lo.", + example = " int Rand;
" + " RandomSeed((uint)(GetTime() * 1000));
" + " Rand = GetRandom(1, 10);
" + " printf(\"%d
" + "\", Rand); ", + also = "GetTime(), RandomSeed()" + }}, {"GetTime", { + description = "Returns the current system time as a Time object.", + example = " int Rand;
" + " RandomSeed((uint)(GetTime() * 1000));
" + " Rand = GetRandom(1, 10);
" + " printf(\"%d
" + "\", Rand); ", + also = "RandomSeed()" + }}, {"MakeDir", { + description = "Creates a directory in the current folder", + example = "MakeDir(\"myDirectoryName\");", + parameters = [ {"path", { + description = "Path name for the directory to be created", + position = 1 + }} ] + }}, {"RandomSeed", { + description = "This method seeds the random number generator.", + usage = "This method needs to be called before any call to GetRandom(), otherwise GetRandom will not retrieve a random number. The seed can be any unsigned integer, however any constant value will produce predictable results each time. For closer to true results, try using GetTime() as the seed.", + example = " int Rand;
" + " RandomSeed((uint)(GetTime() * 1000));
" + " Rand = GetRandom(1, 10);
" + " printf(\"%d
" + "\", Rand); ", + also = "GetRandom(), GetTime()" + }}, {"StripExtension", { + description = "Removes the file extension from the string" + }}, {"WriteECONObject", { + description = "This function writes an object to an ECON file", + example = "File f = FileOpen(\"myFile.econ\", write);
" + "if(f)
" + "{
" + " WriteECONObject(f, class(MyClass), myClass, 0);
" + " delete f;
" + "}", + also = "ECONParser (for reading ECON files), WriteJSONObject (similar function but for writing to JSON files)", + parameters = [ {"f", { + description = "The ECON file to write the object to", + position = 1 + }}, {"indent", { + description = "How much indentation to add", + position = 4 + }}, {"object", { + description = "The object that will be written to the JSON file", + position = 3 + }}, {"objectType", { + description = "The class type of the object that will be written to the ECON file", + position = 2 + }} ] + }}, {"WriteJSONObject", { + description = "This function writes an object to a JSON file", + example = "File f = FileOpen(\"myFile.json\", write);
" + "if(f)
" + "{
" + " WriteJSONObject(f, class(MyClass), myClass, 0);
" + " delete f;
" + "}", + also = "JSONParser (for reading JSON files), WriteECONObject (similar function but for writing to ECON files)", + parameters = [ {"f", { + description = "The JSON file to write the object to", + position = 1 + }}, {"indent", { + description = "How much indentation to add", + position = 4 + }}, {"object", { + description = "The object that will be written to the JSON file", + position = 3 + }}, {"objectType", { + description = "The class type of the object that will be written to the JSON file", + position = 2 + }} ] + }} ] } diff --git a/doc/ecereCOM/String.econ b/doc/ecereCOM/String.econ index 7eee38491c..67b7a32698 100644 --- a/doc/ecereCOM/String.econ +++ b/doc/ecereCOM/String.econ @@ -1,3 +1,3 @@ { - description = "Rudimentary string type (resolved to a C 'char *') meant to hold a null-terminated string of characters. Ecere APIs taking a String or char * representing text expect UTF-8 encoding." + description = "Rudimentary string type (resolved to a C 'char *') meant to hold a null-terminated string of characters. Ecere APIs taking a String or char * representing text expect UTF-8 encoding." } diff --git a/documentor/Makefile b/documentor/Makefile index 927df5f5ba..a52039734d 100644 --- a/documentor/Makefile +++ b/documentor/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -49,6 +53,7 @@ _ECSOURCES = \ ../extras/html/lines.ec \ ../extras/html/tables.ec \ ../ide/src/IDESettings.ec \ + ../ide/src/designer/SyntaxColorScheme.ec \ ../ide/src/OldIDESettings.ec \ ../extras/gui/controls/StringsBox.ec \ src/Documentor.ec \ @@ -89,8 +94,13 @@ PRJ_CFLAGS += \ -DECERE_DOCUMENTOR ECFLAGS += -module $(MODULE) -ECFLAGS += \ - -nolinenumbers +ifndef DEBUG +ECFLAGS += -nolinenumbers +endif + +ifdef DISABLE_MEMMGR +ECFLAGS += -DDISABLE_MEMMGR +endif CECFLAGS += -cpp $(_CPP) @@ -105,7 +115,7 @@ endif all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS) $(ECS) $(ARCH_FLAGS) $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX) -o $(OBJ)$(MODULE).main.ec @@ -147,6 +157,9 @@ $(OBJ)tables.sym: ../extras/html/tables.ec $(OBJ)IDESettings.sym: ../ide/src/IDESettings.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c ../ide/src/IDESettings.ec -o $(OBJ)IDESettings.sym +$(OBJ)SyntaxColorScheme.sym: ../ide/src/designer/SyntaxColorScheme.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c ../ide/src/designer/SyntaxColorScheme.ec -o $(OBJ)SyntaxColorScheme.sym + $(OBJ)OldIDESettings.sym: ../ide/src/OldIDESettings.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c ../ide/src/OldIDESettings.ec -o $(OBJ)OldIDESettings.sym @@ -179,6 +192,9 @@ $(OBJ)IDESettings.c: ../ide/src/IDESettings.ec $(OBJ)IDESettings.sym | $(SYMBOLS $(OBJ)OldIDESettings.c: ../ide/src/OldIDESettings.ec $(OBJ)OldIDESettings.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c ../ide/src/OldIDESettings.ec -o $(OBJ)OldIDESettings.c -symbols $(OBJ) +$(OBJ)SyntaxColorScheme.c: ../ide/src/designer/SyntaxColorScheme.ec $(OBJ)SyntaxColorScheme.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c ../ide/src/designer/SyntaxColorScheme.ec -o $(OBJ)SyntaxColorScheme.c -symbols $(OBJ) + $(OBJ)StringsBox.c: ../extras/gui/controls/StringsBox.ec $(OBJ)StringsBox.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c ../extras/gui/controls/StringsBox.ec -o $(OBJ)StringsBox.c -symbols $(OBJ) @@ -205,6 +221,9 @@ $(OBJ)tables.o: $(OBJ)tables.c $(OBJ)IDESettings.o: $(OBJ)IDESettings.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)IDESettings.c -o $(OBJ)IDESettings.o +$(OBJ)SyntaxColorScheme.o: $(OBJ)SyntaxColorScheme.c + $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)SyntaxColorScheme.c -o $(OBJ)SyntaxColorScheme.o + $(OBJ)OldIDESettings.o: $(OBJ)OldIDESettings.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)OldIDESettings.c -o $(OBJ)OldIDESettings.o @@ -226,7 +245,7 @@ endif $(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.c -o $(OBJ)$(MODULE).main$(O) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -254,9 +273,12 @@ endif realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/documentor/documentor.epj b/documentor/documentor.epj index 22c4ca05a7..3473e21a56 100644 --- a/documentor/documentor.epj +++ b/documentor/documentor.epj @@ -73,7 +73,8 @@ "Folder" : "ide", "Files" : [ "../ide/src/IDESettings.ec", - "../ide/src/OldIDESettings.ec" + "../ide/src/OldIDESettings.ec", + "../ide/src/designer/SyntaxColorScheme.ec" ] }, "../extras/gui/controls/StringsBox.ec" diff --git a/documentor/src/Documentor.ec b/documentor/src/Documentor.ec index 28e8abd6e7..3f9b264e88 100644 --- a/documentor/src/Documentor.ec +++ b/documentor/src/Documentor.ec @@ -535,7 +535,7 @@ public: const char * label; bool showPrivate; - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { return name; } @@ -1126,7 +1126,7 @@ class APIPageNameSpace : APIPage const char * name = RSearchString(function.name, "::", strlen(function.name), true, false); if(name) name += 2; else name = function.name; - if(whiteList && !whiteList->match(name)) continue; + if(whiteList && !whiteList->match(function.name)) continue; if(first) { f.Printf($"

Functions

%s\n", oneBR); @@ -1164,9 +1164,7 @@ class APIPageNameSpace : APIPage { DefinedExpression def = link.data; char * desc; - const char * name = ( name = RSearchString(def.name, "::", strlen(def.name), false, false), name ? name + 2 : def.name); - - if(whiteList && !whiteList->match(name)) continue; + if(whiteList && !whiteList->match(def.name)) continue; desc = ReadDoc(module, nameSpaceDoc, nameSpace, definition, def); if(first) @@ -2241,7 +2239,7 @@ static void AddNameSpace(DataRow parentRow, Module module, NameSpace mainNameSpa if(!module || fn.module == module || (!fn.module.name && !strcmp(module.name, "ecere"))) { const char * name = ( name = RSearchString(fn.name, "::", strlen(fn.name), false, false), name ? name + 2 : fn.name); - if(!whiteList || whiteList->match(name)) + if(!whiteList || whiteList->match(fn.name)) { DataRow fnRow; if(!functionsRow) { functionsRow = row.AddRow(); functionsRow.SetData(null, APIPage { $"Functions", page = page }); functionsRow.collapsed = true; functionsRow.icon = mainForm.icons[typeMethod]; functionsRow.tag = 2; }; @@ -2268,7 +2266,7 @@ static void AddNameSpace(DataRow parentRow, Module module, NameSpace mainNameSpa //if(def.module == module) { char * name = ( name = RSearchString(def.name, "::", strlen(def.name), false, false), name ? name + 2 : def.name); - if(!whiteList || whiteList->match(name)) + if(!whiteList || whiteList->match(def.name)) { DataRow defRow; if(!definesRow) { definesRow = row.AddRow(); definesRow.SetData(null, APIPage { $"Definitions", page = page }); definesRow.collapsed = true; definesRow.icon = mainForm.icons[typeData]; definesRow.tag = 3; }; diff --git a/ear/Makefile b/ear/Makefile index d02394ead1..36e75ad795 100644 --- a/ear/Makefile +++ b/ear/Makefile @@ -1,13 +1,16 @@ ifneq ($(V),1) .SILENT: endif -.PHONY: all nores cleantarget clean realclean distclean + +.PHONY: all nores cleantarget clean realclean wipeclean distclean _CF_DIR = ../ include $(_CF_DIR)crossplatform.mk include $(_CF_DIR)default.cf +.NOTPARALLEL: $(NOT_PARALLEL_TARGETS) + all: @$(call echo,Building self-extract tool...) +cd extract && $(_MAKE) @@ -30,9 +33,13 @@ realclean: +cd extract && $(_MAKE) realclean +cd cmd && $(_MAKE) realclean +wipeclean: + +cd extract && $(_MAKE) wipeclean + +cd cmd && $(_MAKE) wipeclean + distclean: - $(MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs + $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/ear/cmd/Makefile b/ear/cmd/Makefile index 4e630016c2..e5ca426169 100644 --- a/ear/cmd/Makefile +++ b/ear/cmd/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -42,7 +46,7 @@ OBJ = obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ RES = -TARGET = obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ear$(E) +TARGET = obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ear$(B32_SFX)$(E) _ECSOURCES = \ ear.ec @@ -80,8 +84,13 @@ PRJ_CFLAGS += \ $(if $(DEBUG), -g, -O2 -ffast-math) $(FPIC) -w ECFLAGS += -module $(MODULE) -ECFLAGS += \ - -nolinenumbers +ifndef DEBUG +ECFLAGS += -nolinenumbers +endif + +ifdef DISABLE_MEMMGR +ECFLAGS += -DDISABLE_MEMMGR +endif CECFLAGS += -cpp $(_CPP) @@ -96,7 +105,7 @@ endif all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS) $(ECS) -console $(ARCH_FLAGS) $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols $(OBJ) -o $(OBJ)$(MODULE).main.ec @@ -146,7 +155,7 @@ $(OBJ)ear.o: $(OBJ)ear.c $(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.c -o $(OBJ)$(MODULE).main$(O) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -171,9 +180,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/ear/extract/Makefile b/ear/extract/Makefile index 521bf8e7c8..472d6d3ee2 100644 --- a/ear/extract/Makefile +++ b/ear/extract/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -117,8 +121,13 @@ PRJ_CFLAGS += \ -DECERE_STATIC ECFLAGS += -module $(MODULE) -ECFLAGS += \ - -nolinenumbers +ifndef DEBUG +ECFLAGS += -nolinenumbers +endif + +ifdef DISABLE_MEMMGR +ECFLAGS += -DDISABLE_MEMMGR +endif # PLATFORM-SPECIFIC OPTIONS @@ -185,7 +194,7 @@ endif all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS) $(ECS) $(ARCH_FLAGS) $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX) -o $(OBJ)$(MODULE).main.ec @@ -204,10 +213,14 @@ ifndef NOSTRIP endif ifndef WINDOWS_TARGET ifdef EXECUTABLE_TARGET + ifndef DISABLE_EXEC_COMPRESSION @-$(call sys_path,$(UPX) $(UPXFLAGS) $(TARGET)) || $(call echo,upx not installed; not compressing.) + endif endif else + ifndef DISABLE_EXEC_COMPRESSION @-$(call sys_path,$(UPX) $(UPXFLAGS) $(TARGET)) || $(call echo,upx not installed; not compressing.) + endif endif $(EAR) aw$(EARFLAGS) $(TARGET) ../../ecere/res/vanilla/ecere/actions/folderNew.png ../../ecere/res/vanilla/ecere/actions/goUp.png "ecere/actions" $(EAR) aw$(EARFLAGS) $(TARGET) ../../ecere/res/vanilla/ecere/devices/computer.png ../../ecere/res/vanilla/ecere/devices/driveHardDisk.png ../../ecere/res/vanilla/ecere/devices/driveRemovableMedia.png ../../ecere/res/vanilla/ecere/devices/mediaFloppy.png ../../ecere/res/vanilla/ecere/devices/mediaOptical.png "ecere/devices" @@ -242,7 +255,7 @@ $(OBJ)extract.o: $(OBJ)extract.c $(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.c -o $(OBJ)$(MODULE).main$(O) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -264,12 +277,15 @@ clean: cleantarget $(call rm,$(IMPORTS)) $(call rm,$(SYMBOLS)) +wipeclean: + $(call rmr,obj/) + realclean: cleantarget $(call rmr,$(OBJ)) distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/ecere/Makefile b/ecere/Makefile index 951aa5fffd..1f5a8bf4d7 100644 --- a/ecere/Makefile +++ b/ecere/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleaneceretarget cleantarget clean realclean distclean nores +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleaneceretarget cleantarget clean realclean wipeclean distclean nores # CORE VARIABLES @@ -54,7 +58,7 @@ else SONAME = endif -_ECSOURCES = $(_ECSOURCES1) $(_ECSOURCES2) $(_ECSOURCES3) +_ECSOURCES = $(_ECSOURCES1) $(_ECSOURCES2) $(_ECSOURCES3) $(_ECSOURCES4) _ECSOURCES1 = \ src/sys/Archive.ec \ src/sys/BufferedFile.ec \ @@ -94,11 +98,12 @@ _ECSOURCES1 = \ src/gfx/bitmaps/PCXFormat.ec \ src/gfx/bitmaps/PNGFormat.ec \ src/gfx/bitmaps/RGBFormat.ec \ - src/gfx/drivers/gl3/glab.ec \ - src/gfx/drivers/gl3/immediate.ec \ - src/gfx/drivers/gl3/matrixStack.ec \ - src/gfx/drivers/gl3/shaders.ec \ - src/gfx/drivers/gl3/defaultShader.ec \ + src/gfx/bitmaps/ETC2Format.ec \ + $(if $(DISABLE_GL),,src/gfx/drivers/gl3/glab.ec) \ + $(if $(DISABLE_GL),,src/gfx/drivers/gl3/immediate.ec) \ + $(if $(DISABLE_GL),,src/gfx/drivers/gl3/matrixStack.ec) \ + $(if $(DISABLE_GL),,src/gfx/drivers/gl3/shaders.ec) \ + $(if $(DISABLE_GL),,src/gfx/drivers/gl3/defaultShader.ec) \ $(if $(WINDOWS_TARGET),src/gfx/drivers/Direct3D8DisplayDriver.ec,) \ $(if $(WINDOWS_TARGET),src/gfx/drivers/Direct3D9DisplayDriver.ec,) \ $(if $(WINDOWS_TARGET),src/gfx/drivers/DirectDrawDisplayDriver.ec,) \ @@ -108,12 +113,18 @@ _ECSOURCES1 = \ src/gfx/drivers/LFBDisplayDriver.ec \ $(if $(or $(LINUX_TARGET),$(OSX_TARGET)),src/gfx/drivers/NCursesDisplayDriver.ec,) \ $(if $(DISABLE_GL),,src/gfx/drivers/OpenGLDisplayDriver.ec) \ + $(if $(DISABLE_GL),,src/gfx/drivers/gl3/GLMultiDraw.ec) \ $(if $(WINDOWS_TARGET),src/gfx/drivers/Win32BitmapPrinterDisplayDriver.ec,) _ECSOURCES2 = \ $(if ,src/gfx/drivers/CocoaOpenGLDisplayDriver.ec,) \ $(if $(WINDOWS_TARGET),src/gfx/drivers/Win32ConsoleDisplayDriver.ec,) \ $(if $(WINDOWS_TARGET),src/gfx/drivers/Win32PrinterDisplayDriver.ec,) \ $(if $(or $(LINUX_TARGET),$(OSX_TARGET)),src/gfx/drivers/XDisplayDriver.ec,) \ + $(if $(DISABLE_GL),,src/gfx/newFonts/atlasBuilder.ec) \ + $(if $(DISABLE_GL),,src/gfx/newFonts/drawManager.ec) \ + $(if $(DISABLE_GL),,src/gfx/newFonts/fmFontManager.ec) \ + $(if $(DISABLE_GL),,src/gfx/newFonts/fontRenderer.ec) \ + $(if $(DISABLE_GL),,src/gfx/newFonts/textureManager.ec) \ src/gfx/Bitmap.ec \ src/gfx/BitmapResource.ec \ src/gfx/Color.ec \ @@ -186,6 +197,9 @@ _ECSOURCES3 = \ src/com/containers/LinkList.ec \ src/com/containers/List.ec \ src/com/containers/Map.ec \ + src/com/containers/HashMap.ec +_ECSOURCES4 = \ + src/com/containers/HashTable.ec \ src/com/BinaryTree.ec \ src/com/BTNode.ec \ src/com/dataTypes.ec \ @@ -197,31 +211,37 @@ ECSOURCES = $(call shwspace,$(_ECSOURCES)) ECSOURCES1 = $(call shwspace,$(_ECSOURCES1)) ECSOURCES2 = $(call shwspace,$(_ECSOURCES2)) ECSOURCES3 = $(call shwspace,$(_ECSOURCES3)) +ECSOURCES4 = $(call shwspace,$(_ECSOURCES4)) -COBJECTS = $(COBJECTS1) $(COBJECTS2) $(COBJECTS3) +COBJECTS = $(COBJECTS1) $(COBJECTS2) $(COBJECTS3) $(COBJECTS4) COBJECTS1 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(C),$(notdir $(_ECSOURCES1))))) COBJECTS2 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(C),$(notdir $(_ECSOURCES2))))) COBJECTS3 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(C),$(notdir $(_ECSOURCES3))))) +COBJECTS4 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(C),$(notdir $(_ECSOURCES4))))) -SYMBOLS = $(SYMBOLS1) $(SYMBOLS2) $(SYMBOLS3) +SYMBOLS = $(SYMBOLS1) $(SYMBOLS2) $(SYMBOLS3) $(SYMBOLS4) SYMBOLS1 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(S),$(notdir $(_ECSOURCES1))))) SYMBOLS2 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(S),$(notdir $(_ECSOURCES2))))) SYMBOLS3 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(S),$(notdir $(_ECSOURCES3))))) +SYMBOLS4 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(S),$(notdir $(_ECSOURCES4))))) -IMPORTS = $(IMPORTS1) $(IMPORTS2) $(IMPORTS3) +IMPORTS = $(IMPORTS1) $(IMPORTS2) $(IMPORTS3) $(IMPORTS4) IMPORTS1 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(I),$(notdir $(_ECSOURCES1))))) IMPORTS2 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(I),$(notdir $(_ECSOURCES2))))) IMPORTS3 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(I),$(notdir $(_ECSOURCES3))))) +IMPORTS4 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(I),$(notdir $(_ECSOURCES4))))) -ECOBJECTS = $(ECOBJECTS1) $(ECOBJECTS2) $(ECOBJECTS3) +ECOBJECTS = $(ECOBJECTS1) $(ECOBJECTS2) $(ECOBJECTS3) $(ECOBJECTS4) ECOBJECTS1 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(O),$(notdir $(_ECSOURCES1))))) ECOBJECTS2 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(O),$(notdir $(_ECSOURCES2))))) ECOBJECTS3 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(O),$(notdir $(_ECSOURCES3))))) +ECOBJECTS4 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(O),$(notdir $(_ECSOURCES4))))) -BOWLS = $(BOWLS1) $(BOWLS2) $(BOWLS3) +BOWLS = $(BOWLS1) $(BOWLS2) $(BOWLS3) $(BOWLS4) BOWLS1 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(B),$(notdir $(_ECSOURCES1))))) BOWLS2 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(B),$(notdir $(_ECSOURCES2))))) BOWLS3 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(B),$(notdir $(_ECSOURCES3))))) +BOWLS4 = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(B),$(notdir $(_ECSOURCES4))))) _OBJECTS = \ $(OBJ)File.c.o \ @@ -246,7 +266,11 @@ _OBJECTS = \ $(OBJ)harfbuzz-tibetan.o \ $(OBJ)harfbuzz-impl.o \ $(OBJ)harfbuzz-thai.o \ - $(OBJ)gl_compat_4_4.o \ + $(if $(DISABLE_GL),,$(OBJ)gl_compat_4_4.o) \ + $(if $(DISABLE_GL),,$(OBJ)cc$(O)) \ + $(if $(DISABLE_GL),,$(OBJ)ccstr$(O)) \ + $(if $(DISABLE_GL),,$(OBJ)mm$(O)) \ + $(OBJ)mmhash$(O) \ $(if ,$(OBJ)CocoaEcereBridge.o,) \ $(if ,$(OBJ)EcereView.o,) \ $(OBJ)instance.c.o @@ -276,7 +300,11 @@ SOURCES = $(ECSOURCES) \ src/gfx/drivers/harfbuzz/harfbuzz-tibetan.c \ src/gfx/drivers/harfbuzz/harfbuzz-impl.c \ src/gfx/drivers/harfbuzz/harfbuzz-thai.c \ - src/gfx/drivers/gl3/gl_compat_4_4.c \ + $(if $(DISABLE_GL),,src/gfx/drivers/gl3/gl_compat_4_4.c) \ + $(if $(DISABLE_GL),,src/gfx/newFonts/cc/cc.c) \ + $(if $(DISABLE_GL),,src/gfx/newFonts/cc/ccstr.c) \ + $(if $(DISABLE_GL),,src/gfx/newFonts/cc/mm.c) \ + src/gfx/newFonts/cc/mmhash.c \ $(if ,src/gui/drivers/cocoa/CocoaEcereBridge.m,) \ $(if ,src/gui/drivers/cocoa/EcereView.m,) \ src/com/instance.c @@ -404,11 +432,14 @@ LIBS += \ $(call _L,jpeg) \ $(call _L,png) \ $(call _L,z) \ - $(call _L,freetype) + $(call _L,freetype) \ + $(call _L,curl) endif # Careful to keep ../deps/glext as -isystem instead of generated -I PRJ_CFLAGS += \ + $(if $(DISABLE_GL), \ + -DECERE_NOGL,) \ $(if $(WINDOWS_TARGET), \ -isystem ../deps/glext \ -I../deps/jpeg-9a \ @@ -416,6 +447,7 @@ PRJ_CFLAGS += \ -I../deps/libungif-4.1.1/lib \ -I../deps/zlib-1.2.8 \ -I../deps/freetype-2.3.12/include \ + -I../deps/curl-7.51.0/include \ $(if $(ENABLE_SSL),-I$(OPENSSL_INCLUDE_DIR),)) \ $(if $(LINUX_TARGET), \ -I/usr/include/freetype2,) \ @@ -431,6 +463,7 @@ PRJ_CFLAGS += \ -I../deps/libungif-4.1.1/lib,) \ $(if $(DEBUG), -g, -O2 -ffast-math) $(FPIC) -w \ -DBUILDING_ECERE_COM \ + -DCURL_DISABLE_TYPECHECK \ -Isrc/gfx/drivers/gl3/ \ -Isrc/gfx/drivers/harfbuzz \ -I/usr/X11R6/include \ @@ -445,9 +478,22 @@ CUSTOM2_PRJ_CFLAGS = \ -DECERE_COM_MODULE \ $(PRJ_CFLAGS) -ECFLAGS += -module $(MODULE) -ECFLAGS += \ - -nolinenumbers -defaultns ecere +CUSTOM3_PRJ_CFLAGS = \ + -Isrc/gfx/newFonts/cc \ + $(PRJ_CFLAGS) + +CUSTOM5_PRJ_CFLAGS = \ + -Isrc/gfx/newFonts/cc \ + $(CUSTOM2_PRJ_CFLAGS) + +ECFLAGS += -module $(MODULE) -defaultns ecere +ifndef DEBUG +ECFLAGS += -nolinenumbers +endif + +ifdef DISABLE_MEMMGR +ECFLAGS += -DDISABLE_MEMMGR +endif # PLATFORM-SPECIFIC OPTIONS @@ -460,6 +506,7 @@ OFLAGS += \ -L../deps/libpng-1.6.12/obj/release.$(PLATFORM)$(COMPILER_SUFFIX) \ -L../deps/libungif-4.1.1/obj/release.$(PLATFORM)$(COMPILER_SUFFIX) \ -L../deps/freetype-2.3.12/obj/release.$(PLATFORM)$(COMPILER_SUFFIX) \ + -L../deps/curl-7.51.0/obj/release.$(PLATFORM)$(COMPILER_SUFFIX) \ $(if $(ENABLE_SSL),-L$(OPENSSL_LIB_DIR) -L$(OPENSSL_BIN_DIR)) LIBS += \ $(call _L,dxguid) \ @@ -477,14 +524,23 @@ LIBS += \ $(call _L,winspool) \ $(call _L,imm32) \ $(call _L,ungif) \ - $(if $(ENABLE_SSL),$(call _L,eay32)) \ - $(if $(ENABLE_SSL),$(call _L,ssleay32)) + $(if $(and $(ENABLE_SSL),$(wildcard $(OPENSSL_BIN_DIR)/libcrypto-1_1-x64.dll)),$(call _L,crypto-1_1-x64)) \ + $(if $(and $(ENABLE_SSL),$(wildcard $(OPENSSL_BIN_DIR)/libssl-1_1-x64.dll)),$(call _L,ssl-1_1-x64)) \ + $(if $(and $(ENABLE_SSL),$(wildcard $(OPENSSL_BIN_DIR)/libcrypto-1_1.dll)),$(call _L,crypto-1_1)) \ + $(if $(and $(ENABLE_SSL),$(wildcard $(OPENSSL_BIN_DIR)/libssl-1_1.dll)),$(call _L,ssl-1_1)) \ + $(if $(and $(ENABLE_SSL),$(wildcard $(OPENSSL_BIN_DIR)/libeay32.dll)),$(call _L,eay32)) \ + $(if $(and $(ENABLE_SSL),$(wildcard $(OPENSSL_BIN_DIR)/ssleay32.dll)),$(call _L,ssleay32)) endif else ifdef LINUX_TARGET ifndef STATIC_LIBRARY_TARGET +ifneq ($(ARCH),x32) +# $(warning ARCH: $(ARCH) -- adding -Wl,--wrap=fcntl64 to OFLAGS) +OFLAGS += \ + -Wl,--wrap=fcntl64 +endif OFLAGS += \ -L/usr/X11R6/lib LIBS += \ @@ -499,7 +555,8 @@ LIBS += \ $(call _L,Xrender) \ $(if $(DISABLE_GL),,$(call _L,GL)) \ $(if $(ENABLE_SSL),$(call _L,ssl)) \ - $(if $(ENABLE_SSL),$(call _L,crypto)) + $(if $(ENABLE_SSL),$(call _L,crypto)) \ + $(call _L,curl) endif else @@ -537,7 +594,7 @@ CECFLAGS += -cpp $(_CPP) all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS) @$(call rm,$(OBJ)symbols.lst) @@ -545,9 +602,11 @@ $(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS) $(call addtolistfile,$(SYMBOLS1),$(OBJ)symbols.lst) $(call addtolistfile,$(SYMBOLS2),$(OBJ)symbols.lst) $(call addtolistfile,$(SYMBOLS3),$(OBJ)symbols.lst) + $(call addtolistfile,$(SYMBOLS4),$(OBJ)symbols.lst) $(call addtolistfile,$(IMPORTS1),$(OBJ)symbols.lst) $(call addtolistfile,$(IMPORTS2),$(OBJ)symbols.lst) $(call addtolistfile,$(IMPORTS3),$(OBJ)symbols.lst) + $(call addtolistfile,$(IMPORTS4),$(OBJ)symbols.lst) $(ECS) $(ARCH_FLAGS) $(ECSLIBOPT) @$(OBJ)symbols.lst -symbols obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX) -o $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main.c: $(OBJ)$(MODULE).main.ec @@ -585,6 +644,7 @@ $(TARGET): $(SOURCES) $(RESOURCES) $(SYMBOLS) $(OBJECTS) | objdir $(call addtolistfile,$(ECOBJECTS1),$(OBJ)objects.lst) $(call addtolistfile,$(ECOBJECTS2),$(OBJ)objects.lst) $(call addtolistfile,$(ECOBJECTS3),$(OBJ)objects.lst) + $(call addtolistfile,$(ECOBJECTS4),$(OBJ)objects.lst) ifndef STATIC_LIBRARY_TARGET $(CC) $(OFLAGS) @$(OBJ)objects.lst $(LIBS) -o $(TARGET) $(SONAME) $(INSTALLNAME) ifndef NOSTRIP @@ -745,9 +805,15 @@ $(OBJ)PNGFormat.sym: src/gfx/bitmaps/PNGFormat.ec $(OBJ)RGBFormat.sym: src/gfx/bitmaps/RGBFormat.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/bitmaps/RGBFormat.ec -o $(OBJ)RGBFormat.sym +$(OBJ)ETC2Format.sym: src/gfx/bitmaps/ETC2Format.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/bitmaps/ETC2Format.ec -o $(OBJ)ETC2Format.sym + $(OBJ)glab.sym: src/gfx/drivers/gl3/glab.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/drivers/gl3/glab.ec -o $(OBJ)glab.sym +$(OBJ)GLMultiDraw.sym: src/gfx/drivers/gl3/GLMultiDraw.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/drivers/gl3/GLMultiDraw.ec -o $(OBJ)GLMultiDraw.sym + $(OBJ)immediate.sym: src/gfx/drivers/gl3/immediate.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/drivers/gl3/immediate.ec -o $(OBJ)immediate.sym @@ -822,6 +888,21 @@ $(OBJ)XDisplayDriver.sym: src/gfx/drivers/XDisplayDriver.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/drivers/XDisplayDriver.ec -o $(OBJ)XDisplayDriver.sym endif +$(OBJ)atlasBuilder.sym: src/gfx/newFonts/atlasBuilder.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM3_PRJ_CFLAGS) -c $(call quote_path,src/gfx/newFonts/atlasBuilder.ec) -o $(call quote_path,$@) + +$(OBJ)drawManager.sym: src/gfx/newFonts/drawManager.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM3_PRJ_CFLAGS) -c $(call quote_path,src/gfx/newFonts/drawManager.ec) -o $(call quote_path,$@) + +$(OBJ)fmFontManager.sym: src/gfx/newFonts/fmFontManager.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM3_PRJ_CFLAGS) -c $(call quote_path,src/gfx/newFonts/fmFontManager.ec) -o $(call quote_path,$@) + +$(OBJ)fontRenderer.sym: src/gfx/newFonts/fontRenderer.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM3_PRJ_CFLAGS) -c $(call quote_path,src/gfx/newFonts/fontRenderer.ec) -o $(call quote_path,$@) + +$(OBJ)textureManager.sym: src/gfx/newFonts/textureManager.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM3_PRJ_CFLAGS) -c $(call quote_path,src/gfx/newFonts/textureManager.ec) -o $(call quote_path,$@) + $(OBJ)Bitmap.sym: src/gfx/Bitmap.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/Bitmap.ec -o $(OBJ)Bitmap.sym @@ -1047,6 +1128,12 @@ $(OBJ)List.sym: src/com/containers/List.ec $(OBJ)Map.sym: src/com/containers/Map.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM2_PRJ_CFLAGS) -c src/com/containers/Map.ec -o $(OBJ)Map.sym +$(OBJ)HashMap.sym: src/com/containers/HashMap.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM5_PRJ_CFLAGS) -c src/com/containers/HashMap.ec -o $(OBJ)HashMap.sym + +$(OBJ)HashTable.sym: src/com/containers/HashTable.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM5_PRJ_CFLAGS) -c src/com/containers/HashTable.ec -o $(OBJ)HashTable.sym + $(OBJ)BinaryTree.sym: src/com/BinaryTree.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM2_PRJ_CFLAGS) -c src/com/BinaryTree.ec -o $(OBJ)BinaryTree.sym @@ -1181,9 +1268,15 @@ $(OBJ)PNGFormat.c: src/gfx/bitmaps/PNGFormat.ec $(OBJ)PNGFormat.sym | $(SYMBOLS) $(OBJ)RGBFormat.c: src/gfx/bitmaps/RGBFormat.ec $(OBJ)RGBFormat.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/bitmaps/RGBFormat.ec -o $(OBJ)RGBFormat.c -symbols $(OBJ) +$(OBJ)ETC2Format.c: src/gfx/bitmaps/ETC2Format.ec $(OBJ)ETC2Format.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/bitmaps/ETC2Format.ec -o $(OBJ)ETC2Format.c -symbols $(OBJ) + $(OBJ)glab.c: src/gfx/drivers/gl3/glab.ec $(OBJ)glab.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/drivers/gl3/glab.ec -o $(OBJ)glab.c -symbols $(OBJ) +$(OBJ)GLMultiDraw.c: src/gfx/drivers/gl3/GLMultiDraw.ec $(OBJ)GLMultiDraw.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/drivers/gl3/GLMultiDraw.ec -o $(OBJ)GLMultiDraw.c -symbols $(OBJ) + $(OBJ)immediate.c: src/gfx/drivers/gl3/immediate.ec $(OBJ)immediate.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/drivers/gl3/immediate.ec -o $(OBJ)immediate.c -symbols $(OBJ) @@ -1258,6 +1351,21 @@ $(OBJ)XDisplayDriver.c: src/gfx/drivers/XDisplayDriver.ec $(OBJ)XDisplayDriver.s $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/drivers/XDisplayDriver.ec -o $(OBJ)XDisplayDriver.c -symbols $(OBJ) endif +$(OBJ)atlasBuilder.c: src/gfx/newFonts/atlasBuilder.ec $(OBJ)atlasBuilder.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM3_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,src/gfx/newFonts/atlasBuilder.ec) -o $(call quote_path,$@) -symbols $(OBJ) + +$(OBJ)drawManager.c: src/gfx/newFonts/drawManager.ec $(OBJ)drawManager.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM3_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,src/gfx/newFonts/drawManager.ec) -o $(call quote_path,$@) -symbols $(OBJ) + +$(OBJ)fmFontManager.c: src/gfx/newFonts/fmFontManager.ec $(OBJ)fmFontManager.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM3_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,src/gfx/newFonts/fmFontManager.ec) -o $(call quote_path,$@) -symbols $(OBJ) + +$(OBJ)fontRenderer.c: src/gfx/newFonts/fontRenderer.ec $(OBJ)fontRenderer.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM3_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,src/gfx/newFonts/fontRenderer.ec) -o $(call quote_path,$@) -symbols $(OBJ) + +$(OBJ)textureManager.c: src/gfx/newFonts/textureManager.ec $(OBJ)textureManager.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM3_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,src/gfx/newFonts/textureManager.ec) -o $(call quote_path,$@) -symbols $(OBJ) + $(OBJ)Bitmap.c: src/gfx/Bitmap.ec $(OBJ)Bitmap.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gfx/Bitmap.ec -o $(OBJ)Bitmap.c -symbols $(OBJ) @@ -1483,6 +1591,12 @@ $(OBJ)List.c: src/com/containers/List.ec $(OBJ)List.sym | $(SYMBOLS) $(OBJ)Map.c: src/com/containers/Map.ec $(OBJ)Map.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM2_PRJ_CFLAGS) -c src/com/containers/Map.ec -o $(OBJ)Map.c -symbols $(OBJ) +$(OBJ)HashMap.c: src/com/containers/HashMap.ec $(OBJ)HashMap.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM5_PRJ_CFLAGS) $(FVISIBILITY) -c src/com/containers/HashMap.ec -o $(OBJ)HashMap.c -symbols $(OBJ) + +$(OBJ)HashTable.c: src/com/containers/HashTable.ec $(OBJ)HashTable.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM5_PRJ_CFLAGS) $(FVISIBILITY) -c src/com/containers/HashTable.ec -o $(OBJ)HashTable.c -symbols $(OBJ) + $(OBJ)BinaryTree.c: src/com/BinaryTree.ec $(OBJ)BinaryTree.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM2_PRJ_CFLAGS) -c src/com/BinaryTree.ec -o $(OBJ)BinaryTree.c -symbols $(OBJ) @@ -1626,6 +1740,9 @@ $(OBJ)PNGFormat.o: $(OBJ)PNGFormat.c $(OBJ)RGBFormat.o: $(OBJ)RGBFormat.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(OBJ)RGBFormat.c -o $(OBJ)RGBFormat.o +$(OBJ)ETC2Format.o: $(OBJ)ETC2Format.c + $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(OBJ)ETC2Format.c -o $(OBJ)ETC2Format.o + $(OBJ)harfbuzz-freetype.o: src/gfx/drivers/harfbuzz/unicode/harfbuzz-freetype.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c src/gfx/drivers/harfbuzz/unicode/harfbuzz-freetype.c -o $(OBJ)harfbuzz-freetype.o @@ -1745,6 +1862,33 @@ $(OBJ)XDisplayDriver.o: $(OBJ)XDisplayDriver.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(OBJ)XDisplayDriver.c -o $(OBJ)XDisplayDriver.o endif +$(OBJ)cc$(O): src/gfx/newFonts/cc/cc.c + $(CC) $(CFLAGS) $(CUSTOM3_PRJ_CFLAGS) -c $(call quote_path,src/gfx/newFonts/cc/cc.c) -o $(call quote_path,$@) + +$(OBJ)ccstr$(O): src/gfx/newFonts/cc/ccstr.c + $(CC) $(CFLAGS) $(CUSTOM3_PRJ_CFLAGS) -c $(call quote_path,src/gfx/newFonts/cc/ccstr.c) -o $(call quote_path,$@) + +$(OBJ)mmhash$(O): src/gfx/newFonts/cc/mmhash.c + $(CC) $(CFLAGS) $(CUSTOM3_PRJ_CFLAGS) -c $(call quote_path,src/gfx/newFonts/cc/mmhash.c) -o $(call quote_path,$@) + +$(OBJ)mm$(O): src/gfx/newFonts/cc/mm.c + $(CC) $(CFLAGS) $(CUSTOM3_PRJ_CFLAGS) -c $(call quote_path,src/gfx/newFonts/cc/mm.c) -o $(call quote_path,$@) + +$(OBJ)atlasBuilder$(O): $(OBJ)atlasBuilder.c + $(CC) $(CFLAGS) $(CUSTOM3_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,$(OBJ)atlasBuilder.c) -o $(call quote_path,$@) + +$(OBJ)drawManager$(O): $(OBJ)drawManager.c + $(CC) $(CFLAGS) $(CUSTOM3_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,$(OBJ)drawManager.c) -o $(call quote_path,$@) + +$(OBJ)fmFontManager$(O): $(OBJ)fmFontManager.c + $(CC) $(CFLAGS) $(CUSTOM3_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,$(OBJ)fmFontManager.c) -o $(call quote_path,$@) + +$(OBJ)fontRenderer$(O): $(OBJ)fontRenderer.c + $(CC) $(CFLAGS) $(CUSTOM3_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,$(OBJ)fontRenderer.c) -o $(call quote_path,$@) + +$(OBJ)textureManager$(O): $(OBJ)textureManager.c + $(CC) $(CFLAGS) $(CUSTOM3_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,$(OBJ)textureManager.c) -o $(call quote_path,$@) + $(OBJ)Bitmap.o: $(OBJ)Bitmap.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(OBJ)Bitmap.c -o $(OBJ)Bitmap.o @@ -1938,6 +2082,9 @@ $(OBJ)gl_compat_4_4.o: src/gfx/drivers/gl3/gl_compat_4_4.c $(OBJ)glab.o: $(OBJ)glab.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(OBJ)glab.c -o $(OBJ)glab.o +$(OBJ)GLMultiDraw.o: $(OBJ)GLMultiDraw.c + $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(OBJ)GLMultiDraw.c -o $(OBJ)GLMultiDraw.o + $(OBJ)immediate.o: $(OBJ)immediate.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(OBJ)immediate.c -o $(OBJ)immediate.o @@ -1998,6 +2145,12 @@ $(OBJ)List.o: $(OBJ)List.c $(OBJ)Map.o: $(OBJ)Map.c $(CC) $(CFLAGS) $(CUSTOM2_PRJ_CFLAGS) -c $(OBJ)Map.c -o $(OBJ)Map.o +$(OBJ)HashMap$(O): $(OBJ)HashMap.c + $(CC) $(CFLAGS) $(CUSTOM5_PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)HashMap.c -o $(OBJ)HashMap.o + +$(OBJ)HashTable$(O): $(OBJ)HashTable.c + $(CC) $(CFLAGS) $(CUSTOM5_PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)HashTable.c -o $(OBJ)HashTable.o + $(OBJ)BinaryTree.o: $(OBJ)BinaryTree.c $(CC) $(CFLAGS) $(CUSTOM2_PRJ_CFLAGS) -c $(OBJ)BinaryTree.c -o $(OBJ)BinaryTree.o @@ -2022,10 +2175,10 @@ $(OBJ)instance.c.o: src/com/instance.c $(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(OBJ)$(MODULE).main.c -o $(OBJ)$(MODULE).main$(O) -cleaneceretarget: objdir +cleaneceretarget: $(call rm,$(TARGET)) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -2046,18 +2199,23 @@ clean: cleantarget $(call rm,$(ECOBJECTS1)) $(call rm,$(ECOBJECTS2)) $(call rm,$(ECOBJECTS3)) + $(call rm,$(ECOBJECTS4)) $(call rm,$(COBJECTS1)) $(call rm,$(COBJECTS2)) $(call rm,$(COBJECTS3)) + $(call rm,$(COBJECTS4)) $(call rm,$(BOWLS1)) $(call rm,$(BOWLS2)) $(call rm,$(BOWLS3)) + $(call rm,$(BOWLS4)) $(call rm,$(IMPORTS1)) $(call rm,$(IMPORTS2)) $(call rm,$(IMPORTS3)) + $(call rm,$(IMPORTS4)) $(call rm,$(SYMBOLS1)) $(call rm,$(SYMBOLS2)) $(call rm,$(SYMBOLS3)) + $(call rm,$(SYMBOLS4)) $(_MAKE) -f Makefile.ecereCOM clean $(_MAKE) -f Makefile.vanilla clean @@ -2066,12 +2224,15 @@ realclean: cleantarget $(_MAKE) -f Makefile.ecereCOM realclean $(_MAKE) -f Makefile.vanilla realclean +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; #troubleshoot: # @$(call echo,OBJECTS = $(OBJECTS)) diff --git a/ecere/Makefile.bootstrap b/ecere/Makefile.bootstrap index c51bbe5570..84e9790b44 100644 --- a/ecere/Makefile.bootstrap +++ b/ecere/Makefile.bootstrap @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -110,9 +114,14 @@ CUSTOM2_PRJ_CFLAGS = \ -D_FILE_OFFSET_BITS=64,) \ $(PRJ_CFLAGS) -ECFLAGS += -module $(MODULE) -ECFLAGS += \ - -nolinenumbers -defaultns ecere +ECFLAGS += -module $(MODULE) -defaultns ecere +ifndef DEBUG +ECFLAGS += -nolinenumbers +endif + +ifdef DISABLE_MEMMGR +ECFLAGS += -DDISABLE_MEMMGR +endif CECFLAGS += -cpp $(_CPP) @@ -121,7 +130,7 @@ CECFLAGS += -cpp $(_CPP) all: objdir $(COBJECTS) $(OBJ)$(MODULE).main.c objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(if $(ECERE_SDK_SRC),$(if $(wildcard $(call escspace,$(ECERE_SDK_SRC)/crossplatform.mk)),,@$(call echo,Ecere SDK Source Warning: The value of ECERE_SDK_SRC is pointing to an incorrect ($(ECERE_SDK_SRC)) location.)),) $(if $(ECERE_SDK_SRC),,$(if $(ECP_DEBUG)$(ECC_DEBUG)$(ECS_DEBUG),@$(call echo,ECC Debug Warning: Please define ECERE_SDK_SRC before using ECP_DEBUG, ECC_DEBUG or ECS_DEBUG),)) @@ -268,7 +277,7 @@ $(OBJ)String.c: src/com/String.ec $(OBJ)String.sym | $(SYMBOLS) $(OBJ)OldList.c: src/com/OldList.ec $(OBJ)OldList.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM1_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,src/com/OldList.ec) -o $(call quote_path,$@) -symbols $(OBJ) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -283,9 +292,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile.bootstrap: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)$(TARGET_PLATFORM)-$(COMPILER).cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/ecere/Makefile.ecereCOM b/ecere/Makefile.ecereCOM index 9d76a8d7fc..c111fd633c 100644 --- a/ecere/Makefile.ecereCOM +++ b/ecere/Makefile.ecereCOM @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -65,6 +69,8 @@ _ECSOURCES = \ src/com/containers/LinkList.ec \ src/com/containers/List.ec \ src/com/containers/Map.ec \ + src/com/containers/HashMap.ec \ + src/com/containers/HashTable.ec \ src/com/BinaryTree.ec \ src/com/BTNode.ec \ src/com/dataTypes.ec \ @@ -85,6 +91,7 @@ ECOBJECTS = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(O),$(notdir $( BOWLS = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(B),$(notdir $(_ECSOURCES))))) _OBJECTS = \ + $(OBJ)mmhash$(O) \ $(OBJ)instance.c.o OBJECTS = $(_OBJECTS) $(ECOBJECTS) $(OBJ)$(MODULE).main$(O) @@ -106,9 +113,18 @@ PRJ_CFLAGS += \ -DECERE_COM_MODULE \ -DECERE_NOFILE -ECFLAGS += -module $(MODULE) -ECFLAGS += \ - -nolinenumbers -defaultns ecere +CUSTOM1_PRJ_CFLAGS = \ + -Isrc/gfx/newFonts/cc \ + $(PRJ_CFLAGS) + +ECFLAGS += -module $(MODULE) -defaultns ecere +ifndef DEBUG +ECFLAGS += -nolinenumbers +endif + +ifdef DISABLE_MEMMGR +ECFLAGS += -DDISABLE_MEMMGR +endif # PLATFORM-SPECIFIC OPTIONS @@ -130,7 +146,7 @@ CECFLAGS += -cpp $(_CPP) all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS) $(ECS) $(ARCH_FLAGS) $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols obj/$(MODULE).$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX) -o $(OBJ)$(MODULE).main.ec @@ -194,6 +210,12 @@ $(OBJ)List.sym: src/com/containers/List.ec $(OBJ)Map.sym: src/com/containers/Map.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/com/containers/Map.ec -o $(OBJ)Map.sym +$(OBJ)HashMap.sym: src/com/containers/HashMap.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM1_PRJ_CFLAGS) -c $(call quote_path,src/com/containers/HashMap.ec) -o $(call quote_path,$@) + +$(OBJ)HashTable.sym: src/com/containers/HashTable.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM1_PRJ_CFLAGS) -c $(call quote_path,src/com/containers/HashTable.ec) -o $(call quote_path,$@) + $(OBJ)BinaryTree.sym: src/com/BinaryTree.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/com/BinaryTree.ec -o $(OBJ)BinaryTree.sym @@ -244,6 +266,12 @@ $(OBJ)List.c: src/com/containers/List.ec $(OBJ)List.sym | $(SYMBOLS) $(OBJ)Map.c: src/com/containers/Map.ec $(OBJ)Map.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c src/com/containers/Map.ec -o $(OBJ)Map.c -symbols $(OBJ) +$(OBJ)HashMap.c: src/com/containers/HashMap.ec $(OBJ)HashMap.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM1_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,src/com/containers/HashMap.ec) -o $(call quote_path,$@) -symbols $(OBJ) + +$(OBJ)HashTable.c: src/com/containers/HashTable.ec $(OBJ)HashTable.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(CUSTOM1_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,src/com/containers/HashTable.ec) -o $(call quote_path,$@) -symbols $(OBJ) + $(OBJ)BinaryTree.c: src/com/BinaryTree.ec $(OBJ)BinaryTree.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c src/com/BinaryTree.ec -o $(OBJ)BinaryTree.c -symbols $(OBJ) @@ -294,6 +322,15 @@ $(OBJ)List.o: $(OBJ)List.c $(OBJ)Map.o: $(OBJ)Map.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)Map.c -o $(OBJ)Map.o +$(OBJ)HashMap$(O): $(OBJ)HashMap.c + $(CC) $(CFLAGS) $(CUSTOM1_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,$(OBJ)HashMap.c) -o $(call quote_path,$@) + +$(OBJ)HashTable$(O): $(OBJ)HashTable.c + $(CC) $(CFLAGS) $(CUSTOM1_PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,$(OBJ)HashTable.c) -o $(call quote_path,$@) + +$(OBJ)mmhash$(O): src/gfx/newFonts/cc/mmhash.c + $(CC) $(CFLAGS) $(CUSTOM1_PRJ_CFLAGS) -c $(call quote_path,src/gfx/newFonts/cc/mmhash.c) -o $(call quote_path,$@) + $(OBJ)BinaryTree.o: $(OBJ)BinaryTree.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)BinaryTree.c -o $(OBJ)BinaryTree.o @@ -318,7 +355,7 @@ $(OBJ)instance.c.o: src/com/instance.c $(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.c -o $(OBJ)$(MODULE).main$(O) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -343,9 +380,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile.ecereCOM: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/ecere/Makefile.installer b/ecere/Makefile.installer index 8f3428dc31..26eb6a39dc 100644 --- a/ecere/Makefile.installer +++ b/ecere/Makefile.installer @@ -1,3 +1,7 @@ +ifneq ($(V),1) +.SILENT: +endif + .PHONY: all objdir cleantarget clean realclean distclean # CORE VARIABLES @@ -367,9 +371,14 @@ CUSTOM1_PRJ_CFLAGS = \ -DECERE_COM_MODULE \ $(PRJ_CFLAGS) -ECFLAGS += -module $(MODULE) -ECFLAGS += \ - -nolinenumbers -defaultns ecere +ECFLAGS += -module $(MODULE) -defaultns ecere +ifndef DEBUG +ECFLAGS += -nolinenumbers +endif + +ifdef DISABLE_MEMMGR +ECFLAGS += -DDISABLE_MEMMGR +endif # PLATFORM-SPECIFIC OPTIONS @@ -1394,7 +1403,7 @@ $(OBJ)instance.c.o: src/com/instance.c $(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.c -o $(call quote_path,$@) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) diff --git a/ecere/Makefile.vanilla b/ecere/Makefile.vanilla index 6dfece25ce..e0b2914221 100644 --- a/ecere/Makefile.vanilla +++ b/ecere/Makefile.vanilla @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -373,9 +377,14 @@ CUSTOM2_PRJ_CFLAGS = \ -D_FILE_OFFSET_BITS=64,) \ $(PRJ_CFLAGS) -ECFLAGS += -module $(MODULE) -ECFLAGS += \ - -nolinenumbers -defaultns ecere +ECFLAGS += -module $(MODULE) -defaultns ecere +ifndef DEBUG +ECFLAGS += -nolinenumbers +endif + +ifdef DISABLE_MEMMGR +ECFLAGS += -DDISABLE_MEMMGR +endif # PLATFORM-SPECIFIC OPTIONS @@ -461,7 +470,7 @@ CECFLAGS += -cpp $(_CPP) all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS) $(ECS) $(ARCH_FLAGS) $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX) -o $(OBJ)$(MODULE).main.ec @@ -1444,7 +1453,7 @@ $(OBJ)instance.c.o: src/com/instance.c $(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.c -o $(OBJ)$(MODULE).main$(O) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -1475,9 +1484,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile.vanilla: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/ecere/ecere.epj b/ecere/ecere.epj index 932ae6a15c..7a74dd9a51 100644 --- a/ecere/ecere.epj +++ b/ecere/ecere.epj @@ -3,88 +3,12 @@ "ModuleName" : "ecere", "ModuleVersion" : "0.44", "Description" : "Ecere Runtime library", - "License" : "The Ecere SDK v0.44.15 - http://ecere.org - -The Ecere SDK is Free Open Source Software. It is provided with NO WARRANTY -expressed or implied to the extent permitted by law. - -Applications built with it can be distributed both commercially and non -commercially, along with the supporting Ecere runtime library(ies): - - * libecere.so / ecere.dll - - Core eC framework + GUI toolkit, 2D/3D graphics engine, networking - * libecereCOM.so / ecereCOM.dll - - Core eC framework only - * libEDA.so / EDA.dll - - Data Access System - * libEDASQLite.so / EDASQLite.dll - - SQLite driver for EDA - * libEcereAudio.so / EcereAudio.dll - - Audio output library (using ALSA / DirectSound) - -The Ecere SDK is distributed under the New BSD license: - -================================================================================ - - Copyright (c) 1996-2016, Jerome Jacovella-St-Louis - Copyright (c) 2005-2016, Ecere Corporation - - 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 Ecere Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS - \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -================================================================================ - -This software makes use of other software components whose licenses may also -apply, which are found in their respective source directories (most under -deps/). - -Among them: zlib, libPNG, libJPEG, giflib or libungif, HarfBuzz, FreeType, - Tango icons - -For EDA: SQLite (EDASQLite), libffi - -On Windows, applications built statically with the MinGW-w64 runtime should -include the MinGW-w64 runtime license in their application. - See ecere-sdk/extras/res/licenses/MinGW-w64.LICENSE or - Program Files/Ecere SDK/tdm/licenses/COPYING.MinGW-w64-runtime.txt - -The Ecere IDE also communicates with GCC for compiling -(MinGW/MinGW-w64 on Windows), GDB for debugging, and UPX for optionally -compressing executables. - -Each of these have their own license, which can be found from wherever you -obtained them or under: - Program Files/Ecere SDK/tdm/licenses/ and - Program Files/Ecere SDK/upx/doc/ -if distributed with the Ecere SDK Windows installer. -", + "License" : "The Ecere SDK v0.44.15 - http://ecere.org\n\nThe Ecere SDK is Free Open Source Software. It is provided with NO WARRANTY\nexpressed or implied to the extent permitted by law.\n\nApplications built with it can be distributed both commercially and non\ncommercially, along with the supporting Ecere runtime library(ies):\n\n * libecere.so / ecere.dll\n - Core eC framework + GUI toolkit, 2D/3D graphics engine, networking\n * libecereCOM.so / ecereCOM.dll\n - Core eC framework only\n * libEDA.so / EDA.dll\n - Data Access System\n * libEDASQLite.so / EDASQLite.dll\n - SQLite driver for EDA\n * libEcereAudio.so / EcereAudio.dll\n - Audio output library (using ALSA / DirectSound)\n\nThe Ecere SDK is distributed under the New BSD license:\n\n================================================================================\n\n Copyright (c) 1996-2016, Jerome Jacovella-St-Louis\n Copyright (c) 2005-2016, Ecere Corporation\n\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\n * Redistributions of source code must retain the above copyright notice,\n this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n * Neither the name of Ecere Corporation nor the names of its contributors\n may be used to endorse or promote products derived from this software\n without specific prior written permission.\n\n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n 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\n================================================================================\n\nThis software makes use of other software components whose licenses may also\napply, which are found in their respective source directories (most under\ndeps/).\n\nAmong them: zlib, libPNG, libJPEG, giflib or libungif, HarfBuzz, FreeType,\n Tango icons\n\nFor EDA: SQLite (EDASQLite), libffi\n\nOn Windows, applications built statically with the MinGW-w64 runtime should\ninclude the MinGW-w64 runtime license in their application.\n See ecere-sdk/extras/res/licenses/MinGW-w64.LICENSE or\n Program Files/Ecere SDK/tdm/licenses/COPYING.MinGW-w64-runtime.txt\n\nThe Ecere IDE also communicates with GCC for compiling\n(MinGW/MinGW-w64 on Windows), GDB for debugging, and UPX for optionally\ncompressing executables.\n\nEach of these have their own license, which can be found from wherever you\nobtained them or under:\n Program Files/Ecere SDK/tdm/licenses/ and\n Program Files/Ecere SDK/upx/doc/\nif distributed with the Ecere SDK Windows installer.\n", "Options" : { "Warnings" : "All", "PreprocessorDefinitions" : [ - "BUILDING_ECERE_COM" + "BUILDING_ECERE_COM", + "CURL_DISABLE_TYPECHECK" ], "IncludeDirs" : [ "src/gfx/drivers/harfbuzz", @@ -100,7 +24,8 @@ if distributed with the Ecere SDK Windows installer. "jpeg", "png", "z", - "freetype" + "freetype", + "curl" ] }, "Platforms" : [ @@ -122,10 +47,12 @@ if distributed with the Ecere SDK Windows installer. "Xrender", "GL", "ssl", - "crypto" + "crypto", + "etc" ], "LibraryDirs" : [ - "/usr/X11R6/lib" + "/usr/X11R6/lib", + "../deps/etc2comp/EtcLib/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)" ] } }, @@ -139,6 +66,7 @@ if distributed with the Ecere SDK Windows installer. "../deps/zlib-1.2.8", "../deps/freetype-2.3.12/include", "../deps/glext", + "../deps/curl-7.51.0/include", "$(OPENSSL_INCLUDE_DIR)" ], "Libraries" : [ @@ -157,8 +85,8 @@ if distributed with the Ecere SDK Windows installer. "winspool", "imm32", "ungif", - "eay32", - "ssleay32" + "ssl", + "crypto" ], "LibraryDirs" : [ "../deps/zlib-1.2.8/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)", @@ -166,6 +94,7 @@ if distributed with the Ecere SDK Windows installer. "../deps/libpng-1.6.12/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)", "../deps/libungif-4.1.1/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)", "../deps/freetype-2.3.12/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)", + "../deps/curl-7.51.0/obj/release.$(PLATFORM)$(COMPILER_SUFFIX)", "$(OPENSSL_BIN_DIR)", "$(OPENSSL_LIB_DIR)" ] @@ -215,7 +144,32 @@ if distributed with the Ecere SDK Windows installer. "PreprocessorDefinitions" : [ "_DEBUG" ] - } + }, + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "PreprocessorDefinitions" : [ + "ETC2_COMPRESS" + ], + "Libraries" : [ + "ncurses", + "pthread", + "m", + "dl", + "fontconfig", + "gif", + "X11", + "Xext", + "Xrender", + "GL", + "ssl", + "crypto", + "etc" + ] + } + } + ] }, { "Name" : "MemoryGuard", @@ -271,6 +225,9 @@ if distributed with the Ecere SDK Windows installer. "-msse3", "-msse4" ], + "LinkerOptions" : [ + "--wrap=fcntl64" + ], "FastMath" : true, "PostbuildCommands" : [ "$(call mkdir,../$(SODESTDIR))", @@ -281,15 +238,24 @@ if distributed with the Ecere SDK Windows installer. "$(if $(WINDOWS_HOST),,ln -sf $(LP)$(MODULE)$(SOV) $(DESTLIBDIR)/$(LP)$(MODULE)$(SO).0)", "$(if $(WINDOWS_HOST),,ln -sf $(LP)$(MODULE)$(SOV) $(DESTLIBDIR)/$(LP)$(MODULE)$(SO))" ] - } + }, + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "PreprocessorDefinitions" : [ + "ETC2_COMPRESS" + ] + } + } + ] }, { "Name" : "Static", "Options" : { - "Optimization" : "Size", + "Optimization" : "Speed", "PreprocessorDefinitions" : [ - "ECERE_STATIC", - "ECERE_NOSSL" + "ECERE_STATIC" ], "TargetType" : "StaticLibrary", "TargetFileName" : "ecereStatic", @@ -299,7 +265,8 @@ if distributed with the Ecere SDK Windows installer. "z", "freetype" ], - "Compress" : true + "Compress" : true, + "FastMath" : true } }, { @@ -372,14 +339,16 @@ if distributed with the Ecere SDK Windows installer. { "Name" : "WSMS", "Options" : { - "Optimization" : "Size", + "Optimization" : "Speed", "PreprocessorDefinitions" : [ "ECERE_STATIC", "ECERE_NO3D", - "ECERE_NOSSL" + "ECERE_NOSSL", + "ECERE_NOCURL" ], "TargetType" : "StaticLibrary", - "TargetFileName" : "ecereWSMS" + "TargetFileName" : "ecereWSMS", + "FastMath" : true }, "Platforms" : [ { @@ -420,15 +389,16 @@ if distributed with the Ecere SDK Windows installer. "ECERE_NOFONTCONFIG", "HIGH_DPI", "ECERE_NOSSL", - "_GLES" + "_GLES2" ], "IncludeDirs" : [ "../deps/jpeg-9a", - "../deps/libpng-1.4.0", + "../deps/libpng-1.6.12", "../deps/libungif-4.1.1/lib", "../deps/zlib", "../deps/freetype-2.3.12/include", - "../deps/glext" + "../deps/glext", + "../deps/curl-7.51.0/include" ], "FastMath" : true }, @@ -443,13 +413,66 @@ if distributed with the Ecere SDK Windows installer. "log", "android", "EGL", - "GLESv1_CM" + "GLESv3" ], "LibraryDirs" : [ "../deps/libungif-4.1.1/obj/release.$(PLATFORM).$(COMPILER)", "../deps/libpng-1.6.12/obj/release.$(PLATFORM).$(COMPILER)", "../deps/jpeg-9a/obj/release.$(PLATFORM).$(COMPILER)", - "../deps/freetype-2.3.12/obj/release.$(PLATFORM).$(COMPILER)" + "../deps/freetype-2.3.12/obj/release.$(PLATFORM).$(COMPILER)", + "../deps/curl-7.51.0/obj/release.$(PLATFORM).$(COMPILER)", + "../deps/etc2comp/EtcLib/obj/android.$(PLATFORM).$(COMPILER)" + ] + } + } + ] + }, + { + "Name" : "Lumin", + "Options" : { + "Debug" : false, + "Optimization" : "Speed", + "PreprocessorDefinitions" : [ + "ECERE_NOFONTCONFIG", + "HIGH_DPI", + "ECERE_NOSSL", + "_GLES2", + "_DEBUG", + "ECERE_STATIC" + ], + "IncludeDirs" : [ + "../deps/jpeg-9a", + "../deps/libpng-1.6.12", + "../deps/libungif-4.1.1/lib", + "../deps/zlib", + "../deps/freetype-2.3.12/include", + "../deps/glext", + "../deps/curl-7.51.0/include" + ], + "TargetType" : "StaticLibrary", + "TargetFileName" : "ecereLuminStatic", + "FastMath" : true + }, + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "Libraries" : [ + "m", + "dl", + "ungif", + "log", + "android", + "EGL", + "GLESv3" + ], + "LibraryDirs" : [ + "../deps/libungif-4.1.1/obj/release.$(PLATFORM).$(COMPILER)", + "../deps/libpng-1.6.12/obj/release.$(PLATFORM).$(COMPILER)", + "../deps/jpeg-9a/obj/release.$(PLATFORM).$(COMPILER)", + "../deps/freetype-2.3.12/obj/release.$(PLATFORM).$(COMPILER)", + "../deps/curl-7.51.0/obj/release.$(PLATFORM).$(COMPILER)", + "../deps/etc2comp/EtcLib/obj/android.$(PLATFORM).$(COMPILER)" ] } } @@ -459,7 +482,7 @@ if distributed with the Ecere SDK Windows installer. "Name" : "GLES", "Options" : { "PreprocessorDefinitions" : [ - "_GLES" + "_GLES2" ] } }, @@ -488,7 +511,8 @@ if distributed with the Ecere SDK Windows installer. "PreprocessorDefinitions" : [ "ECERE_STATIC", "NOBLENDING", - "ECERE_NOSSL" + "ECERE_NOSSL", + "ECERE_NONET" ], "TargetType" : "StaticLibrary", "TargetFileName" : "ecereStatic", @@ -510,6 +534,39 @@ if distributed with the Ecere SDK Windows installer. "TargetFileName" : "ecereStatic", "FastMath" : true } + }, + { + "Name" : "UWP", + "Options" : { + "NoLineNumbers" : false, + "Optimization" : "Speed", + "PreprocessorDefinitions" : [ + "_GLES3", + "__UWP__", + "ECERE_NOSSL", + "ECERE_NOFONTCONFIG", + "ECERE_NODINPUT" + ], + "FastMath" : true + }, + "Platforms" : [ + { + "Name" : "win32", + "Options" : { + "Libraries" : [ + "dxguid", + "dinput", + "winmm", + "libGLESv2", + "libEGL", + "ws2_32", + "kernel32", + "user32", + "ungif" + ] + } + } + ] } ], "Files" : [ @@ -854,7 +911,8 @@ if distributed with the Ecere SDK Windows installer. } } ] - } + }, + "ETC2Format.ec" ], "Configurations" : [ { @@ -1015,6 +1073,12 @@ if distributed with the Ecere SDK Windows installer. "ExcludeFromBuild" : false } }, + { + "Name" : "Lumin", + "Options" : { + "ExcludeFromBuild" : false + } + }, { "Name" : "StaticGLES", "Options" : { @@ -1074,6 +1138,12 @@ if distributed with the Ecere SDK Windows installer. "ExcludeFromBuild" : false } }, + { + "Name" : "Lumin", + "Options" : { + "ExcludeFromBuild" : false + } + }, { "Name" : "StaticGLES", "Options" : { @@ -1133,6 +1203,12 @@ if distributed with the Ecere SDK Windows installer. "ExcludeFromBuild" : false } }, + { + "Name" : "Lumin", + "Options" : { + "ExcludeFromBuild" : false + } + }, { "Name" : "StaticGLES", "Options" : { @@ -1163,6 +1239,12 @@ if distributed with the Ecere SDK Windows installer. "ExcludeFromBuild" : false } }, + { + "Name" : "Lumin", + "Options" : { + "ExcludeFromBuild" : false + } + }, { "Name" : "Static", "Options" : { @@ -1227,6 +1309,12 @@ if distributed with the Ecere SDK Windows installer. "Options" : { "ExcludeFromBuild" : false } + }, + { + "Name" : "UWP", + "Options" : { + "ExcludeFromBuild" : true + } } ] }, @@ -1252,11 +1340,23 @@ if distributed with the Ecere SDK Windows installer. "ExcludeFromBuild" : false } }, + { + "Name" : "Lumin", + "Options" : { + "ExcludeFromBuild" : false + } + }, { "Name" : "Installer", "Options" : { "ExcludeFromBuild" : true } + }, + { + "Name" : "WSMS", + "Options" : { + "ExcludeFromBuild" : true + } } ] }, @@ -1278,6 +1378,12 @@ if distributed with the Ecere SDK Windows installer. "ExcludeFromBuild" : false } }, + { + "Name" : "Lumin", + "Options" : { + "ExcludeFromBuild" : false + } + }, { "Name" : "Static", "Options" : { @@ -1289,8 +1395,20 @@ if distributed with the Ecere SDK Windows installer. "Options" : { "ExcludeFromBuild" : true } + }, + { + "Name" : "WSMS", + "Options" : { + "ExcludeFromBuild" : true + } } ] + }, + { + "FileName" : "GLMultiDraw.ec", + "Options" : { + "ExcludeFromBuild" : false + } } ], "Configurations" : [ @@ -1299,6 +1417,12 @@ if distributed with the Ecere SDK Windows installer. "Options" : { "ExcludeFromBuild" : false } + }, + { + "Name" : "UWP", + "Options" : { + "ExcludeFromBuild" : false + } } ] }, @@ -1389,6 +1513,28 @@ if distributed with the Ecere SDK Windows installer. } } ] + }, + { + "Name" : "UWP", + "Platforms" : [ + { + "Name" : "win32", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + }, + { + "Name" : "Debug", + "Platforms" : [ + { + "Name" : "win32", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] } ] }, @@ -1479,6 +1625,28 @@ if distributed with the Ecere SDK Windows installer. } } ] + }, + { + "Name" : "UWP", + "Platforms" : [ + { + "Name" : "win32", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + }, + { + "Name" : "Debug", + "Platforms" : [ + { + "Name" : "win32", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] } ] }, @@ -1569,6 +1737,17 @@ if distributed with the Ecere SDK Windows installer. } } ] + }, + { + "Name" : "UWP", + "Platforms" : [ + { + "Name" : "win32", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] } ] }, @@ -1604,6 +1783,17 @@ if distributed with the Ecere SDK Windows installer. } } ] + }, + { + "Name" : "UWP", + "Platforms" : [ + { + "Name" : "win32", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] } ] }, @@ -1705,6 +1895,17 @@ if distributed with the Ecere SDK Windows installer. } ] }, + { + "Name" : "Lumin", + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + }, { "Name" : "MinEmscripten", "Platforms" : [ @@ -1843,6 +2044,17 @@ if distributed with the Ecere SDK Windows installer. } } ] + }, + { + "Name" : "UWP", + "Platforms" : [ + { + "Name" : "win32", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] } ] }, @@ -2009,6 +2221,17 @@ if distributed with the Ecere SDK Windows installer. } } ] + }, + { + "Name" : "UWP", + "Platforms" : [ + { + "Name" : "win32", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] } ] }, @@ -2088,6 +2311,17 @@ if distributed with the Ecere SDK Windows installer. } } ] + }, + { + "Name" : "UWP", + "Platforms" : [ + { + "Name" : "win32", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] } ] }, @@ -2136,6 +2370,17 @@ if distributed with the Ecere SDK Windows installer. } ] }, + { + "Name" : "Lumin", + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + }, { "Name" : "Emscripten", "Platforms" : [ @@ -2173,6 +2418,71 @@ if distributed with the Ecere SDK Windows installer. } ] }, + { + "Folder" : "newFonts", + "Files" : [ + { + "Folder" : "cc", + "Files" : [ + "cc.c", + "cc.h", + "cchybridsort.h", + "ccmergesort.h", + "cpuconfig.h", + "mm.c", + "mm.h", + "mmatomic.h", + "mmthread.h", + "ccstr.c", + "ccstr.h", + { + "FileName" : "mmhash.c", + "Configurations" : [ + { + "Name" : "WSMS", + "Options" : { + "ExcludeFromBuild" : false + } + }, + { + "Name" : "Vanilla", + "Options" : { + "ExcludeFromBuild" : false + } + } + ] + }, + "mmhash.h", + "mmhashinline.h", + "mmhashinternal.h" + ] + }, + "atlasBuilder.ec", + "drawManager.ec", + "fmFontManager.ec", + "fontRenderer.ec", + "textureManager.ec" + ], + "Options" : { + "IncludeDirs" : [ + "src/gfx/newFonts/cc" + ] + }, + "Configurations" : [ + { + "Name" : "WSMS", + "Options" : { + "ExcludeFromBuild" : true + } + }, + { + "Name" : "Vanilla", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + }, "Bitmap.ec", "BitmapResource.ec", "Color.ec", @@ -2181,8 +2491,34 @@ if distributed with the Ecere SDK Windows installer. "FontResource.ec", "Resource.ec", "Surface.ec", - "fontManagement.ec", - "fontRendering.ec", + { + "FileName" : "fontManagement.ec", + "Configurations" : [ + { + "Name" : "Vanilla", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + }, + { + "FileName" : "fontRendering.ec", + "Configurations" : [ + { + "Name" : "WSMS", + "Options" : { + "ExcludeFromBuild" : true + } + }, + { + "Name" : "Vanilla", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + }, { "FileName" : "imgDistMap.ec", "Options" : { @@ -2396,6 +2732,17 @@ if distributed with the Ecere SDK Windows installer. } ] }, + { + "Name" : "Lumin", + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + }, { "Name" : "Emscripten", "Platforms" : [ @@ -2515,6 +2862,17 @@ if distributed with the Ecere SDK Windows installer. } } ] + }, + { + "Name" : "UWP", + "Platforms" : [ + { + "Name" : "win32", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] } ] }, @@ -2563,6 +2921,17 @@ if distributed with the Ecere SDK Windows installer. } ] }, + { + "Name" : "Lumin", + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + }, { "Name" : "Emscripten", "Platforms" : [ @@ -2615,7 +2984,28 @@ if distributed with the Ecere SDK Windows installer. } ] }, - "test_html5.c" + { + "FileName" : "LuminInterface.ec", + "Configurations" : [ + { + "Name" : "Lumin", + "Options" : { + "ExcludeFromBuild" : false + } + } + ] + }, + { + "FileName" : "UWPInterface.ec", + "Configurations" : [ + { + "Name" : "UWP", + "Options" : { + "ExcludeFromBuild" : false + } + } + ] + } ], "Options" : { "ExcludeFromBuild" : true @@ -2704,6 +3094,12 @@ if distributed with the Ecere SDK Windows installer. "Options" : { "ExcludeFromBuild" : true } + }, + { + "Name" : "Emscripten", + "Options" : { + "ExcludeFromBuild" : true + } } ] }, @@ -2775,13 +3171,13 @@ if distributed with the Ecere SDK Windows installer. } }, { - "Name" : "Installer", + "Name" : "Lumin", "Options" : { "ExcludeFromBuild" : true } }, { - "Name" : "Emscripten", + "Name" : "Installer", "Options" : { "ExcludeFromBuild" : true } @@ -2813,6 +3209,12 @@ if distributed with the Ecere SDK Windows installer. "Options" : { "ExcludeFromBuild" : true } + }, + { + "Name" : "Emscripten", + "Options" : { + "ExcludeFromBuild" : true + } } ] }, @@ -2829,7 +3231,23 @@ if distributed with the Ecere SDK Windows installer. "CustomAVLTree.ec", "LinkList.ec", "List.ec", - "Map.ec" + "Map.ec", + { + "FileName" : "HashMap.ec", + "Options" : { + "IncludeDirs" : [ + "src/gfx/newFonts/cc" + ] + } + }, + { + "FileName" : "HashTable.ec", + "Options" : { + "IncludeDirs" : [ + "src/gfx/newFonts/cc" + ] + } + } ] }, { @@ -3182,7 +3600,7 @@ if distributed with the Ecere SDK Windows installer. ] }, { - "FileName" : "C:/Windows/Fonts/tahoma.ttf", + "FileName" : "res/fonts/tahomabd.ttf", "Options" : { "ExcludeFromBuild" : true }, @@ -3192,17 +3610,9 @@ if distributed with the Ecere SDK Windows installer. "Options" : { "ExcludeFromBuild" : false } - } - ] - }, - { - "FileName" : "C:/Windows/Fonts/tahomabd.ttf", - "Options" : { - "ExcludeFromBuild" : true - }, - "Configurations" : [ + }, { - "Name" : "Android", + "Name" : "Lumin", "Options" : { "ExcludeFromBuild" : false } @@ -3210,7 +3620,7 @@ if distributed with the Ecere SDK Windows installer. ] }, { - "FileName" : "C:/Windows/Fonts/arial.ttf", + "FileName" : "res/fonts/tahoma.ttf", "Options" : { "ExcludeFromBuild" : true }, @@ -3220,22 +3630,15 @@ if distributed with the Ecere SDK Windows installer. "Options" : { "ExcludeFromBuild" : false } - } - ] - }, - { - "FileName" : "C:/Windows/Fonts/arialbd.ttf", - "Options" : { - "ExcludeFromBuild" : true - }, - "Configurations" : [ + }, { - "Name" : "Android", + "Name" : "Lumin", "Options" : { "ExcludeFromBuild" : false } } ] - } + }, + "mozilla-cacert.pem" ] } diff --git a/ecere/ecereCOM.epj b/ecere/ecereCOM.epj index 5166b8cac4..8c83c1b3bc 100644 --- a/ecere/ecereCOM.epj +++ b/ecere/ecereCOM.epj @@ -114,8 +114,16 @@ "CustomAVLTree.ec", "LinkList.ec", "List.ec", - "Map.ec" - ] + "Map.ec", + "HashMap.ec", + "HashTable.ec", + "src/gfx/newFonts/cc/mmhash.c" + ], + "Options" : { + "IncludeDirs" : [ + "src/gfx/newFonts/cc" + ] + } }, "BinaryTree.ec", "BTNode.ec", diff --git a/ecere/res/fonts/DejaVuSans-Bold.ttf b/ecere/res/fonts/DejaVuSans-Bold.ttf new file mode 100644 index 0000000000..2de47c1f0b Binary files /dev/null and b/ecere/res/fonts/DejaVuSans-Bold.ttf differ diff --git a/ecere/res/fonts/DejaVuSans.ttf b/ecere/res/fonts/DejaVuSans.ttf new file mode 100644 index 0000000000..3295ea2853 Binary files /dev/null and b/ecere/res/fonts/DejaVuSans.ttf differ diff --git a/ecere/res/fonts/tahoma.ttf b/ecere/res/fonts/tahoma.ttf new file mode 120000 index 0000000000..e89f883c6c --- /dev/null +++ b/ecere/res/fonts/tahoma.ttf @@ -0,0 +1 @@ +DejaVuSans.ttf \ No newline at end of file diff --git a/ecere/res/fonts/tahomabd.ttf b/ecere/res/fonts/tahomabd.ttf new file mode 120000 index 0000000000..a33b94e62c --- /dev/null +++ b/ecere/res/fonts/tahomabd.ttf @@ -0,0 +1 @@ +DejaVuSans-Bold.ttf \ No newline at end of file diff --git a/ecere/res/mozilla-cacert.pem b/ecere/res/mozilla-cacert.pem new file mode 100644 index 0000000000..3a08fd8db4 --- /dev/null +++ b/ecere/res/mozilla-cacert.pem @@ -0,0 +1,3435 @@ +## +## Bundle of CA Root Certificates +## +## Certificate data from Mozilla as of: Tue Dec 8 04:12:05 2020 GMT +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## +## Conversion done with mk-ca-bundle.pl version 1.28. +## SHA256: d820b8696d8ffe42064a1384a56a8981cdc7e7e198036bbb5fa04a6c282dd9a2 +## + + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +GlobalSign Root CA - R2 +======================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 +ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp +s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN +S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL +TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C +ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i +YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN +BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp +9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu +01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 +9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ +KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy +T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT +J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e +nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +GeoTrust Global CA +================== +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw +MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo +BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet +8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc +T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU +vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q +zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 +d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 +mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p +XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm +Mw== +-----END CERTIFICATE----- + +GeoTrust Universal CA +===================== +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 +MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu +Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t +JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e +RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs +7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d +8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V +qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga +Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB +Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu +KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 +ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 +XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB +hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 +qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL +oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK +xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF +KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 +DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK +xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU +p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI +P/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +GeoTrust Universal CA 2 +======================= +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 +MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg +SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 +DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 +j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q +JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a +QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 +WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP +20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn +ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC +SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG +8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 ++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E +BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ +4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ +mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq +A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg +Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP +pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d +FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp +gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm +X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +QuoVadis Root CA +================ +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE +ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz +MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp +cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD +EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk +J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL +F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL +YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen +AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w +PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y +ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 +MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj +YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs +ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW +Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu +BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw +FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 +tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo +fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul +LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x +gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi +5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi +5nrQNiOKSnQ2+Q== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx +ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6 +XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk +lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB +lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy +lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt +66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn +wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh +D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy +BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie +J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud +DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU +a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv +Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3 +UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm +VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK ++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW +IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1 +WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X +f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II +4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8 +VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx +OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg +DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij +KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K +DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv +BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp +p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8 +nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX +MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM +Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz +uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT +BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj +YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB +BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD +VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4 +ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE +AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV +qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s +hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z +POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2 +Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp +8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC +bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu +g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p +vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr +qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +Sonera Class 2 Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG +U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw +NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh +IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 +/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT +dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG +f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P +tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH +nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT +XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt +0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI +cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph +Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx +EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH +llpwrN9M +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +DST Root CA X3 +============== +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK +ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X +DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 +cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT +rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 +UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy +xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d +utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ +MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug +dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE +GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw +RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS +fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw +EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN +MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp +c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq +t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C +jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg +vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF +ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR +AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend +jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO +peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR +7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi +GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 +OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm +5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr +44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf +Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m +Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp +mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk +vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf +KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br +NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj +viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT +BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X +DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3 +aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644 +N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm ++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH +6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu +MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h +qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5 +FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs +ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc +celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X +CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB +tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P +4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F +kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L +3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx +/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa +DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP +e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu +WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ +DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub +DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ +cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN +b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 +nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge +RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt +tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI +hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K +Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN +NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa +Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG +1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +thawte Primary Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 +MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg +SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv +KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT +FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs +oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ +1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc +q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K +aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p +afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF +AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE +uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 +jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH +z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G5 +============================================================ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh +dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz +j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD +Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r +fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv +Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG +SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ +X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE +KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC +Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE +ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +Network Solutions Certificate Authority +======================================= +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr +IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx +MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx +jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT +aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT +crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc +/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB +AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv +bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA +A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q +4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ +GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD +ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +Cybertrust Global Root +====================== +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li +ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 +MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD +ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA ++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW +0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL +AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin +89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT +8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 +MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G +A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO +lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi +5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 +hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T +X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G3 +============================================= +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz +NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo +YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT +LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j +K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE +c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C +IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu +dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr +2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 +cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE +Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s +t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +thawte Primary Root CA - G2 +=========================== +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC +VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu +IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg +Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV +MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG +b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt +IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS +LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 +8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN +G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K +rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +thawte Primary Root CA - G3 +=========================== +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w +ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD +VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG +A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At +P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC ++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY +7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW +vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ +KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK +A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC +8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm +er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G2 +============================================= +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 +OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl +b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG +BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc +KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ +EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m +ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 +npaqBA+K +-----END CERTIFICATE----- + +VeriSign Universal Root Certification Authority +=============================================== +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj +1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP +MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 +9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I +AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR +tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G +CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O +a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 +Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx +Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx +P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P +wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 +mJO37M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G4 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC +VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 +b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz +ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo +b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 +Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz +rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw +HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u +Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD +A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx +AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) Főtanúsítvány +======================================== +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG +EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz +MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu +QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ +03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK +ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU ++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC +PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT +OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK +F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK +0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+ +0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB +leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID +AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+ +SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG +NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l +Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga +kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q +hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs +g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5 +aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5 +nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC +ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo +Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z +WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +Chambers of Commerce Root - 2008 +================================ +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy +Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl +ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF +EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl +cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA +XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj +h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ +ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk +NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g +D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 +lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ +0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 +EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI +G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ +BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh +bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh +bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC +CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH +AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 +wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH +3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU +RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 +M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 +YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF +9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK +zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG +nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ +-----END CERTIFICATE----- + +Global Chambersign Root - 2008 +============================== +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx +NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg +Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ +QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf +VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf +XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 +ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB +/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA +TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M +H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe +Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF +HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB +AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT +BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE +BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm +aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm +aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp +1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 +dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG +/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 +ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s +dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg +9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH +foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du +qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr +P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq +c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +EC-ACC +====== +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE +BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w +ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD +VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE +CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT +BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 +MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt +SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl +Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh +cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK +w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT +ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 +HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a +E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw +0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD +VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 +Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l +dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ +lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa +Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe +l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 +E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D +5EI= +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2011 +======================================================= +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT +O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y +aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT +AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo +IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI +1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa +71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u +8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH +3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ +MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 +MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu +b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt +XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD +/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N +7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +Actalis Authentication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM +BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE +AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky +MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz +IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ +wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa +by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 +zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f +YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 +oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l +EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 +hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 +EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 +jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY +iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI +WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 +JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx +K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ +Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC +4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo +2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz +lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem +OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 +vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +Trustis FPS Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG +EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 +IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV +BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ +RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk +H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa +cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt +o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA +AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd +BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c +GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC +yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P +8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV +l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl +iB6XzCGcKQENZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +Buypass Class 2 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X +DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 +g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn +9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b +/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU +CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff +awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI +zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn +Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX +Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs +M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI +osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S +aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd +DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD +LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 +oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC +wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS +CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN +rJgWVqA= +-----END CERTIFICATE----- + +Buypass Class 3 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X +DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH +sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR +5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh +7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ +ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH +2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV +/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ +RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA +Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq +j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G +uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG +Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 +ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 +KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz +6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug +UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe +eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi +Cp/HuZc= +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 3 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx +MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK +9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU +NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF +iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W +0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr +AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb +fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT +ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h +P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe +Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE +LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD +ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA +BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv +KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z +p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC +AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ +4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y +eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw +MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G +PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw +OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm +2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV +dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph +X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 EV 2009 +================================= +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS +egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh +zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T +7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 +sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 +11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv +cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v +ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El +MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp +b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh +c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ +PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX +ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA +NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv +w9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +CA Disig Root R2 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC +w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia +xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 +A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S +GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV +g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa +5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE +koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A +Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i +Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u +Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV +sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je +dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 +1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx +mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 +utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 +sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg +UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV +7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +ACCVRAIZ1 +========= +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB +SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 +MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH +UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM +jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 +RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD +aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ +0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG +WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 +8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR +5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J +9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK +Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw +Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu +Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM +Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA +QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh +AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA +YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj +AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA +IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk +aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 +dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 +MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI +hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E +R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN +YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 +nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ +TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 +sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg +Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd +3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p +EfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +TWCA Global Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT +CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD +QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK +EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C +nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV +r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR +Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV +tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W +KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 +sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p +yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn +kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI +zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g +cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M +8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg +/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg +lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP +A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m +i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 +EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 +zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= +-----END CERTIFICATE----- + +TeliaSonera Root CA v1 +====================== +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE +CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4 +MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW +VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+ +6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA +3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k +B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn +Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH +oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3 +F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ +oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7 +gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc +TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB +AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW +DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm +zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW +pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV +G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc +c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT +JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2 +qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6 +Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems +WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +E-Tugra Certification Authority +=============================== +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w +DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls +ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw +NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx +QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl +cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD +DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd +hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K +CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g +ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ +BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0 +E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz +rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq +jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5 +dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB +/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG +MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK +kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO +XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807 +VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo +a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc +dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV +KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT +Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0 +8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G +C7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 2 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx +MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ +SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F +vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970 +2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV +WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy +YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4 +r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf +vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR +3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg== +-----END CERTIFICATE----- + +Atos TrustedRoot 2011 +===================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU +cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4 +MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG +A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV +hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr +54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+ +DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320 +HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR +z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R +l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ +bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h +k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh +TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9 +61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G +3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +QuoVadis Root CA 1 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE +PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm +PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6 +Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN +ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l +g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV +7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX +9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f +iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg +t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI +hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3 +GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct +Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP ++V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh +3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa +wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6 +O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0 +FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV +hMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +QuoVadis Root CA 2 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh +ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY +NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t +oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o +MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l +V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo +L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ +sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD +6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh +lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI +hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K +pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9 +x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz +dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X +U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw +mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD +zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN +JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr +O3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +QuoVadis Root CA 3 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286 +IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL +Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe +6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3 +I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U +VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7 +5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi +Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM +dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt +rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI +hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS +t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ +TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du +DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib +Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD +hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX +0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW +dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2 +PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +DigiCert Assured ID Root G2 +=========================== +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw +MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH +35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq +bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw +VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP +YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn +lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO +w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv +0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz +d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW +hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M +jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +DigiCert Assured ID Root G3 +=========================== +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD +VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb +RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs +KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF +UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy +YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy +1vUhZscv6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +DigiCert Global Root G2 +======================= +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx +MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ +kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO +3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV +BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM +UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB +o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu +5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr +F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U +WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH +QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/ +iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +DigiCert Global Root G3 +======================= +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD +VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw +MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k +aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C +AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O +YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp +Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y +3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34 +VOKa5Vt8sycX +-----END CERTIFICATE----- + +DigiCert Trusted Root G4 +======================== +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw +HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp +pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o +k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa +vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY +QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6 +MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm +mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7 +f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH +dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8 +oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY +ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr +yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy +7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah +ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN +5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb +/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa +5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK +G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP +82Z+ +-----END CERTIFICATE----- + +COMODO RSA Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn +dJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ +FGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+ +5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG +x8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX +2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL +OvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3 +sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C +GCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5 +WdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt +rFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+ +nq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg +tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW +sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp +pC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA +zMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq +ZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52 +7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I +LaZRfyHBNVOFBkpdn627G190 +-----END CERTIFICATE----- + +USERTrust RSA Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz +0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j +Y0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn +RghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O ++T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq +/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE +Y1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM +lXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8 +yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+ +eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW +FPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ +7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ +Eg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM +8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi +FSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi +yA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c +J2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw +sAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx +Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +USERTrust ECC Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2 +0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez +nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV +HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB +HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu +9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R4 +=========================== +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprl +OQcJFspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAwDgYDVR0P +AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61FuOJAf/sKbvu+M8k8o4TV +MAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGXkPoUVy0D7O48027KqGx2vKLeuwIgJ6iF +JzWbVsaj8kfSt24bAgAXqmemFZHe+pTsewv4n4Q= +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R5 +=========================== +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6 +SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS +h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx +uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7 +yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G3 +================================== +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloXDTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMC +TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l +ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4y +olQPcPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WWIkYFsO2t +x1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqXxz8ecAgwoNzFs21v0IJy +EavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFyKJLZWyNtZrVtB0LrpjPOktvA9mxjeM3K +Tj215VKb8b475lRgsGYeCasH/lSJEULR9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUur +mkVLoR9BvUhTFXFkC4az5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU5 +1nus6+N86U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7Ngzp +07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHPbMk7ccHViLVlvMDo +FxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXtBznaqB16nzaeErAMZRKQFWDZJkBE +41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTtXUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleu +yjWcLhL75LpdINyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwpLiniyMMB8jPq +KqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8Ipf3YF3qKS9Ysr1YvY2WTxB1 +v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixpgZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA +8KCWAg8zxXHzniN9lLf9OtMJgwYh/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b +8KKaa8MFSu1BYBQw0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0r +mj1AfsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq4BZ+Extq +1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR1VmiiXTTn74eS9fGbbeI +JG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/QFH1T/U67cjF68IeHRaVesd+QnGTbksV +tzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM94B7IWcnMFk= +-----END CERTIFICATE----- + +Staat der Nederlanden EV Root CA +================================ +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M +MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl +cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk +SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW +O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r +0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8 +Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV +XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr +08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV +0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd +74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx +fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa +ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu +c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq +5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN +b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN +f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi +5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4 +WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK +DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy +eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg== +-----END CERTIFICATE----- + +IdenTrust Commercial Root CA 1 +============================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS +b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES +MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB +IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld +hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/ +mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi +1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C +XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl +3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy +NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV +WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg +xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix +uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI +hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg +ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt +ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV +YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX +feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro +kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe +2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz +Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R +cGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +IdenTrust Public Sector Root CA 1 +================================= +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv +ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV +UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS +b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy +P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6 +Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI +rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf +qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS +mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn +ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh +LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v +iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL +4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B +Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw +DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A +mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt +GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt +m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx +NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4 +Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI +ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC +ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ +3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +Entrust Root Certification Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy +bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug +b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw +HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT +DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx +OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP +/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz +HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU +s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y +TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx +AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6 +0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z +iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi +nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+ +vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO +e4pIb4tF9g== +-----END CERTIFICATE----- + +Entrust Root Certification Authority - EC1 +========================================== +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx +FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn +YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw +FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs +LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg +dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt +IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy +AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef +9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h +vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8 +kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +CFCA EV ROOT +============ +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE +CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB +IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw +MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD +DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV +BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD +7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN +uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW +ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7 +xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f +py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K +gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol +hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ +tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf +BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q +ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua +4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG +E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX +BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn +aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy +PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX +kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C +ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GB CA +=============================== +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG +EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw +MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds +b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX +scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP +rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk +9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o +Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg +GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI +hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD +dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0 +VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui +HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +SZAFIR ROOT CA2 +=============== +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG +A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV +BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ +BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD +VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q +qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK +DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE +2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ +ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi +ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P +AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC +AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5 +O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67 +oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul +4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6 ++/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +Certum Trusted Network CA 2 +=========================== +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE +BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1 +bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y +ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ +TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB +IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9 +7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o +CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b +Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p +uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130 +GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ +9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB +Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye +hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM +BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI +hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW +Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA +L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo +clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM +pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb +w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo +J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm +ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX +is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7 +zAYspsbiDrW5viSP +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2015 +======================================================= +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT +BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0 +aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx +MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg +QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV +BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw +MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv +bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh +iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+ +6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd +FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr +i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F +GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2 +fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu +iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI +hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+ +D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM +d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y +d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn +82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb +davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F +Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt +J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa +JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q +p/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions ECC RootCA 2015 +=========================================================== +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0 +aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u +cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw +MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj +IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD +VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290 +Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP +dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK +Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA +GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn +dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +ISRG Root X1 +============ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE +BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD +EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG +EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT +DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r +Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1 +3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K +b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN +Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ +4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf +1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu +hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH +usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r +OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G +A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY +9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV +0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt +hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw +TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx +e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA +JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD +YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n +JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ +m+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +AC RAIZ FNMT-RCM +================ +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT +AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw +MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD +TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf +qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr +btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL +j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou +08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw +WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT +tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ +47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC +ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa +i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o +dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s +D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ +j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT +Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW ++YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7 +Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d +8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm +5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG +rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +Amazon Root CA 1 +================ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1 +MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH +FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ +gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t +dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce +VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3 +DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM +CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy +8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa +2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2 +xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +Amazon Root CA 2 +================ +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1 +MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4 +kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp +N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9 +AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd +fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx +kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS +btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0 +Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN +c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+ +3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw +DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA +A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE +YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW +xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ +gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW +aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV +Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3 +KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi +JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw= +-----END CERTIFICATE----- + +Amazon Root CA 3 +================ +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB +f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr +Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43 +rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc +eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +Amazon Root CA 4 +================ +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN +/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri +83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA +MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1 +AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT +D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr +IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g +TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp +ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD +VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt +c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth +bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11 +IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8 +6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc +wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0 +3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9 +WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU +ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc +lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R +e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j +q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +GDCA TrustAUTH R5 ROOT +====================== +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04xMjAw +BgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQD +DBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVow +YjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJjDp6L3TQs +AlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBjTnnEt1u9ol2x8kECK62p +OqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+uKU49tm7srsHwJ5uu4/Ts765/94Y9cnrr +pftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ +9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQ +xXABZG12ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsM +R6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZ +D2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4 +oR24qoAATILnsn8JuLwwoC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx +9hoh49pwBiFYFIeFd3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlR +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9 +H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35 +6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd ++PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQ +HtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBD +F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ +8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQXR4EzzffHqhmsYzmIGrv +/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0LcIymSLumoRT2+1hEmRSuqguT +aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +TrustCor RootCert CA-1 +====================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYDVQQGEwJQQTEP +MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig +U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkx +MjMxMTcyMzE2WjCBpDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFu +YW1hIENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUGA1UECwwe +VHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZUcnVzdENvciBSb290Q2Vy +dCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv463leLCJhJrMxnHQFgKq1mq +jQCj/IDHUHuO1CAmujIS2CNUSSUQIpidRtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4 +pQa81QBeCQryJ3pS/C3Vseq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0 +JEsq1pme9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CVEY4h +gLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorWhnAbJN7+KIor0Gqw +/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/DeOxCbeKyKsZn3MzUOcwHwYDVR0j +BBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwDQYJKoZIhvcNAQELBQADggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5 +mDo4Nvu7Zp5I/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf +ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZyonnMlo2HD6C +qFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djtsL1Ac59v2Z3kf9YKVmgenFK+P +3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdNzl/HHk484IkzlQsPpTLWPFp5LBk= +-----END CERTIFICATE----- + +TrustCor RootCert CA-2 +====================== +-----BEGIN CERTIFICATE----- +MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNVBAYTAlBBMQ8w +DQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQwIgYDVQQKDBtUcnVzdENvciBT +eXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0 +eTEfMB0GA1UEAwwWVHJ1c3RDb3IgUm9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEy +MzExNzI2MzlaMIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5h +bWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U +cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0 +IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnIG7CKqJiJJWQdsg4foDSq8Gb +ZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9Nk +RvRUqdw6VC0xK5mC8tkq1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1 +oYxOdqHp2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nKDOOb +XUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hapeaz6LMvYHL1cEksr1 +/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF3wP+TfSvPd9cW436cOGlfifHhi5q +jxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQP +eSghYA2FFn3XVDjxklb9tTNMg9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+Ctg +rKAmrhQhJ8Z3mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh +8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAdBgNVHQ4EFgQU +2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6UnrybPZx9mCAZ5YwwYrIwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/h +Osh80QA9z+LqBrWyOrsGS2h60COXdKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnp +kpfbsEZC89NiqpX+MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv +2wnL/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RXCI/hOWB3 +S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYaZH9bDTMJBzN7Bj8RpFxw +PIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dv +DDqPys/cA8GiCcjl/YBeyGBCARsaU1q7N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYU +RpFHmygk71dSTlxCnKr3Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANE +xdqtvArBAs8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp5KeX +RKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu1uwJ +-----END CERTIFICATE----- + +TrustCor ECA-1 +============== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYDVQQGEwJQQTEP +MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig +U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkxFzAVBgNVBAMMDlRydXN0Q29yIEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3Mjgw +N1owgZwxCzAJBgNVBAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5 +MSQwIgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29y +IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3IgRUNBLTEwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb3w9U73NjKYKtR8aja+3+XzP4Q1HpGjOR +MRegdMTUpwHmspI+ap3tDvl0mEDTPwOABoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23 +xFUfJ3zSCNV2HykVh0A53ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmc +p0yJF4OuowReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/wZ0+ +fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZFZtS6mFjBAgMBAAGj +YzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAfBgNVHSMEGDAWgBREnkj1zG1I1KBL +f/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF +AAOCAQEABT41XBVwm8nHc2FvcivUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u +/ukZMjgDfxT2AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F +hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50soIipX1TH0Xs +J5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BIWJZpTdwHjFGTot+fDz2LYLSC +jaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1WitJ/X5g== +-----END CERTIFICATE----- + +SSL.com Root Certification Authority RSA +======================================== +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxDjAM +BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24x +MTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYw +MjEyMTczOTM5WhcNNDEwMjEyMTczOTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx +EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NM +LmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2RxFdHaxh3a3by/ZPkPQ/C +Fp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aXqhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8 +P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcCC52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/ge +oeOy3ZExqysdBP+lSgQ36YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkp +k8zruFvh/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrFYD3Z +fBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93EJNyAKoFBbZQ+yODJ +gUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVcUS4cK38acijnALXRdMbX5J+tB5O2 +UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi8 +1xtZPCvM8hnIk2snYxnP/Okm+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4s +bE6x/c+cCbqiM+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGVcpNxJK1ok1iOMq8bs3AD/CUr +dIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBcHadm47GUBwwyOabqG7B52B2ccETjit3E+ZUf +ijhDPwGFpUenPUayvOUiaPd7nNgsPgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAsl +u1OJD7OAUN5F7kR/q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjq +erQ0cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jra6x+3uxj +MxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90IH37hVZkLId6Tngr75qNJ +vTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/YK9f1JmzJBjSWFupwWRoyeXkLtoh/D1JI +Pb9s2KJELtFOt3JY04kTlf5Eq/jXixtunLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406y +wKBjYZC6VWg3dGq2ktufoYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NI +WuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +SSL.com Root Certification Authority ECC +======================================== +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAMBgNV +BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xMTAv +BgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEy +MTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO +BgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI7Z4INcgn64mMU1jrYor+ +8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPgCemB+vNH06NjMGEwHQYDVR0OBBYEFILR +hXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTT +jgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCW +e+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z +5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +SSL.com EV Root Certification Authority RSA R2 +============================================== +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVTMQ4w +DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9u +MTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MB4XDTE3MDUzMTE4MTQzN1oXDTQyMDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI +DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYD +VQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvqM0fNTPl9fb69LT3w23jh +hqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssufOePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7w +cXHswxzpY6IXFJ3vG2fThVUCAtZJycxa4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTO +Zw+oz12WGQvE43LrrdF9HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+ +B6KjBSYRaZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcAb9Zh +CBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQGp8hLH94t2S42Oim +9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQVPWKchjgGAGYS5Fl2WlPAApiiECto +RHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMOpgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+Slm +JuwgUHfbSguPvuUCYHBBXtSuUDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48 ++qvWBkofZ6aYMBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa49QaAJadz20Zp +qJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBWs47LCp1Jjr+kxJG7ZhcFUZh1 +++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nx +Y/hoLVUE0fKNsKTPvDxeH3jnpaAgcLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2G +guDKBAdRUNf/ktUM79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDz +OFSz/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXtll9ldDz7 +CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEmKf7GUmG6sXP/wwyc5Wxq +lD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKKQbNmC1r7fSOl8hqw/96bg5Qu0T/fkreR +rwU7ZcegbLHNYhLDkBvjJc40vG93drEQw/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1 +hlMYegouCRw2n5H9gooiS9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX +9hwJ1C07mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +SSL.com EV Root Certification Authority ECC +=========================================== +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAMBgNV +BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xNDAy +BgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYw +MjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx +EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NM +LmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMAVIbc/R/fALhBYlzccBYy +3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1KthkuWnBaBu2+8KGwytAJKaNjMGEwHQYDVR0O +BBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe +5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJ +N+vp1RPZytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mm +m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +GlobalSign Root CA - R6 +======================= +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UECxMX +R2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds +b2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9i +YWxTaWduIFJvb3QgQ0EgLSBSNjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFs +U2lnbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQss +grRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1kZguSgMpE +3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxDaNc9PIrFsmbVkJq3MQbF +vuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqM +PKq0pPbzlUoSB239jLKJz9CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+ +azayOeSsJDa38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05O +WgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/hbguy +CLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4nWUx2OVvq+aWh2IMP +0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kN +b7gu3GduyYsRtYQUigAZcIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQE +AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNV +HSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0 +lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrY +BzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFym +Fe944Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr +3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB1 +0jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/T +uTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItK +oZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+t +JDfLRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GC CA +=============================== +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQGEwJD +SDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEo +MCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRa +Fw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQL +ExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4nieUqjFqdr +VCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4Wp2OQ0jnUsYd4XxiWD1Ab +NTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0E +AwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtk +AjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +GTS Root R1 +=========== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG +EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv +b3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG +A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx +9vaMf/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7wCl7r +aKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjwTcLCeoiKu7rPWRnW +r4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0PfyblqAj+lug8aJRT7oM6iCsVlgmy4HqM +LnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly +4cpk9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr +06zqkUspzBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om +3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNu +JLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEM +BQADggIBADiWCu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 +d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6ZXPYfcX3v73sv +fuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZRgyFmxhE+885H7pwoHyXa/6xm +ld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9b +gsiG1eGZbYwE8na6SfZu6W0eX6DvJ4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq +4BjFbkerQUIpm/ZgDdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWEr +tXvM+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyyF62ARPBo +pY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9SQ98POyDGCBDTtWTurQ0 +sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdwsE3PYJ/HQcu51OyLemGhmW/HGY0dVHLql +CFF1pkgl +-----END CERTIFICATE----- + +GTS Root R2 +=========== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG +EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv +b3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG +A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTuk +k3LvCvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo +7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWI +m8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5Gm +dFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbu +ak7MkogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscsz +cTJGr61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW +Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73Vululycsl +aVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy +5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEM +BQADggIBALZp8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT +vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiTz9D2PGcDFWEJ ++YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiApJiS4wGWAqoC7o87xdFtCjMw +c3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvbpxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3Da +WsYDQvTtN6LwG1BUSw7YhN4ZKJmBR64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5r +n/WkhLx3+WuXrD5RRaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56Gtmwfu +Nmsk0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC5AwiWVIQ +7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiFizoHCBy69Y9Vmhh1fuXs +gWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLnyOd/xCxgXS/Dr55FBcOEArf9LAhST4Ld +o/DUhgkC +-----END CERTIFICATE----- + +GTS Root R3 +=========== +-----BEGIN CERTIFICATE----- +MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUU +Rout736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL24Cej +QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTB8Sa6oC2uhYHP +0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFukfCPAlaUs3L6JbyO5o91lAFJekazInXJ0 +glMLfalAvWhgxeG4VDvBNhcl2MG9AjEAnjWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOa +KaqW04MjyaR7YbPMAuhd +-----END CERTIFICATE----- + +GTS Root R4 +=========== +-----BEGIN CERTIFICATE----- +MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa +6zzuhXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvRHYqj +QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSATNbrdP9JNqPV +2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0CMRw3J5QdCHojXohw0+WbhXRIjVhLfoI +N+4Zba3bssx9BzT1YBkstTTZbyACMANxsbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11x +zPKwTdb+mciUqXWi4w== +-----END CERTIFICATE----- + +UCA Global G2 Root +================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBHbG9iYWwgRzIgUm9vdDAeFw0x +NjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0xCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlU +cnVzdDEbMBkGA1UEAwwSVUNBIEdsb2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxeYrb3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmT +oni9kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzmVHqUwCoV +8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/RVogvGjqNO7uCEeBHANBS +h6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDcC/Vkw85DvG1xudLeJ1uK6NjGruFZfc8o +LTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIjtm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/ +R+zvWr9LesGtOxdQXGLYD0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBe +KW4bHAyvj5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6DlNaBa +4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6iIis7nCs+dwp4wwc +OxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznPO6Q0ibd5Ei9Hxeepl2n8pndntd97 +8XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFIHEjMz15DD/pQwIX4wVZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo +5sOASD0Ee/ojL3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl1qnN3e92mI0A +Ds0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oUb3n09tDh05S60FdRvScFDcH9 +yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LVPtateJLbXDzz2K36uGt/xDYotgIVilQsnLAX +c47QN6MUPJiVAAwpBVueSUmxX8fjy88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHo +jhJi6IjMtX9Gl8CbEGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZk +bxqgDMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI+Vg7RE+x +ygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGyYiGqhkCyLmTTX8jjfhFn +RR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bXUB+K+wb1whnw0A== +-----END CERTIFICATE----- + +UCA Extended Validation Root +============================ +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9u +IFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMxMDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8G +A1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrs +iWogD4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvSsPGP2KxF +Rv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aopO2z6+I9tTcg1367r3CTu +eUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dksHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR +59mzLC52LqGj3n5qiAno8geK+LLNEOfic0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH +0mK1lTnj8/FtDw5lhIpjVMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KR +el7sFsLzKuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/TuDv +B0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41Gsx2VYVdWf6/wFlth +WG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs1+lvK9JKBZP8nm9rZ/+I8U6laUpS +NwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQDfwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS +3H5aBZ8eNJr34RQwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL +BQADggIBADaNl8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQVBcZEhrxH9cM +aVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5c6sq1WnIeJEmMX3ixzDx/BR4 +dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb ++7lsq+KePRXBOy5nAliRn+/4Qh8st2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOW +F3sGPjLtx7dCvHaj2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwi +GpWOvpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2CxR9GUeOc +GMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmxcmtpzyKEC2IPrNkZAJSi +djzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbMfjKaiJUINlK73nZfdklJrX+9ZSCyycEr +dhh2n1ax +-----END CERTIFICATE----- + +Certigna Root CA +================ +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UE +BhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAwMiA0ODE0NjMwODEwMDAzNjEZ +MBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0xMzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjda +MFoxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYz +MDgxMDAwMzYxGTAXBgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sOty3tRQgX +stmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9MCiBtnyN6tMbaLOQdLNyz +KNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPuI9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8 +JXrJhFwLrN1CTivngqIkicuQstDuI7pmTLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16 +XdG+RCYyKfHx9WzMfgIhC59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq +4NYKpkDfePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3YzIoej +wpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWTCo/1VTp2lc5ZmIoJ +lXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1kJWumIWmbat10TWuXekG9qxf5kBdI +jzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp/ +/TBt2dzhauH8XwIDAQABo4IBGjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczovL3d3d3cuY2Vy +dGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilodHRwOi8vY3JsLmNlcnRpZ25h +LmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYraHR0cDovL2NybC5kaGlteW90aXMuY29tL2Nl +cnRpZ25hcm9vdGNhLmNybDANBgkqhkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOIt +OoldaDgvUSILSo3L6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxP +TGRGHVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH60BGM+RFq +7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncBlA2c5uk5jR+mUYyZDDl3 +4bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdio2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd +8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS +6Cvu5zHbugRqh5jnxV/vfaci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaY +tlu3zM63Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayhjWZS +aX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw3kAP+HwV96LOPNde +E4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- + +emSign Root CA - G1 +=================== +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJJTjET +MBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl +ZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBHMTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgx +ODMwMDBaMGcxCzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVk +aHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQzf2N4aLTN +LnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO8oG0x5ZOrRkVUkr+PHB1 +cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aqd7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHW +DV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhMtTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ +6DqS0hdW5TUaQBw+jSztOd9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrH +hQIDAQABo0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQDAgEG +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31xPaOfG1vR2vjTnGs2 +vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjMwiI/aTvFthUvozXGaCocV685743Q +NcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6dGNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q ++Mri/Tm3R7nrft8EI6/6nAYH6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeih +U80Bv2noWgbyRQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx +iN66zB+Afko= +-----END CERTIFICATE----- + +emSign ECC Root CA - G3 +======================= +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQGEwJJTjETMBEG +A1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg +MB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4 +MTgzMDAwWjBrMQswCQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11 +ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0WXTsuwYc +58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xySfvalY8L1X44uT6EYGQIr +MgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuBzhccLikenEhjQjAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+D +CBeQyh+KTOgNG3qxrdWBCUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7 +jHvrZQnD+JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE----- + +emSign Root CA - C1 +=================== +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCVVMx +EzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNp +Z24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UE +BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQD +ExNlbVNpZ24gUm9vdCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+up +ufGZBczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZHdPIWoU/ +Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH3DspVpNqs8FqOp099cGX +OFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvHGPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4V +I5b2P/AgNBbeCsbEBEV5f6f9vtKppa+cxSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleooms +lMuoaJuvimUnzYnu3Yy1aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+ +XJGFehiqTbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD +ggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87/kOXSTKZEhVb3xEp +/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4kqNPEjE2NuLe/gDEo2APJ62gsIq1 +NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrGYQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9 +wC68AivTxEDkigcxHpvOJpkT+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQ +BmIMMMAVSKeoWXzhriKi4gp6D/piq1JM4fHfyr6DDUI= +-----END CERTIFICATE----- + +emSign ECC Root CA - C3 +======================= +-----BEGIN CERTIFICATE----- +MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQGEwJVUzETMBEG +A1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMxIDAeBgNVBAMTF2VtU2lnbiBF +Q0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UE +BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQD +ExdlbVNpZ24gRUNDIFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd +6bciMK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4OjavtisIGJAnB9 +SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0OBBYEFPtaSNCAIEDyqOkA +B2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gA +MGUCMQC02C8Cif22TGK6Q04ThHK1rt0c3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwU +ZOR8loMRnLDRWmFLpg9J0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== +-----END CERTIFICATE----- + +Hongkong Post Root CA 3 +======================= +-----BEGIN CERTIFICATE----- +MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQELBQAwbzELMAkG +A1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJSG9uZyBLb25nMRYwFAYDVQQK +Ew1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2 +MDMwMjI5NDZaFw00MjA2MDMwMjI5NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtv +bmcxEjAQBgNVBAcTCUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMX +SG9uZ2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz +iNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFOdem1p+/l6TWZ5Mwc50tf +jTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mIVoBc+L0sPOFMV4i707mV78vH9toxdCim +5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOe +sL4jpNrcyCse2m5FHomY2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj +0mRiikKYvLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+TtbNe/ +JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZbx39ri1UbSsUgYT2u +y1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+l2oBlKN8W4UdKjk60FSh0Tlxnf0h ++bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YKTE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsG +xVd7GYYKecsAyVKvQv83j+GjHno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwID +AQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e +i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEwDQYJKoZIhvcN +AQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG7BJ8dNVI0lkUmcDrudHr9Egw +W62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCkMpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWld +y8joRTnU+kLBEUx3XZL7av9YROXrgZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov ++BS5gLNdTaqX4fnkGMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDc +eqFS3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJmOzj/2ZQw +9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+l6mc1X5VTMbeRRAc6uk7 +nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6cJfTzPV4e0hz5sy229zdcxsshTrD3mUcY +hcErulWuBurQB7Lcq9CClnXO0lD+mefPL5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB +60PZ2Pierc+xYw5F9KBaLJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fq +dBb9HxEGmpv0 +-----END CERTIFICATE----- + +Entrust Root Certification Authority - G4 +========================================= +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAwgb4xCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3Qu +bmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1 +dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eSAtIEc0MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYT +AlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3D +umSXbcr3DbVZwbPLqGgZ2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV +3imz/f3ET+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j5pds +8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAMC1rlLAHGVK/XqsEQ +e9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73TDtTUXm6Hnmo9RR3RXRv06QqsYJn7 +ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNXwbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5X +xNMhIWNlUpEbsZmOeX7m640A2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV +7rtNOzK+mndmnqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8 +dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwlN4y6mACXi0mW +Hv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNjc0kCAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9n +MA0GCSqGSIb3DQEBCwUAA4ICAQAS5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4Q +jbRaZIxowLByQzTSGwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht +7LGrhFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/B7NTeLUK +YvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uIAeV8KEsD+UmDfLJ/fOPt +jqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbwH5Lk6rWS02FREAutp9lfx1/cH6NcjKF+ +m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKW +RGhXxNUzzxkvFMSUHHuk2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjA +JOgc47OlIQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk5F6G ++TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuYn/PIjhs4ViFqUZPT +kcpG2om3PVODLAgfi49T3f+sHw== +-----END CERTIFICATE----- + +Microsoft ECC Root Certificate Authority 2017 +============================================= +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQgRUND +IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4 +MjMxNjA0WjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZRogPZnZH6 +thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYbhGBKia/teQ87zvH2RPUB +eMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIy5lycFIM ++Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlf +Xu5gKcs68tvWMoQZP3zVL8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaR +eNtUjGUBiudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- + +Microsoft RSA Root Certificate Authority 2017 +============================================= +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQg +UlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIw +NzE4MjMwMDIzWjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u +MTYwNAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZNt9GkMml +7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0ZdDMbRnMlfl7rEqUrQ7e +S0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw7 +1VdyvD/IybLeS2v4I2wDwAW9lcfNcztmgGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+ +dkC0zVJhUXAoP8XFWvLJjEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49F +yGcohJUcaDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaGYaRS +MLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6W6IYZVcSn2i51BVr +lMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4KUGsTuqwPN1q3ErWQgR5WrlcihtnJ +0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJ +ClTUFLkqqNfs+avNJVgyeY+QW5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZCLgLNFgVZJ8og +6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OCgMNPOsduET/m4xaRhPtthH80 +dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk ++ONVFT24bcMKpBLBaYVu32TxU5nhSnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex +/2kskZGT4d9Mozd2TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDy +AmH3pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGRxpl/j8nW +ZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiAppGWSZI1b7rCoucL5mxAyE +7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKT +c0QWbej09+CVgI+WXTik9KveCjCHk9hNAHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D +5KbvtwEwXlGjefVwaaZBRA+GsCyRxj3qrg+E +-----END CERTIFICATE----- + +e-Szigno Root CA 2017 +===================== +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNVBAYTAkhVMREw +DwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUt +MjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJvb3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZa +Fw00MjA4MjIxMjA3MDZaMHExCzAJBgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UE +CgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3pp +Z25vIFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtvxie+RJCx +s1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+HWyx7xf58etqjYzBhMA8G +A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSHERUI0arBeAyxr87GyZDv +vzAEwDAfBgNVHSMEGDAWgBSHERUI0arBeAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEA +tVfd14pVCzbhhkT61NlojbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxO +svxyqltZ+efcMQ== +-----END CERTIFICATE----- + +certSIGN Root CA G2 +=================== +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNVBAYTAlJPMRQw +EgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjAeFw0xNzAy +MDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJBgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lH +TiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAMDFdRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05 +N0IwvlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZuIt4Imfk +abBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhpn+Sc8CnTXPnGFiWeI8Mg +wT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKscpc/I1mbySKEwQdPzH/iV8oScLumZfNp +dWO9lfsbl83kqK/20U6o2YpxJM02PbyWxPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91Qqh +ngLjYl/rNUssuHLoPj1PrCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732 +jcZZroiFDsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fxDTvf +95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgyLcsUDFDYg2WD7rlc +z8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6CeWRgKRM+o/1Pcmqr4tTluCRVLERL +iohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1Ud +DgQWBBSCIS1mxteg4BXrzkwJd8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOB +ywaK8SJJ6ejqkX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQlqiCA2ClV9+BB +/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0OJD7uNGzcgbJceaBxXntC6Z5 +8hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+cNywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5 +BiKDUyUM/FHE5r7iOZULJK2v0ZXkltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklW +atKcsWMy5WHgUyIOpwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tU +Sxfj03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZkPuXaTH4M +NMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE1LlSVHJ7liXMvGnjSG4N +0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MXQRBdJ3NghVdJIgc= +-----END CERTIFICATE----- + +Trustwave Global Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV +UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2 +ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0xNzA4MjMxOTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJV +UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2 +ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALldUShLPDeS0YLOvR29 +zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0XznswuvCAAJWX/NKSqIk4cXGIDtiLK0thAf +LdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4Bq +stTnoApTAbqOl5F2brz81Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9o +WN0EACyW80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotPJqX+ +OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1lRtzuzWniTY+HKE40 +Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfwhI0Vcnyh78zyiGG69Gm7DIwLdVcE +uE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm ++9jaJXLE9gCxInm943xZYkqcBW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqj +ifLJS3tBEW1ntwiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1UdDwEB/wQEAwIB +BjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W0OhUKDtkLSGm+J1WE2pIPU/H +PinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfeuyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0H +ZJDmHvUqoai7PF35owgLEQzxPy0QlG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla +4gt5kNdXElE1GYhBaCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5R +vbbEsLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPTMaCm/zjd +zyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qequ5AvzSxnI9O4fKSTx+O +856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxhVicGaeVyQYHTtgGJoC86cnn+OjC/QezH +Yj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu +3R3y4G5OBVixwJAWKqQ9EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP +29FpHOTKyeC2nOnOcXHebD8WpHk= +-----END CERTIFICATE----- + +Trustwave Global ECC P256 Certification Authority +================================================= +-----BEGIN CERTIFICATE----- +MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYDVQQGEwJVUzER +MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy +dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1 +NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH77bOYj +43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoNFWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqm +P62jQzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt +0UrrdaVKEJmzsaGLSvcwCgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjz +RM4q3wghDDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 +-----END CERTIFICATE----- + +Trustwave Global ECC P384 Certification Authority +================================================= +-----BEGIN CERTIFICATE----- +MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYDVQQGEwJVUzER +MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy +dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4 +NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuBBAAiA2IABGvaDXU1CDFH +Ba5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJj9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr +/TklZvFe/oyujUF5nQlgziip04pt89ZF1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNV +HQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNn +ADBkAjA3AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsCMGcl +CrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVuSw== +-----END CERTIFICATE----- diff --git a/ecere/src/com/String.ec b/ecere/src/com/String.ec index 5c2497e114..4d6eefaba5 100644 --- a/ecere/src/com/String.ec +++ b/ecere/src/com/String.ec @@ -739,6 +739,31 @@ public char * ChangeExtension(const char * string, const char * ext, char * outp return output; } +public bool IsPathInsideOf(const char * path, const char * of) +{ + if(!path[0] || !of[0]) + return false; // What to do here? Ever used? + else + { + char ofPart[MAX_FILENAME], ofRest[MAX_LOCATION]; + char pathPart[MAX_FILENAME], pathRest[MAX_LOCATION]; + strcpy(ofRest, of); + strcpy(pathRest, path); + for(; ofRest[0] && pathRest[0];) + { + SplitDirectory(ofRest, ofPart, ofRest); + SplitDirectory(pathRest, pathPart, pathRest); + if(fstrcmp(pathPart, ofPart)) + return false; + } + if(!ofRest[0] && !pathRest[0]) // paths are identical - should return false or true? (changed to false) + return false; + else if(!pathRest[0]) // not inside of, it's the other way around + return false; + return true; + } +} + // --- String Stuff (Temporarily outside String class) --- public void PrintSize(char * string, uint64 size, int prec) { @@ -797,6 +822,11 @@ public void PrintBigSize(char * string, double size, int prec) sprintf(string, "%.0f B", size); } +public bool ishexdigit(char x) +{ + return (isdigit(x) || (x >= 'a' && x<='f') || (x >= 'A' && x <= 'F')); +} + public char * SearchString(const char * buffer, int start, const char * subStr, bool matchCase, bool matchWord) { if(buffer && subStr) @@ -840,7 +870,7 @@ public char * RSearchString(const char * buffer, const char * subStr, int maxLen { int subLen = strlen(subStr); const char * ptr1 = buffer + maxLen - subLen; - const char * ptr2 = buffer + maxLen - subLen - 1; + const char * ptr2 = ptr1 - 1; int (*strcompare)(const char *, const char *, uintsize) = matchCase ? strncmp : strnicmp; for(; ptr1 >=buffer; ptr1--, ptr2--) { @@ -866,6 +896,136 @@ public char * RSearchString(const char * buffer, const char * subStr, int maxLen return null; } +// NOTE: The 'len' is a stop on the SOURCE string, and these functions are missing a stop on the destination string +public int UnescapeCString(char * d, const char * s, int len) +{ + /* + * Remove c escape sequences from a string: + * if non-standard sequences are present, (eg: "\z"), + * the leading backslash is __removed__. + * */ + return _UnescapeCString(d, s, len, false);} + +public int UnescapeCStringLoose(char * d, const char * s, int len) +{ + /* + * Remove c escape sequences from a string: + * if non-standard sequences are present, (eg: "\z"), + * the leading backslash is __kept__. + * */ + return _UnescapeCString(d, s, len, true); +} + +// TOFIX: THIS DOESN'T HANDLE NUMERIC ESCAPE CODES (OCTAL/HEXADECIMAL...)? +static inline int _UnescapeCString(char * d, const char * s, int len, bool keepBS) +{ + /* + * Remove c escape sequences from a string: + * if non-standard sequences are present, (eg: "\z", the leading backslash + * is removed or kept depending to the value of the boolean parameter + * keepBS with obvious meaning. + **/ + int j = 0, k = 0; + char ch; + while(j < len && (ch = s[j])) + { + switch(ch) + { + case '\\': + switch((ch = s[++j])) + { + case 'n': d[k] = '\n'; break; + case 't': d[k] = '\t'; break; + case 'a': d[k] = '\a'; break; + case 'b': d[k] = '\b'; break; + case 'f': d[k] = '\f'; break; + case 'r': d[k] = '\r'; break; + case 'v': d[k] = '\v'; break; + case '\\': d[k] = '\\'; break; + case '\"': d[k] = '\"'; break; + case '\'': d[k] = '\''; break; + default: d[k] = '\\'; d[k+(int)keepBS] = ch; + } + break; + default: + d[k] = ch; + } + j++, k++; + } + d[k] = '\0'; + return k; +} + +public class EscapeCStringOptions : uint32 +{ +public: + bool escapeSingleQuote:1; + bool escapeDoubleQuotes:1; + bool writeQuotes:1; + bool multiLine:1; + int indent:16; +}; + +// TODO: Review... See also copyEscapeString() and strescpy() extras/stringTools.ec, WriteONString() in JSON.ec +public int EscapeCString(String outString, int bufferLen, const String s, EscapeCStringOptions options) +{ + uintsize actualIndent = 3 * options.indent; + int d = 0, c = 0; + const char * string = s; + char ch; + + if(!options) + options.escapeDoubleQuotes = true; // default to double quotes if nothing is specified + + if(options.writeQuotes) + outString[d++] = '\"'; + while(d + 2 < bufferLen) + { + ch = string[c++]; + // TODO: Properly handle all necessary escapes + if(ch == '\"' && options.escapeDoubleQuotes) outString[d++] = '\\', outString[d++] = '\"'; + else if(ch == '\'' && options.escapeSingleQuote) outString[d++] = '\\', outString[d++] = '\''; + else if(ch == '\\') outString[d++] = '\\', outString[d++] = '\\'; + else if(ch == '\t') outString[d++] = '\\', outString[d++] = 't'; + else if(ch == '\b') outString[d++] = '\\', outString[d++] = 'b'; + else if(ch == '\r') outString[d++] = '\\', outString[d++] = 'r'; + else if(ch == '\f') outString[d++] = '\\', outString[d++] = 'f'; + else if(ch == '\n') + { + outString[d++] = '\\', outString[d++] = 'n'; + if(options.multiLine && options.writeQuotes) + { + outString[d++] = '\"'; + outString[d++] = '\n'; + memset(outString+d, ' ', actualIndent); + d += actualIndent; + outString[d++] = '\"'; + } + } + /* + // Special code for JSON writer to add an automatic newline for
as this is how we imported documentor data... + else if(c >= 4 && ch == '>' && string[c-2] == 'r' && string[c-3] == 'b' && string[c-4] == '<' && + options.multiLine && options.writeQuotes) + { + int i; + outString[d++] = '>'; + outString[d++] = '\"', outString[d++] = '\n'; + for(i = 0; i <= options.indent; i++) + outString[d++] = ' ', outString[d++] = ' ', outString[d++] = ' '; + outString[d++] = '\"'; + } + */ + else if(ch) + outString[d++] = ch; + else + break; + } + if(options.writeQuotes) + outString[d++] = '\"'; + outString[d] = 0; + return d; +} + //public define gnuMakeCharsNeedEscaping = "$%"; //public define windowsFileNameCharsNotAllowed = "*/:<>?\\\"|"; //public define linuxFileNameCharsNotAllowed = "/"; @@ -1173,31 +1333,6 @@ public double FloatFromString(const char * string) return neg * res; } -public bool IsPathInsideOf(const char * path, const char * of) -{ - if(!path[0] || !of[0]) - return false; // What to do here? Ever used? - else - { - char ofPart[MAX_FILENAME], ofRest[MAX_LOCATION]; - char pathPart[MAX_FILENAME], pathRest[MAX_LOCATION]; - strcpy(ofRest, of); - strcpy(pathRest, path); - for(; ofRest[0] && pathRest[0];) - { - SplitDirectory(ofRest, ofPart, ofRest); - SplitDirectory(pathRest, pathPart, pathRest); - if(fstrcmp(pathPart, ofPart)) - return false; - } - if(!ofRest[0] && !pathRest[0]) // paths are identical - should return false or true? (changed to false) - return false; - else if(!pathRest[0]) // not inside of, it's the other way around - return false; - return true; - } -} - public enum StringAllocType { pointer, stack, heap }; public class ZString { @@ -1230,7 +1365,7 @@ public: } if(allocType == heap) { - int newSize = newLen ? newLen + 1 : 0; + int newSize = newLen ? newLen + 1 : 1; if(newSize != size) { if(newSize < minSize) newSize = minSize; @@ -1257,7 +1392,7 @@ public: public: - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { return _string; } @@ -1364,15 +1499,16 @@ public: { memcpy(_string + len, s._string, addedLen); len += addedLen; - _string[len] = 0; } + _string[len] = 0; + // WARNING: auto-decref'ing for now when s is of pointer type! if(s.allocType == pointer) delete s; } } - void concatx(typed_object object, ...) + void concatx(const typed_object object, ...) { if(allocType != pointer) { @@ -1387,8 +1523,49 @@ public: } } + void concatn(ZString s, int l) + { + if(s && allocType != pointer) + { + int addedLen = l; + int newLen = len + addedLen; + if(allocType == heap && newLen + 1 > size) + { + int newSize = newLen + 1; + if(newSize > maxSize) + newSize = maxSize; + if(newSize > size) + { + _string = renew _string char[newSize]; + size = newSize; + } + } + if(newLen + 1 > size) + addedLen = size - 1 - len; + if(addedLen > 0) + { + memcpy(_string + len, s._string, addedLen); + len += addedLen; + } + _string[len] = 0; + // WARNING: auto-decref'ing for now when s is of pointer type! + if(s.allocType == pointer) + delete s; + } + } + void copy(ZString s) { copyString(s._string, s.len); } }; + +public char * strchrmax(const char * s, int c, int max) +{ + int i; + char ch; + for(i = 0; i < max && (ch = s[i]); i++) + if(ch == c) + return (char *)s + i; + return null; +} diff --git a/ecere/src/com/containers/Array.ec b/ecere/src/com/containers/Array.ec index b2c8cc88ab..bfe15d511b 100644 --- a/ecere/src/com/containers/Array.ec +++ b/ecere/src/com/containers/Array.ec @@ -17,7 +17,7 @@ extern int __ecereVMethodID_class_OnCompare; defined __NetBSD__ || defined __DragonFly__ || defined AMIGA) #define BSD extern void qsort_r(void *base, uintsize nel, uintsize width, void *arg, int (*compare)(void *, const void *, const void *)); -#elif defined(__WIN32__) +#elif defined(__WIN32__) && defined(__MINGW32__) __declspec(dllimport) __cdecl extern void qsort_s(void *base, uintsize nel, uintsize width, int (*compare)(void *, const void *, const void *), void * arg); #elif (defined __GLIBC__ && ((__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8))) #define GLIBC @@ -185,8 +185,8 @@ public: } // Generic iterator support - IteratorPointer GetFirst() { return (IteratorPointer)array; } - IteratorPointer GetLast() { return (IteratorPointer)(array ? (array + (count - 1)) : null); } + IteratorPointer GetFirst() { return (IteratorPointer)(count ? array : null); } + IteratorPointer GetLast() { return (IteratorPointer)(count && array ? (array + (count - 1)) : null); } IteratorPointer GetPrev(IteratorPointer ip) { T * item = (T *)ip; @@ -276,10 +276,30 @@ public: void Move(IteratorPointer ip, IteratorPointer afterIp) { - /* T * it = (T *)ip; T * after = (T *)afterIp; - */ + uintsize size = class(T).typeSize; + byte * temp = new byte[size]; + memcpy(temp, it, size); + if(!after) + { + memmove(array+1, array, (byte*)it - (byte *)array); + memcpy(array, temp, size); + } + else + { + if(it < after) + { + memmove(it, it + 1, (byte *)after - (byte *)it); + memcpy(after, temp, size); + } + else if(it > after) + { + memmove(after + 2, after + 1, (byte *)it - (byte *)(after + 1)); + memcpy(after + 1, temp, size); + } + } + delete temp; } virtual void RemoveAll() @@ -300,7 +320,7 @@ public: { if(count != value) { - if(value > minAllocSize) + if(!minAllocSize || value > minAllocSize) array = renew0 array T[value]; else if(value > count) memset((byte *)array + count * class(T).typeSize, 0, (value - count) * class(T).typeSize); @@ -337,7 +357,7 @@ public: } } - virtual void Copy(Container source) + virtual void Copy(Container source) // FIXME: Document that this never does a deep copy { count = source.GetCount(); if(count > minAllocSize) diff --git a/ecere/src/com/containers/BuiltInContainer.ec b/ecere/src/com/containers/BuiltInContainer.ec index 85cbe306a8..16cb69a7f8 100644 --- a/ecere/src/com/containers/BuiltInContainer.ec +++ b/ecere/src/com/containers/BuiltInContainer.ec @@ -24,8 +24,8 @@ public: property Container { get { return (void *)this; } } - virtual IteratorPointer GetFirst() { return data; } - virtual IteratorPointer GetLast() { return (IteratorPointer)(data ? ((byte *)data + (count * type.typeSize) - 1) : null); } + virtual IteratorPointer GetFirst() { return count ? data : null; } // TODO: explicitly set 'null' for data for empty containers + virtual IteratorPointer GetLast() { return (IteratorPointer)(count && data ? ((byte *)data + (count * type.typeSize) - 1) : null); } virtual IteratorPointer GetPrev(IteratorPointer pointer) { return (IteratorPointer)((pointer && (byte *)pointer > (byte *)data) ? @@ -50,7 +50,7 @@ public: } virtual IteratorPointer GetAtPosition(const uint64 pos, bool create) { - return data ? (IteratorPointer)((byte *)data + pos * type.typeSize) : null; + return count && data ? (IteratorPointer)((byte *)data + pos * type.typeSize) : null; } virtual IteratorPointer Insert(IteratorPointer after, uint64 value) { @@ -105,7 +105,7 @@ public: virtual void Delete(IteratorPointer it) { } - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { if(this) { diff --git a/ecere/src/com/containers/Container.ec b/ecere/src/com/containers/Container.ec index 6f1b0db421..3a791db048 100644 --- a/ecere/src/com/containers/Container.ec +++ b/ecere/src/com/containers/Container.ec @@ -6,7 +6,7 @@ default: static __attribute__((unused)) void UnusedFunction() { - int a; + int a = 0; a.OnCompare(null); a.OnCopy(null); a.OnGetString(null, null, null); @@ -199,8 +199,23 @@ public: Class Dclass = class(D); bool byRef = (Dclass.type == systemClass && !Dclass.byValueSystemClass) || Dclass.type == bitClass || Dclass.type == enumClass || Dclass.type == unitClass; int (* onCompare)(void *, const void *, const void *) = (void *)Dclass._vTbl[__ecereVMethodID_class_OnCompare]; + bool isInt64 = false; + if(onCompare == (void *)class(int64).OnCompare || + (Dclass.type == unitClass && Dclass.typeSize == sizeof(int64) && !strcmp(Dclass.name, "Id"))) + { + onCompare = (void *)uint64::OnCompare; + isInt64 = true; + } - if(byRef) + if(isInt64) + { + for(i = GetFirst(); i; i = GetNext(i)) + { + D data = GetData(i); + if((uint64)value == (uint64)data) return i; + } + } + else if(byRef) { for(i = GetFirst(); i; i = GetNext(i)) { @@ -247,7 +262,7 @@ public: Remove(i); } - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { if(this) { @@ -277,10 +292,15 @@ public: } // TODO: Warn against the danger of using TakeOut with 'normal' classes, as they will be considered equivalent if onCompare says so - void TakeOut(const D d) + bool TakeOut(const D d) { IteratorPointer i = Find(d); - if(i) Remove(i); + if(i) + { + Remove(i); + return true; + } + return false; } ~Container() diff --git a/ecere/src/com/containers/CustomAVLTree.ec b/ecere/src/com/containers/CustomAVLTree.ec index 42c3f0e9d6..0fc725c1d6 100644 --- a/ecere/src/com/containers/CustomAVLTree.ec +++ b/ecere/src/com/containers/CustomAVLTree.ec @@ -157,6 +157,8 @@ private: public thisclass Find(Class Tclass, const T key) { + return FindEx(Tclass, key, null, null); + /* byte * a; bool reference = false; uint offset = 0; @@ -185,6 +187,7 @@ private: break; } return this; + */ } thisclass FindEx(Class Tclass, const T key, AVLNode */*thisclass **/ addTo, AddSide * addSide) @@ -195,8 +198,13 @@ private: ClassType t = Tclass.type; int (* onCompare)(void *, void *, void *) = (void *)Tclass._vTbl[__ecereVMethodID_class_OnCompare]; bool isInt64 = false, isDouble = false; + AVLNode to = null; + AddSide side = 0; + + // NOTE: Currently using int64 for uint64 may result in wrong order... if(onCompare == (void *)class(int64).OnCompare || - (Tclass.type == unitClass && Tclass.typeSize == sizeof(int64) && !strcmp(Tclass.name, "Id"))) isInt64 = true; + (Tclass.type == unitClass && Tclass.typeSize == sizeof(int64) && !strcmp(Tclass.name, "Id")) || + (Tclass.type == bitClass && Tclass.typeSize == sizeof(int64))) isInt64 = true; else if(onCompare == (void *)class(double).OnCompare) isDouble = true; reference = (t == systemClass && !Tclass.byValueSystemClass) || t == bitClass || t == enumClass || t == unitClass; @@ -208,35 +216,94 @@ private: offset = __ENDIAN_PAD(sizeof(void *)); } - if(Tclass == class(uint)) + if(Tclass == class(int)) + { + int ia = *(int *)a; + if(reference) + { + while(this) + { + byte * b = (((byte *)&this.key) + __ENDIAN_PAD(sizeof(int))); + int ib = *(int *)b; + int result = ia > ib ? 1 : ia < ib ? -1 : 0; + if(result) + { + thisclass node = result < 0 ? left : right; + if(!node) + to = this, side = (AddSide)result; + this = node; + } + else + break; + } + } + else + { + while(this) + { + int ib = *(int *)((byte *)(uintptr)this.key); + int result = ia > ib ? 1 : ia < ib ? -1 : 0; + if(result) + { + thisclass node = result < 0 ? left : right; + if(!node) + to = this, side = (AddSide)result; + this = node; + } + else + break; + } + } + } + else if(Tclass == class(uint)) { uint ia = *(uint *)a; - while(this) + if(reference) { - uint ib = *(uint *)(reference ? ((byte *)&this.key) + offset : (byte *)(uintptr)this.key); - int result = ia > ib ? 1 : ia < ib ? -1 : 0; - if(result) + while(this) { - thisclass node = result < 0 ? left : right; - if(!node) + byte * b = (((byte *)&this.key) + __ENDIAN_PAD(sizeof(uint))); + uint ib = *(uint *)b; + int result = ia > ib ? 1 : ia < ib ? -1 : 0; + if(result) { - if(addTo) *addTo = this; - if(addSide) *addSide = (AddSide)result; + thisclass node = result < 0 ? left : right; + if(!node) + to = this, side = (AddSide)result; + this = node; } - this = node; + else + break; + } + } + else + { + while(this) + { + uint ib = *(uint *)((byte *)(uintptr)this.key); + int result = ia > ib ? 1 : ia < ib ? -1 : 0; + if(result) + { + thisclass node = result < 0 ? left : right; + if(!node) + to = this, side = (AddSide)result; + this = node; + } + else + break; } - else - break; } } else { + int result; int64 a64; double aDouble; if(isInt64) a64 = *(int64 *)a; else if(isDouble) aDouble = *(double *)a; + /* while(this) { byte * b = reference ? ((byte *)&this.key) + offset : (byte *)(uintptr)this.key; @@ -270,7 +337,127 @@ private: else break; } + */ + if(reference) + { + if(isInt64) + { + while(this) + { + int64 b64 = *(int64 *)&this.key; + if(a64 > b64) result = 1; + else if(a64 < b64) result = -1; + else result = 0; + if(result) + { + thisclass node = result < 0 ? left : right; + if(!node) + to = this, side = (AddSide)result; + this = node; + } + else + break; + } + } + else if(isDouble) + { + while(this) + { + const byte * b = (byte *)&this.key; + double bDouble = *(double *)(byte *)b; + if(aDouble > bDouble) result = 1; + else if(aDouble < bDouble) result = -1; + else result = 0; + if(result) + { + thisclass node = result < 0 ? left : right; + if(!node) + to = this, side = (AddSide)result; + this = node; + } + else + break; + } + } + else + { + while(this) + { + const byte * b = ((byte *)&this.key) + offset; + result = onCompare(Tclass, a, (byte *)b); + if(result) + { + thisclass node = result < 0 ? left : right; + if(!node) + to = this, side = (AddSide)result; + this = node; + } + else + break; + } + } + } + else + { + if(isInt64) + { + while(this) + { + int64 b64 = *(int64 *)(uintptr)this.key; + if(a64 > b64) result = 1; + else if(a64 < b64) result = -1; + else result = 0; + if(result) + { + thisclass node = result < 0 ? left : right; + if(!node) + to = this, side = (AddSide)result; + this = node; + } + else + break; + } + } + else if(isDouble) + { + while(this) + { + double bDouble = *(double *)(uintptr)this.key; + if(aDouble > bDouble) result = 1; + else if(aDouble < bDouble) result = -1; + else result = 0; + if(result) + { + thisclass node = result < 0 ? left : right; + if(!node) + to = this, side = (AddSide)result; + this = node; + } + else + break; + } + } + else + { + while(this) + { + const byte * b = (byte *)(uintptr)this.key; + result = onCompare(Tclass, a, (byte *)b); + if(result) + { + thisclass node = result < 0 ? left : right; + if(!node) + to = this, side = (AddSide)result; + this = node; + } + else + break; + } + } + } } + if(addTo) *addTo = to; + if(addSide) *addSide = side; return this; } @@ -506,6 +693,93 @@ private: right.SingleRotateRight(); SingleRotateLeft(); } + + bool Check(Class Tclass) + { + bool valid = true; + uint offset = 0; + bool reference = false; + byte * b; + int (* onCompare)(void *, void *, void *); + ClassType t; + int leftHeight = left ? left.depthProp+1 : 0; + int rightHeight = right ? right.depthProp+1 : 0; + + // Now get the height of each subtree + + // Verify that AVL tree property is satisfied + int diffHeight = rightHeight - leftHeight; + + if(!Tclass) + Tclass = class(uint64); + t = Tclass.type; + onCompare = (void *)Tclass._vTbl[__ecereVMethodID_class_OnCompare]; + if((t == systemClass && !Tclass.byValueSystemClass) || t == bitClass || t == enumClass || t == unitClass || t == structClass) + { + reference = true; + offset = __ENDIAN_PAD((t == structClass) ? sizeof(void *) : Tclass.typeSize); + } + + if(left) + { + if(left.parent != this) + { + printf("Parent not set properly at node %d\n", (int)left.key); + valid = false; + } + valid *= left.Check(Tclass); + } + if(right) + { + if(right.parent != this) + { + printf("Parent not set properly at node %d\n", (int)right.key); + valid = false; + } + valid *= right.Check(Tclass); + } + + if(depth != depthProp) + { + printf("Depth value at node %d (%d) doesn't match depth property (%d)\n", (int)key, depth, depthProp); + valid = false; + } + + if (diffHeight < -1 || diffHeight > 1) + { + valid = false; + printf("Height difference is %d at node %d\n", diffHeight, (int)key); + } + + // Verify that balance-factor is correct + if (diffHeight != balanceFactor) + { + valid = false; + printf("Height difference %d doesn't match balance-factor of %d at node %d\n", diffHeight, balanceFactor, (int)key); + } + + // Verify that search-tree property is satisfied + b = reference ? ((byte *)&this.key + offset) : ((byte *)(uintptr)this.key); + if(left) + { + byte * a = reference ? ((byte *)&left.key + offset) : ((byte *)(uintptr)left.key); + if(onCompare(Tclass, a, b) > 0) + { + valid = false; + printf("Node %d is *smaller* than left subtree %d\n", (int)key, (int)left.key); + } + } + if (right) + { + byte * a = reference ? ((byte *)&right.key + offset) : ((byte *)(uintptr)right.key); + if(onCompare(Tclass, a, b) < 0) + { + valid = false; + printf("Node %d is *greater* than right subtree %d\n", (int)key, (int)right.key); + } + } + return valid; + } } public class CustomAVLTree, class KT = uint64> : Container @@ -650,7 +924,7 @@ public: if(class(KT).type == structClass) { uint size = sizeof(class AVLNode); - if(class(KT).type == structClass) size += class(KT).typeSize - sizeof(node.AVLNode::key); + if(class(KT).type == structClass) size += class(KT).typeSize - sizeof(node./*AVLNode::*/key); node = (AVLNode)new0 byte[size]; } else @@ -667,4 +941,9 @@ public: } return node; } + + bool Check() + { + return root ? root.Check(class(KT)) : true; + } } diff --git a/ecere/src/com/containers/HashMap.ec b/ecere/src/com/containers/HashMap.ec new file mode 100644 index 0000000000..e08a6cf062 --- /dev/null +++ b/ecere/src/com/containers/HashMap.ec @@ -0,0 +1,243 @@ +#undef __BLOCKS__ + +namespace com; + +import "Container" + +#include +#include + +#include "cpuconfig.h" +#include "cc.h" +#include "mmhash.h" + +static define HASH_PAGE_SHIFT = 7; + +// TODO: Make this generic -- require context for callbacks? +struct HashMapEntry +{ + int64 key; + uintptr value; +}; + +#define NULL_KEY ((int64)0xFFFFFFFFFFFFFFFFLL) + +static void hashClearEntry(HashMapEntry entry) +{ + entry.key = NULL_KEY; + entry.value = 0; +} + +static void hashClearEntries(HashMapEntry * entries, uint numEntries) +{ + int i; + for(i = 0; i < numEntries; i++) + { + entries->key = NULL_KEY; + entries->value = 0; + entries++; + } +} + +static int hashEntryValid(const HashMapEntry entry) +{ + return entry.key != NULL_KEY; +} + +// TOCHECK: we're hashing to 32-bit but storing 64 bit key, is it to avoid conflict with the -1 NULL key? +static uint32_t hashEntryKey(const HashMapEntry entry) +{ + uint32 hashkey; +#if CPUCONF_WORD_SIZE == 64 + uint64 *v = (uint64 *)&entry.key; + hashkey = ccHash32Int64Inline(*v); +#else + hashkey = ccHash32Data4Inline((void *)&entry.key); +#endif + return hashkey; +} + +static int hashEntryCmp(const HashMapEntry entry, const HashMapEntry entryRef) +{ + if(entry.key == NULL_KEY) return MM_HASH_ENTRYCMP_INVALID; + if(entry.key == entryRef.key) return MM_HASH_ENTRYCMP_FOUND; + return MM_HASH_ENTRYCMP_SKIP; +} + +static const mmHashAccess hashAccess = +{ + hashClearEntry, + hashEntryValid, + hashEntryKey, + hashEntryCmp, + null, // entrylist + hashClearEntries +}; + +public struct HashMapIterator : Iterator +{ + property HashMap map + { + set { container = (Container)value; } + get { return (HashMap)container; } + } + property const KT key + { + get { return ((HashMap)container).GetKey(pointer); } + } + property VT value + { + get { return container.GetData(pointer); } + set { container.SetData(pointer, value); } + } +}; + +public class HashMap : Container +{ + void *tbl; + public bool noRemResize; //noRemResize = 1; + + KT GetKey(IteratorPointer pointer) + { + return (KT)(pointer ? ((HashMapEntry*)pointer)->key : 0); + } + + VT GetData(IteratorPointer pointer) + { + return (VT)(pointer ? ((HashMapEntry*)pointer)->value : 0); + } + + bool SetData(IteratorPointer pointer, VT data) + { + if(pointer) + { + ((HashMapEntry *)pointer)->value = (uintptr)data; + return true; + } + return false; + } + + void Remove(IteratorPointer it) + { + mmHashDirectDeleteEntry2(tbl, &hashAccess, it, 0); + if(!noRemResize) + resizeEx(null, false); + } + + void Delete(IteratorPointer it) + { + VT d = GetData(it); + delete d; + mmHashDirectDeleteEntry2(tbl, &hashAccess, it, 0); + if(!noRemResize) + resizeEx(null, false); + } + + public void removeIterating(IteratorPointer * it) + { + HashMapEntry * entry; + mmHashDirectDeleteEntry2(tbl, &hashAccess, *it, noRemResize); + if(!noRemResize) + resizeEx(it, false); + + entry = (HashMapEntry *)*it; + if(entry->key == NULL_KEY) + *it = GetNext(*it); + } + + private static inline void resizeEx(IteratorPointer * movedEntry, bool forceResize) + { + int bits, status = tbl ? mmHashGetStatus(tbl, &bits) : MM_HASH_STATUS_NORMAL; + if(status == MM_HASH_STATUS_MUSTGROW) bits++; + else if(status == MM_HASH_STATUS_MUSTSHRINK && bits > 12) bits--; + else if(!forceResize || !noRemResize || !tbl) return; // Must re-pack if we were not doing mem resize + { + uint pageShift = 4; + uintsize memSize = mmHashRequiredSize(sizeof(HashMapEntry), bits, pageShift); + void *newTbl = malloc(memSize); + mmHashResize2(newTbl, tbl, &hashAccess, bits, pageShift, movedEntry); + free(tbl); + tbl = newTbl; + } + } + + public void resize(IteratorPointer * movedEntry) + { + resizeEx(movedEntry, true); + } + + IteratorPointer GetFirst() { return tbl ? mmHashGetNext(tbl, null, &hashAccess) : null; } + IteratorPointer GetLast() { return tbl ? mmHashGetPrev(tbl, null, &hashAccess) : null; } + IteratorPointer GetPrev(IteratorPointer pointer) { return mmHashGetPrev(tbl, pointer, &hashAccess); } + IteratorPointer GetNext(IteratorPointer pointer) { return mmHashGetNext(tbl, pointer, &hashAccess); } + + IteratorPointer GetAtPosition(int64 pos, bool create, bool * justAdded) + { + if(create) + { + HashMapEntry * entry = null; + HashMapEntry newEntry { pos, 0 }; + int r; + + if(!tbl) + { + int bits = 8; + uintsize size = mmHashRequiredSize(sizeof(HashMapEntry), bits, bits-1); + tbl = malloc(size); + mmHashInit(tbl, &hashAccess, sizeof(HashMapEntry), bits, bits-1, 0); + } + r = mmHashDirectAddEntry2(tbl, &hashAccess, &newEntry, bool::true, &entry); + if(r != MM_HASH_FOUND) + { + resizeEx((IteratorPointer *)&entry, false); + if(justAdded) *justAdded = true; + } + return (IteratorPointer)entry; + } + return tbl ? mmHashDirectFindEntry(tbl, &hashAccess, &pos) : null; + } + + void Free() + { + IteratorPointer it = GetFirst(); + eSystem_LockMem(); + while(it) + { + VT d = GetData(it); + delete d; + it = GetNext(it); + } + eSystem_UnlockMem(); + if(tbl) + mmHashReset(tbl, &hashAccess); + } + + void RemoveAll() + { + if(tbl) + mmHashReset(tbl, &hashAccess); + } + + ~HashMap() + { + if(tbl) free(tbl); + tbl = null; + } + + public property int count + { + get { return tbl ? mmHashGetCount(tbl) : 0; } + } + public property int initSize + { + set + { + int bits = Max(8, log2i(value)); // size == 1, 1 bit causes crashes... + uintsize s = mmHashRequiredSize(sizeof(HashMapEntry), bits, HASH_PAGE_SHIFT); + if(tbl) free(tbl); + tbl = malloc(s); + if(tbl) + mmHashInit(tbl, &hashAccess, sizeof(HashMapEntry), bits, HASH_PAGE_SHIFT, 0); + } + } +} diff --git a/ecere/src/com/containers/HashTable.ec b/ecere/src/com/containers/HashTable.ec new file mode 100644 index 0000000000..3d8068a42c --- /dev/null +++ b/ecere/src/com/containers/HashTable.ec @@ -0,0 +1,163 @@ +#undef __BLOCKS__ + +namespace com; + +import "Container" + +#include +#include + +#include "cpuconfig.h" +#include "cc.h" +#include "mmhash.h" + +static define HASH_PAGE_SHIFT = 7; +/* +static void hashClearEntry(int64 *entry) +{ + *entry = 0; +} +*/ +static int hashEntryValid(const int64 *entry) +{ + return *entry != 0; +} + +static uint32_t hashEntryKey(const int64 *entry) +{ + uint32 hashkey; +#if CPUCONF_WORD_SIZE == 64 + uint64 *v = (uint64 *)entry; + hashkey = ccHash32Int64Inline(*v); +#else + hashkey = ccHash32Data4Inline((void *)entry); +#endif + return hashkey; +} + +static int hashEntryCmp(const int64 *entry, const int64 *entryRef) +{ + if(!*entry) return MM_HASH_ENTRYCMP_INVALID; + if(*entry == *entryRef) return MM_HASH_ENTRYCMP_FOUND; + return MM_HASH_ENTRYCMP_SKIP; +} +/* +static int hashEntryList( void *opaque, const int64 *entry, const int64 *entryRef ) +{ + return MM_HASH_ENTRYLIST_CONTINUE; + // return MM_HASH_ENTRYLIST_BREAK; +} +*/ +static const mmHashAccess hashAccess = +{ + null, //hashClearEntry, + hashEntryValid, + hashEntryKey, + hashEntryCmp +}; + +public class HashTable : Container +{ + void *tbl; + + HashTable() + { + int bits = 8; + uintsize size = mmHashRequiredSize( sizeof(int64), bits, HASH_PAGE_SHIFT); + tbl = malloc(size); + mmHashInit(tbl, &hashAccess, sizeof(int64), bits, HASH_PAGE_SHIFT, 0 /*MM_HASH_FLAGS_NO_COUNT*/); + } + + IteratorPointer Add(T value) + { + int64 * entry; + bool success = mmHashDirectAddEntry2(tbl, &hashAccess, &value, bool::true, &entry) == MM_HASH_SUCCESS; + if(success) + resize(); + return success ? (IteratorPointer)entry : null; + } + + KT GetData(IteratorPointer pointer) + { + return (KT)(pointer ? *(int64*)pointer : 0); + } + + IteratorPointer Find(D value) + { + return mmHashDirectFindEntry(tbl, &hashAccess, &value); + } + + bool SetData(IteratorPointer pointer, int64 data) + { + + return false; // Not supported for now... + } + + void Remove(IteratorPointer it) + { + mmHashDirectDeleteEntry2(tbl, &hashAccess, it, 0); + resize(); + } + + void Delete(IteratorPointer it) + { + mmHashDirectDeleteEntry2(tbl, &hashAccess, it, 0); + resize(); + } + + static void resize() + { + int bits, status = mmHashGetStatus(tbl, &bits); + if(status == MM_HASH_STATUS_MUSTGROW) bits++; + else if(status == MM_HASH_STATUS_MUSTSHRINK && bits > 12) bits--; + else return; + { + uint pageShift = 4; + uintsize memSize = mmHashRequiredSize(sizeof(int64), bits, pageShift); + void *newTbl = malloc(memSize); + mmHashResize(newTbl, tbl, &hashAccess, bits, pageShift); + free(tbl); + tbl = newTbl; + } + } + + IteratorPointer GetFirst() { return mmHashGetNext(tbl, null, &hashAccess); } + IteratorPointer GetLast() { return mmHashGetPrev(tbl, null, &hashAccess); } + IteratorPointer GetPrev(IteratorPointer pointer) { return mmHashGetPrev(tbl, pointer, &hashAccess); } + IteratorPointer GetNext(IteratorPointer pointer) { return mmHashGetNext(tbl, pointer, &hashAccess); } + + IteratorPointer GetAtPosition(int64 pos, bool create, bool * justAdded) + { + if(create) + { + void * entry = null; + int r = mmHashDirectAddEntry2(tbl, &hashAccess, &pos, bool::true, &entry); + if(r != MM_HASH_FOUND) + { + resize(); + if(justAdded) *justAdded = true; + } + return entry; + } + else + return mmHashDirectFindEntry(tbl, &hashAccess, &pos); + } + + void Free() + { + if(tbl) + mmHashReset(tbl, &hashAccess); + } + + void RemoveAll() + { + if(tbl) + mmHashReset(tbl, &hashAccess); + } + + ~HashTable() + { + free(tbl); + tbl = null; + } +} diff --git a/ecere/src/com/containers/List.ec b/ecere/src/com/containers/List.ec index edd898c280..d5686d59e7 100644 --- a/ecere/src/com/containers/List.ec +++ b/ecere/src/com/containers/List.ec @@ -69,6 +69,22 @@ public class List : LinkList void Free() { + Link item = *&first; + Class lltClass = class(LLT); + bool byAddress = lltClass && lltClass.type == structClass; + while(item) + { + Link next = item.next; + D data = byAddress ? (LLT)&item.data : (LLT)item.data; + delete data; + delete item; + item = next; + } + *&first = null; + *&last = null; + count = 0; + + /* LT item; while((item = first)) { @@ -76,6 +92,7 @@ public class List : LinkList delete data; Remove(item); } + */ } Link Find(const LLT value) diff --git a/ecere/src/com/containers/Map.ec b/ecere/src/com/containers/Map.ec index cacecc2341..ab9fdd9884 100644 --- a/ecere/src/com/containers/Map.ec +++ b/ecere/src/com/containers/Map.ec @@ -42,7 +42,7 @@ public: public struct MapIterator : Iterator { - property Map map + property Map map { set { container = (Container)value; } get { return (Map)container; } @@ -83,14 +83,17 @@ public class Map : CustomAVLTree, I = MT, D = bool SetData(MapNode node, V value) { - // Adjust node pointer for non-standard AVLNode - if(class(MT).type == structClass) - node = (MapNode)(((byte *) node) + class(MT).structSize - sizeof(node.AVLNode::key)); + if(node) + { + // Adjust node pointer for non-standard AVLNode + if(class(MT).type == structClass) + node = (MapNode)(((byte *) node) + class(MT).structSize - sizeof(node.AVLNode::key)); - if(class(V).type == structClass) - memcpy((void *)&node.value, (void *)value, class(V).structSize); - else - node.value = value; + if(class(V).type == structClass) + memcpy((void *)&node.value, (void *)value, class(V).structSize); + else + node.value = value; + } return true; } @@ -99,12 +102,17 @@ public class Map : CustomAVLTree, I = MT, D = MapNode newNode = (MapNode) _newNode; if(class(MT).type == structClass || class(V).type == structClass) { + // REVIEW: This assumes that the added node is temporary storage when either they key or value is a struct? + // The "added" node is not actually the node added to the map in this case. MapNode realNode = (MapNode)GetAtPosition(newNode.key, true, null); - if(class(V).type == structClass) - SetData(realNode, (V)&newNode.value); - else - SetData(realNode, newNode.value); - return newNode; + if(realNode) + { + if(class(V).type == structClass) + SetData(realNode, (V)&newNode.value); + else + SetData(realNode, newNode.value); + } + return realNode; } else { @@ -182,6 +190,7 @@ public class Map : CustomAVLTree, I = MT, D = void Free() { MapNode node = root; + eSystem_LockMem(); while(node) { if(node.left) @@ -207,6 +216,7 @@ public class Map : CustomAVLTree, I = MT, D = node = parent; } } + eSystem_UnlockMem(); root = null; count = 0; } diff --git a/ecere/src/com/dataTypes.ec b/ecere/src/com/dataTypes.ec index e134f34393..bbe5362418 100644 --- a/ecere/src/com/dataTypes.ec +++ b/ecere/src/com/dataTypes.ec @@ -5,6 +5,7 @@ namespace com; #endif import "instance" +import "Container" default extern Platform runtimePlatform; @@ -56,7 +57,7 @@ public define FORMAT64U = (__runtimePlatform == win32) ? "%I64u" : "%llu"; __attribute__((unused)) static void UnusedFunction() { - int a; + int a = 0; a.OnGetString(0,0,0); a.OnFree(); a.OnCopy(0); @@ -193,7 +194,7 @@ public: } }; -/*static */const char * Enum_OnGetString(Class _class, void * data, char * tempString, void * fieldData, bool * needClass) +/*static */const char * Enum_OnGetString(Class _class, void * data, char * tempString, void * fieldData, ObjectNotationType * onType) { NamedLink64 item = null; Class b; @@ -225,7 +226,7 @@ public: if(tempString) { strcpy(tempString, item.name); - if(!needClass || !*needClass) + if(!onType || *onType != econ) tempString[0] = (char)toupper(tempString[0]); return tempString; } @@ -281,7 +282,15 @@ static bool Enum_OnGetDataFromString(Class _class, void * data, const char * str return true; } else - return Int64_OnGetDataFromString(_class, data, string); + { + switch(_class.typeSize) + { + case 1: return Byte_OnGetDataFromString(_class, data, string); + case 2: return Int16_OnGetDataFromString(_class, data, string); + case 4: return Integer_OnGetDataFromString(_class, data, string); + case 8: return Int64_OnGetDataFromString(_class, data, string); + } + } return false; } @@ -410,6 +419,11 @@ static int OnCompare(Class _class, void * data1, void * data2) value1.f = ((float(*)(void *))(void *)prop.Get)(data1); value2.f = ((float(*)(void *))(void *)prop.Get)(data2); } + else if(!strcmp(memberType.dataTypeString, "double")) + { + value1.d = ((double(*)(void *))(void *)prop.Get)(data1); + value2.d = ((double(*)(void *))(void *)prop.Get)(data2); + } else { value1.i = ((int(*)(void*))(void *)prop.Get)(data1); @@ -509,22 +523,25 @@ static int OnCompare(Class _class, void * data1, void * data2) return 0; } -static const char * OnGetString(Class _class, void * data, char * tempString, void * fieldData, bool * needClass) +public enum ObjectNotationType : bool { none = 0, econ = 1, json = 2 }; + +static const char * OnGetString(Class _class, void * data, char * tempString, void * fieldData /* ObjectNotationType * notation */, ObjectNotationType * _onType) { + ObjectNotationType onType = _onType ? *_onType : none; // WHY DOES _class.module NOT SEEM TO WORK? Module module = _class.templateClass ? _class.templateClass.module : _class.module; if(_class.type == normalClass && _class.base && !_class.base.base) { if(sizeof(uintsize) == 8) - return UInt64Hex_OnGetString(_class, (void *)&data, tempString, fieldData, needClass); + return UInt64Hex_OnGetString(_class, (void *)&data, tempString, fieldData, _onType); else - return UIntegerHex_OnGetString(_class, (void *)&data, tempString, fieldData, needClass); + return UIntegerHex_OnGetString(_class, (void *)&data, tempString, fieldData, _onType); } else if(_class.type == enumClass) { - return Enum_OnGetString(_class, data, tempString, fieldData, needClass); + return Enum_OnGetString(_class, data, tempString, fieldData, _onType); } - else if(_class.type == unitClass) + else if(_class.type == unitClass || _class.type == systemClass) { Class dataType; Property prop; @@ -554,22 +571,22 @@ static const char * OnGetString(Class _class, void * data, char * tempString, vo if(!strcmp(dts, "double")) { double d = ((double(*)(double))(void *)prop.Set)(*(double *)data); - return ((const char *(*)(void *, void *, char *, void *, bool *))(void *)class(double)._vTbl[__ecereVMethodID_class_OnGetString])(class(double), &d, tempString, fieldData, needClass); + return ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)class(double)._vTbl[__ecereVMethodID_class_OnGetString])(class(double), &d, tempString, fieldData, _onType); } else if(!strcmp(dts, "float")) { float d = ((float(*)(float))(void *)prop.Set)(*(float *)data); - return ((const char *(*)(void *, void *, char *, void *, bool *))(void *)class(float)._vTbl[__ecereVMethodID_class_OnGetString])(class(float), &d, tempString, fieldData, needClass); + return ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)class(float)._vTbl[__ecereVMethodID_class_OnGetString])(class(float), &d, tempString, fieldData, _onType); } else if(!strcmp(dts, "int")) { int d = ((int(*)(int))(void *)prop.Set)(*(int *)data); - return ((const char *(*)(void *, void *, char *, void *, bool *))(void *)class(int)._vTbl[__ecereVMethodID_class_OnGetString])(class(int), &d, tempString, fieldData, needClass); + return ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)class(int)._vTbl[__ecereVMethodID_class_OnGetString])(class(int), &d, tempString, fieldData, _onType); } else if(!strcmp(dts, "int64")) { int64 d = ((int64(*)(int64))(void *)prop.Set)(*(int64 *)data); - return ((const char *(*)(void *, void *, char *, void *, bool *))(void *)class(int64)._vTbl[__ecereVMethodID_class_OnGetString])(class(int64), &d, tempString, fieldData, needClass); + return ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)class(int64)._vTbl[__ecereVMethodID_class_OnGetString])(class(int64), &d, tempString, fieldData, _onType); } } else @@ -577,7 +594,7 @@ static const char * OnGetString(Class _class, void * data, char * tempString, vo } } dataType = eSystem_FindClass(module, _class.dataTypeString); - return ((const char *(*)(void *, void *, char *, void *, bool *))(void *)dataType._vTbl[__ecereVMethodID_class_OnGetString])(dataType, data, tempString, fieldData, needClass); + return ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)dataType._vTbl[__ecereVMethodID_class_OnGetString])(dataType, data, tempString, fieldData, _onType); } else { @@ -601,7 +618,7 @@ static const char * OnGetString(Class _class, void * data, char * tempString, vo char memberString[1024]; Class memberType; const char * name; - const char *(* onGetString)(void *, void *, char *, void *, bool *); + const char *(* onGetString)(void *, void *, char *, void *, ObjectNotationType *); if(m.id < 0) continue; // TODO: Full union & struct member support @@ -618,6 +635,8 @@ static const char * OnGetString(Class _class, void * data, char * tempString, vo memberType = m.dataTypeClass = eSystem_FindClass(module, "int"); onGetString = memberType._vTbl[__ecereVMethodID_class_OnGetString]; + if(!onGetString) + onGetString = OnGetString; // REVIEW: How does an enum class not have OnGetString set? if(m.isProperty) { @@ -633,8 +652,8 @@ static const char * OnGetString(Class _class, void * data, char * tempString, vo value.f = ((float(*)(void *))(void *)prop.Get)(data); if(value.f) { - bool needClass = true; - const char * result = onGetString(memberType, &value, memberString, null, &needClass); + ObjectNotationType _onType = onType; + const char * result = onGetString(memberType, &value, memberString, null, &_onType); if(result && result != memberString) strcpy(memberString, result); // TESTING THIS HERE @@ -647,9 +666,9 @@ static const char * OnGetString(Class _class, void * data, char * tempString, vo value.p = ((void *(*)(void *))(void *)prop.Get)(data); if(value.p || prop.IsSet) { - bool needClass = true; + ObjectNotationType onType = econ; const char * result = onGetString(memberType, - (memberType.type == normalClass) ? value.p : &value, memberString, null, &needClass); + (memberType.type == normalClass) ? value.p : &value, memberString, null, &onType); if(result && result != memberString) strcpy(memberString, result); } @@ -659,8 +678,8 @@ static const char * OnGetString(Class _class, void * data, char * tempString, vo value.i = ((int(*)(void *))(void *)prop.Get)(data); if(value.i || prop.IsSet) { - bool needClass = true; - const char * result = onGetString(memberType, &value, memberString, null, &needClass); + ObjectNotationType onType = econ; + const char * result = onGetString(memberType, &value, memberString, null, &onType); if(result && result != memberString) strcpy(memberString, result); } @@ -715,13 +734,13 @@ static const char * OnGetString(Class _class, void * data, char * tempString, vo break; if(c < typeSize) { - bool needClass = true; + ObjectNotationType onType = econ; const char * result; if(memberType.type == normalClass) - result = onGetString(memberType, *(Instance *)memberData, internalMemberString, null, &needClass); + result = onGetString(memberType, *(Instance *)memberData, internalMemberString, null, &onType); else - result = onGetString(memberType, memberData, internalMemberString, null, &needClass); - if(needClass && strcmp(memberType.dataTypeString, "char *")) + result = onGetString(memberType, memberData, internalMemberString, null, &onType); + if(onType && strcmp(memberType.dataTypeString, "char *")) { //strcpy(memberString, memberType.name); strcat(memberString, "{ "); @@ -750,15 +769,17 @@ static const char * OnGetString(Class _class, void * data, char * tempString, vo value.ui64 = (value.ui64 & bitMember.mask) >> bitMember.pos; if(value.ui64 && (memberType != _class)) // Avoid infinite recursion on bit classes holding themselves { - bool needClass = true; + ObjectNotationType _onType = onType; char internalMemberString[1024]; - const char * result = onGetString(memberType, &value, internalMemberString, null, &needClass); + const char * result = onGetString(memberType, &value, internalMemberString, null, &_onType); - if(needClass && memberType.type != systemClass && memberType.type != enumClass && memberType.type != unitClass) + if(onType && memberType.type != systemClass && memberType.type != enumClass && memberType.type != unitClass) { //strcpy(memberString, memberType.name); strcat(memberString, " { "); + if(onType == json) strcat(memberString, "\""); if(result) strcat(memberString, result); + if(onType == json) strcat(memberString, "\""); strcat(memberString, " }"); } else if(result) @@ -774,8 +795,8 @@ static const char * OnGetString(Class _class, void * data, char * tempString, vo // TOCHECK: Is this null check still right?? if(memberType.typeSize > 4 || *(int *)memberData) { - bool needClass = true; - const char * result = onGetString(memberType, memberData, memberString, null, &needClass); + ObjectNotationType onType = econ; + const char * result = onGetString(memberType, memberData, memberString, null, &onType); if(result && memberString != result) strcpy(memberString, result); } @@ -785,10 +806,10 @@ static const char * OnGetString(Class _class, void * data, char * tempString, vo { char internalMemberString[1024]; byte * memberData = ((byte *)data + (((m._class.type == normalClass) ? m._class.offset : 0) + m.offset)); - bool needClass = true; + ObjectNotationType onType = econ; const char * result; - result = onGetString(memberType, memberData, internalMemberString, null, &needClass); - if(needClass) + result = onGetString(memberType, memberData, internalMemberString, null, &onType); + if(onType) { //strcpy(memberString, memberType.name); strcat(memberString, "{ "); @@ -807,14 +828,16 @@ static const char * OnGetString(Class _class, void * data, char * tempString, vo { if(prev) strcat(tempString, ", "); - if(!atMember || !strcmp(memberType.name, "bool")) + if(!atMember || onType == json || !strcmp(memberType.name, "bool")) { + if(onType == json) strcat(tempString, "\""); strcat(tempString, name); - strcat(tempString, " = "); + if(onType == json) strcat(tempString, "\""); + strcat(tempString, onType == json ? " : " : " = "); } // Only quote and escape for data serialization purposes - if(needClass && *needClass && !strcmp(memberType.name, "char *")) + if(onType == json || (onType && !strcmp(memberType.name, "char *"))) { int len = strlen(tempString); int c; @@ -1107,9 +1130,12 @@ static bool OnGetDataFromString(Class _class, void ** data, const char * string) memberData = (byte *)data + offset; if(memberType.type == structClass) { - if(thisMember) + if(thisMember && !thisMember.isProperty) { - if(!((bool (*)(void *, void *, const char *))(void *)memberType._vTbl[__ecereVMethodID_class_OnGetDataFromString])(memberType, memberData, memberString)) + if(_class.type == bitClass) + { + } + else if(!((bool (*)(void *, void *, const char *))(void *)memberType._vTbl[__ecereVMethodID_class_OnGetDataFromString])(memberType, memberData, memberString)) result = false; } } @@ -1121,7 +1147,9 @@ static bool OnGetDataFromString(Class _class, void ** data, const char * string) // We don't want KeyCode to use its base class OnGetDataFromString if(memberType._vTbl[__ecereVMethodID_class_OnGetDataFromString] == _class._vTbl[__ecereVMethodID_class_OnGetDataFromString]) { - if(!OnGetDataFromString(memberType, (void **)&value, memberString)) + if(_class.templateClass == class(Container) && memberType == _class && !strcmp(thisMember.name, "copySrc")) + result = false; + else if(!OnGetDataFromString(memberType, (void **)&value, memberString)) result = false; } else if(!((bool (*)(void *, void *, const char *))(void *)memberType._vTbl[__ecereVMethodID_class_OnGetDataFromString])(memberType, &value, memberString)) @@ -1131,6 +1159,7 @@ static bool OnGetDataFromString(Class _class, void ** data, const char * string) if(_class.type == bitClass) { BitMember bitMember = (BitMember) thisMember; + // TODO: Review for other type sizes? if(_class.typeSize == 4) { *(uint *)data = (uint32)(((*(uint *)data & ~bitMember.mask)) | ((value.ui64< 1E20 || Abs(f) < 1E-20)) + sprintf(string, "%.15e", f); else { int c; @@ -2135,12 +2185,12 @@ static char * Float_OnGetString(Class _class, float * data, char * string, void break; } } - if(first9 < c) + if(first9 < c) // TOCHECK: How is this code reachable? { string[c-1] = '1'; first9 = c; } - string[first9] = 0; + string[first9+1] = 0; } } break; @@ -2213,7 +2263,7 @@ static int Double_OnCompare(Class _class, double * data1, double * data2) return result; } -static char * Double_OnGetString(Class _class, double * data, char * string, void * fieldData, bool * needClass) +static char * Double_OnGetString(Class _class, double * data, char * string, void * fieldData, ObjectNotationType * onType) { double f = *data; if(f.isInf) @@ -2230,6 +2280,8 @@ static char * Double_OnGetString(Class _class, double * data, char * string, voi else strcpy(string, "nan"); } + else if(f && (Abs(f) > 1E20 || Abs(f) < 1E-20)) + sprintf(string, "%.15e", f); else { int c; @@ -2345,7 +2397,7 @@ public struct StaticString return result; } - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { return this ? string : null; } @@ -2360,7 +2412,7 @@ static void String_OnCopy(Class _class, char ** data, char * newData) if(newData) { int len = strlen(newData); - if(len) + if(1) //len) // TOCHECK: What will this break? Turning empty string into null string is problematic e.g. doesn't match OnCompare checks... { *data = eSystem_New(len+1); memcpy(*data, newData, len+1); @@ -2396,7 +2448,7 @@ static int String_OnCompare(Class _class, const String string1, const String str return 0; } -static char * String_OnGetString(Class _class, char * string, char * tempString, void * fieldData, bool * needClass) +static char * String_OnGetString(Class _class, char * string, char * tempString, void * fieldData, ObjectNotationType * onType) { return string; } @@ -2486,7 +2538,7 @@ void InitializeDataTypes1(Module module) eClass_AddVirtualMethod(baseClass, "OnCompare", "int typed_object::OnCompare(any_object object)", OnCompare, publicAccess); eClass_AddVirtualMethod(baseClass, "OnCopy", "void typed_object&::OnCopy(any_object newData)", OnCopy, publicAccess); eClass_AddVirtualMethod(baseClass, "OnFree", "void typed_object::OnFree(void)", OnFree, publicAccess); - eClass_AddVirtualMethod(baseClass, "OnGetString", "const char * typed_object::OnGetString(char * tempString, void * fieldData, bool * needClass)", OnGetString, publicAccess); + eClass_AddVirtualMethod(baseClass, "OnGetString", "const char * typed_object::OnGetString(char * tempString, void * reserved, ObjectNotationType * onType)", OnGetString, publicAccess); eClass_AddVirtualMethod(baseClass, "OnGetDataFromString", "bool typed_object&::OnGetDataFromString(const char * string)", OnGetDataFromString, publicAccess); eClass_AddVirtualMethod(baseClass, "OnEdit", "Window typed_object::OnEdit(DataBox dataBox, DataBox obsolete, int x, int y, int w, int h, void * userData)", null, publicAccess); eClass_AddVirtualMethod(baseClass, "OnSerialize", "void typed_object::OnSerialize(IOChannel channel)", OnSerialize, publicAccess); @@ -2508,7 +2560,7 @@ void InitializeDataTypes(Module module) RegisterClass_String(module); } -public int PrintStdArgsToBuffer(char * buffer, int maxLen, typed_object object, va_list args) +public int PrintStdArgsToBuffer(char * buffer, int maxLen, const typed_object object, va_list args) { int len = 0; // TOFIX: OnGetString will need a maxLen as well @@ -2531,7 +2583,7 @@ public int PrintStdArgsToBuffer(char * buffer, int maxLen, typed_object object, if(data) { // TOFIX: OnGetString will need a maxLen as well - result = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)_class._vTbl[__ecereVMethodID_class_OnGetString])(_class, data, buffer + len, null, null); + result = ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)_class._vTbl[__ecereVMethodID_class_OnGetString])(_class, data, buffer + len, null, null); if(result) { int newLen = strlen(result); @@ -2546,7 +2598,7 @@ public int PrintStdArgsToBuffer(char * buffer, int maxLen, typed_object object, return len; } -public int PrintBuf(char * buffer, int maxLen, typed_object object, ...) +public int PrintBuf(char * buffer, int maxLen, const typed_object object, ...) { va_list args; int len; @@ -2556,7 +2608,7 @@ public int PrintBuf(char * buffer, int maxLen, typed_object object, ...) return len; } -public int PrintLnBuf(char * buffer, int maxLen, typed_object object, ...) +public int PrintLnBuf(char * buffer, int maxLen, const typed_object object, ...) { va_list args; int len; @@ -2568,7 +2620,7 @@ public int PrintLnBuf(char * buffer, int maxLen, typed_object object, ...) return len; } -public char * PrintString(typed_object object, ...) +public char * PrintString(const typed_object object, ...) { char buffer[4096]; va_list args; @@ -2582,7 +2634,7 @@ public char * PrintString(typed_object object, ...) return string; } -public char * PrintLnString(typed_object object, ...) +public char * PrintLnString(const typed_object object, ...) { char buffer[4096]; va_list args; @@ -2600,24 +2652,41 @@ public char * PrintLnString(typed_object object, ...) #if defined(__ANDROID__) #include +#if defined(__LUMIN__) +#define bool _bool +#define false _false +#define true _true +#include +#undef bool +#undef false +#undef true +#endif #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "ecere-app", __VA_ARGS__)) #endif -public void PrintLn(typed_object object, ...) +#if defined(__UWP__) +import "System" +#endif + +public void PrintLn(const typed_object object, ...) { va_list args; char buffer[4096]; va_start(args, object); PrintStdArgsToBuffer(buffer, sizeof(buffer), object, args); va_end(args); -#if defined(__ANDROID__) && !defined(ECERE_NOFILE) +#if defined(__ANDROID__) && !defined(ECERE_NOFILE) && !defined(__LUMIN__) LOGI("%s", buffer); +#elif defined(__LUMIN__) + ML_LOG(Info, "%s", buffer); +#elif defined(__UWP__) + Logf("%s\n", buffer); #else puts(buffer); #endif } -public void Print(typed_object object, ...) +public void Print(const typed_object object, ...) { va_list args; char buffer[4096]; diff --git a/ecere/src/com/instance.c b/ecere/src/com/instance.c index 59304a8ab6..a435d968f4 100644 --- a/ecere/src/com/instance.c +++ b/ecere/src/com/instance.c @@ -192,7 +192,30 @@ static bool DualPipe_GetLine(FILE * p, char *s, int max) bool Instance_LocateModule(const char * name, char * fileName) { -#if defined(__WIN32__) +#if defined(__UWP__) + if(name && name[0]) + { + uint16 _wmoduleName[MAX_LOCATION]; + HMODULE hModule = null; + UTF8toUTF16Buffer(name, _wmoduleName, MAX_LOCATION); + hModule = LoadPackagedLibrary(_wmoduleName, 0); + if(hModule) + { + uint16 _wfileName[MAX_LOCATION]; + GetModuleFileNameW(hModule, _wfileName, MAX_LOCATION); + UTF16toUTF8Buffer(_wfileName, fileName, MAX_LOCATION); + FreeLibrary(hModule); + return true; + } + } + else + { + uint16 _wfileName[MAX_LOCATION]; + GetModuleFileNameW(null, _wfileName, MAX_LOCATION); + UTF16toUTF8Buffer(_wfileName, fileName, MAX_LOCATION); + return true; + } +#elif defined(__WIN32__) HMODULE hModule = null; if(name && name[0]) { @@ -456,7 +479,30 @@ void * Instance_Module_Load(const char * libLocation, const char * name, void ** *Load = null; *Unload = null; -#if defined(__WIN32__) +#if defined(__UWP__) + strcpy(fileName, name); + GetExtension(fileName, extension); + if(!extension[0]) + strcat(fileName, ".dll"); + + { + uint16 _wfileName[MAX_LOCATION]; + UTF8toUTF16Buffer(fileName, _wfileName, MAX_LOCATION); + library = LoadPackagedLibrary(_wfileName, 0); + } + if(library) + { +#ifdef _WIN64 + *Load = (void *)GetProcAddress(library, "__ecereDll_Load"); + *Unload = (void *)GetProcAddress(library, "__ecereDll_Unload"); +#else + *Load = (void *)GetProcAddress(library, "__ecereDll_Load@4"); + *Unload = (void *)GetProcAddress(library, "__ecereDll_Unload@4"); +#endif + if(!*Load) + FreeLibrary(library); + } +#elif defined(__WIN32__) strcpy(fileName, name); GetExtension(fileName, extension); if(!extension[0]) @@ -604,3 +650,17 @@ bool Double_isInf(double n) { return isinf(n); } int Double_signBit(double n) { return signbit(n); } double Double_nan(void) { return NAN; } double Double_inf(void) { return INFINITY; } + +#if defined(__clang__) && defined(_MSC_VER) + +int strcasecmp(const char * a, const char * b) +{ + return strcmpi(a, b); +} + +int strncasecmp(const char * a, const char * b, size_t n) +{ + return strnicmp(a, b, n); +} + +#endif diff --git a/ecere/src/com/instance.ec b/ecere/src/com/instance.ec index a78855fa1e..1f4b64b76a 100644 --- a/ecere/src/com/instance.ec +++ b/ecere/src/com/instance.ec @@ -4,6 +4,7 @@ namespace com; #if defined(__ANDROID__) #define DISABLE_MEMMGR + // #define USE_ATOMICS #endif #if defined(__EMSCRIPTEN__) @@ -19,6 +20,11 @@ import "dataTypes" //#define JUST_CHECK_LEAKS //#define JUST_CHECK_BOUNDARIES +#if defined(__linux__) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__i386__) && !defined(ECERE_BOOTSTRAP) +asm(".symver pow,pow@GLIBC_2.2.5"); +asm(".symver log,log@GLIBC_2.2.5"); +asm(".symver exp,exp@GLIBC_2.2.5"); +#endif #if defined(ECERE_BOOTSTRAP) || defined(ECERE_STATIC) #define dllexport @@ -102,7 +108,7 @@ uintsize _msize(void * p); uintsize malloc_usable_size(void * p); #endif -# define msize malloc_usable_size +// # define msize malloc_usable_size // malloc_usable_size is not requested size!! #endif #endif @@ -110,6 +116,14 @@ uintsize malloc_usable_size(void * p); #include #include +#ifdef USE_ATOMICS +#include + +void __atomic_fetch_add( volatile int* obj, int arg, int order); +void __atomic_fetch_sub( volatile int* obj, int arg, int order); + +#endif + private: #if defined(__ANDROID__) @@ -117,9 +131,19 @@ private: default const char * AndroidInterface_GetLibLocation(Module m); #include +#if !defined(__LUMIN__) #include #define printf(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "ecere-app", __VA_ARGS__)) +#else +#define bool _bool +#define false _false +#define true _true +#include +#undef bool +#undef false +#undef true +#endif #endif #undef property @@ -159,7 +183,7 @@ public class Angle : double; public class ::unichar : uint32 { - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { UTF32toUTF8Len(&this, 1, tempString, 5); return tempString; @@ -368,7 +392,12 @@ public: const char * defaultProperty; bool comRedefinition; - int count; // DEBUGGING + // Number of instances of this class alive + #ifdef USE_ATOMICS + atomic_int count; // TOCHECK: Verify handling of sizeof(atomic_int) != sizeof(int) + #else + int count; + #endif int isRemote; // TODO: Convert to an enum, can have values 0..3 bool internalDecl; @@ -390,6 +419,7 @@ public: int numParams; // TOTAL number of params including all base classes; use templateParams.count for this level bool isInstanceClass; bool byValueSystemClass; + void * bindingsClass; property const char * { @@ -405,7 +435,7 @@ public: } }; - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { return name; } @@ -740,14 +770,14 @@ bool allocateInternal; #endif -static uint TOTAL_MEM = 0; +static uint64 TOTAL_MEM = 0; #if !defined(MEMINFO) && !defined(DISABLE_MEMMGR) -static uint OUTSIDE_MEM = 0; +static uint64 OUTSIDE_MEM = 0; #endif #if !defined(_NOMUTEX) #if !defined(ECERE_BOOTSTRAP) -static Mutex memMutex { }; +/*static */Mutex memMutex { }; #endif #endif @@ -946,9 +976,9 @@ private struct BlockPool bool Expand(uint numBlocks) { - byte * memory = malloc(numBlocks * blockSpace); - // byte * memory = calloc(1, numBlocks * blockSpace); - TOTAL_MEM += numBlocks * blockSpace; + byte * memory = malloc((uintsize)numBlocks * blockSpace); + // byte * memory = calloc(1, (uintsize)numBlocks * blockSpace); + TOTAL_MEM += (uint64)numBlocks * blockSpace; #ifdef _DEBUG /*if(blockSize == 28) printf("Expanding pool %x (%d)\n", this, blockSize);*/ @@ -1113,7 +1143,7 @@ private struct BlockPool if(prev) prev.next = null; - TOTAL_MEM -= part.size * blockSpace; + TOTAL_MEM -= (uint64)part.size * blockSpace; TOTAL_MEM -= sizeof(class MemPart); numParts--; @@ -1305,9 +1335,9 @@ static void * _mymalloc(unsigned int size) static void * _mycalloc(int n, unsigned int size) { - void * pointer = _mymalloc(n*size); + void * pointer = _mymalloc(n * size); if(pointer) - memset(pointer, 0, n*size); + memset(pointer, 0, n * size); return pointer; } @@ -1468,10 +1498,17 @@ static void * _mycrealloc(void * pointer, unsigned int size) static void * _malloc(unsigned int size) { #if defined(DISABLE_MEMMGR) && !defined(MEMINFO) + +#if defined(msize) return size ? malloc(size) : null; #else - void * pointer; + byte * p = size ? malloc(size + sizeof(size_t)) : null; + if(p) *(size_t *)p = size; + return p ? p + sizeof(size_t) : null; +#endif +#else + void * pointer; #if !defined(_NOMUTEX) memMutex.Wait(); @@ -1536,7 +1573,15 @@ static void * _malloc(unsigned int size) static void * _calloc(int n, unsigned int size) { #if defined(DISABLE_MEMMGR) && !defined(MEMINFO) - return size ? calloc(n, size) : null; + +#if defined(msize) + return n && size ? calloc(n, size) : null; +#else + byte * p = n && size ? calloc(n, size + sizeof(size_t)) : null; + if(p) *(size_t *)p = size; + return p ? p + sizeof(size_t) : null; +#endif + #else void * pointer; @@ -1544,7 +1589,7 @@ static void * _calloc(int n, unsigned int size) memMutex.Wait(); #endif - pointer = (n*size) ? calloc(1, n*size + 2 * REDZONE) : null; + pointer = n && size ? calloc(1, n * size + 2 * REDZONE) : null; #ifdef MEMINFO if(pointer) { @@ -1576,7 +1621,7 @@ static void * _calloc(int n, unsigned int size) MemInfo block; stack.recurse = true; - block = MemInfo { size = (unsigned int)n*size, key = (uintptr)((byte *)pointer + REDZONE), _class = allocateClass, internal = allocateInternal, id = blockID++ }; + block = MemInfo { size = (unsigned int)n * size, key = (uintptr)((byte *)pointer + REDZONE), _class = allocateClass, internal = allocateInternal, id = blockID++ }; memcpy(block.allocLoc, stack.frames + stack.pos - Min(stack.pos, MAX_MEMORY_LOC), Min(stack.pos, MAX_MEMORY_LOC) * sizeof(char *)); memBlocks.Add(block); stack.recurse = false; @@ -1592,7 +1637,7 @@ static void * _calloc(int n, unsigned int size) if(pointer) { memset(pointer, 0xAB, REDZONE); - memset((byte *)pointer + REDZONE + (unsigned int)n*size, 0xAB, REDZONE); + memset((byte *)pointer + REDZONE + (unsigned int)n * size, 0xAB, REDZONE); } #endif return pointer ? ((byte*)pointer + REDZONE) : null; @@ -1602,8 +1647,18 @@ static void * _calloc(int n, unsigned int size) static void * _realloc(void * pointer, unsigned int size) { #if defined(DISABLE_MEMMGR) && !defined(MEMINFO) + +#if defined(msize) if(!size) { free(pointer); return null; } return realloc(pointer, size); +#else + byte * p; + if(!size) { if(pointer) free((byte *)pointer - sizeof(size_t)); return null; } + p = realloc(pointer ? (byte *)pointer - sizeof(size_t) : null, size + sizeof(size_t)); + if(p) *(size_t *)p = size; + if(p) p += sizeof(size_t); + return p; +#endif #else if(!size) { _free(pointer); return null; } @@ -1710,13 +1765,23 @@ static void * _realloc(void * pointer, unsigned int size) static void * _crealloc(void * pointer, unsigned int size) { #if defined(DISABLE_MEMMGR) && !defined(MEMINFO) + byte * p; + +#if defined(msize) uintsize s = pointer ? msize(pointer) : 0; - void * p; if(!size) { free(pointer); return null; } - p = realloc(pointer, size); - if(size > s) +#else + uintsize s = pointer ? *(size_t *)((byte *)pointer - sizeof(size_t)) : 0; + if(!size) { free((byte *)pointer - sizeof(size_t)); return null; } + p = realloc(pointer ? (byte *)pointer - sizeof(size_t) : null, size + sizeof(size_t)); + if(p) *(size_t *)p = size; + if(p) p += sizeof(size_t); +#endif + if(size > s && p) memset((byte *)p + s, 0, size - s); + if(size && !p) + printf("_crealloc failed allocating %d bytes!\n", size); return p; #else if(!size) { _free(pointer); return null; } @@ -1825,7 +1890,13 @@ static void _free(void * pointer) if(pointer) { #if defined(DISABLE_MEMMGR) && !defined(MEMINFO) + +#if defined(msize) free(pointer); +#else + free((byte *)pointer - sizeof(size_t)); +#endif + #else #if !defined(_NOMUTEX) if(memMutex != pointer) memMutex.Wait(); @@ -2078,6 +2149,7 @@ public void CheckMemory() memoryErrorsCount++; } printf("Memory Check Completed.\n"); + fflush(stdout); #if defined(__WIN32__) && !defined(ECERE_BOOTSTRAP) if(memoryErrorsCount) system("pause"); @@ -2145,10 +2217,14 @@ static void FixDerivativesBase(Class base, Class mod) if(type == normalClass || type == noHeadClass) { - // Use 'memberOffset' for nohead class as the members get added without padding - _class.offset = (base && (base.templateClass ? (type == normalClass ? base.templateClass.structSize : base.templateClass.memberOffset) : (type == normalClass ? base.structSize : base.memberOffset)) && base.type != systemClass) ? (base.templateClass ? base.templateClass.structSize : base.structSize) : ((type == noHeadClass) ? 0 : sizeof(class Instance)); - if(_class.structAlignment && (_class.offset % _class.structAlignment)) - _class.offset += _class.structAlignment - _class.offset % _class.structAlignment; + // TOCHECK: base.memberOffset might not properly set at this stage + if(type != noHeadClass || base.memberOffset) + { + // Use 'memberOffset' for nohead class as the members get added without padding + _class.offset = (base && (base.templateClass ? (type == normalClass ? base.templateClass.structSize : base.templateClass.memberOffset) : (type == normalClass ? base.structSize : base.memberOffset)) && base.type != systemClass) ? (base.templateClass ? base.templateClass.structSize : base.structSize) : ((type == noHeadClass) ? 0 : sizeof(class Instance)); + if(_class.structAlignment && (_class.offset % _class.structAlignment)) + _class.offset += _class.structAlignment - _class.offset % _class.structAlignment; + } } else _class.offset = 0; // Force set to 0 @@ -2168,6 +2244,8 @@ static void FixDerivativesBase(Class base, Class mod) } else if(type == normalClass || type == noHeadClass) { + if(type == noHeadClass && _class.structSize != _class.offset + size) + printf("ERROR: inconsistent nohead class struct size for %s\n", _class.name); _class.structSize = _class.offset + size; _class.typeSize = sizeof(void *); } @@ -2722,10 +2800,13 @@ public dllexport Class eSystem_RegisterClass(ClassType type, const char * name, _class.data = renew _class.data byte[totalSizeClass]; // Class Data is often not inherited... e.g. Window::pureVtbl problem // memset(_class.data, 0, totalSizeClass); - if(base && base.type != systemClass && base.type != enumClass) - memcpy(_class.data, base.data, offsetClass); - else - memset(_class.data, 0, offsetClass); + if(offsetClass) + { + if(base && base.type != systemClass && base.type != enumClass) + memcpy(_class.data, base.data, offsetClass); + else + memset(_class.data, 0, offsetClass); + } memset((byte *)_class.data + offsetClass, 0, sizeClass); } @@ -2878,14 +2959,15 @@ public dllexport Class eSystem_RegisterClass(ClassType type, const char * name, } else if(type == bitClass || type == enumClass || type == unitClass) { - Class dataTypeClass = System_FindClass(_class.module, dataTypeString, true); + Class dataTypeClass = dataTypeString ? System_FindClass(_class.module, dataTypeString, true) : null; if(dataTypeClass) _class.typeSize = dataTypeClass.typeSize; _class.structSize = 0; } else if(type == normalClass || type == noHeadClass) { - _class.structSize = _class.offset + size; + // REVIEW: This was wrongly set for NoHead classes -- the sizeof() of the struct passed includes all bases + _class.structSize = type == normalClass ? _class.offset + size : size; _class.typeSize = sizeof(void *); } _class.offsetClass = offsetClass; @@ -3457,6 +3539,16 @@ Class System_FindClass(Module module, const char * name, bool registerTemplatesI if(_class && templateParams) { + Class b; + bool isAVLNode = false, isMapNode = false; + + for(b = _class; b; b = b.base) + { + while(b.templateClass) b = b.templateClass; + if(!strcmp(b.fullName, "ecere::com::MapNode")) isMapNode = true; + else if(!strcmp(b.fullName, "ecere::com::AVLNode")) isAVLNode = true; + } + // if(!numParams) return null; templatedClass = Class { }; @@ -3480,6 +3572,25 @@ Class System_FindClass(Module module, const char * name, bool registerTemplatesI ComputeClassParameters(templatedClass, templateParams, module, registerTemplatesInternalDecl); + if(isAVLNode && templatedClass.templateArgs) + { + // Current work-around for correcting variable size of AVLNodes + Class keyClass = templatedClass.templateArgs[0].dataTypeClass; + int keySize = (keyClass && keyClass.type == structClass) ? keyClass.typeSize : sizeof(uint64); + + if(keySize != sizeof(uint64)) + templatedClass.structSize += keySize - sizeof(uint64); + + if(isMapNode) + { + // NOTE: the value member will still have incorrect offset in members shared with base class + Class valClass = templatedClass.templateArgs[2].dataTypeClass; + int valSize = (valClass && valClass.type == structClass) ? valClass.typeSize : sizeof(uint64); + + if(valSize != sizeof(uint64)) + templatedClass.structSize += valSize - sizeof(uint64); + } + } _class.templatized.Add(OldLink { data = templatedClass }); } return templatedClass; @@ -4623,12 +4734,12 @@ public dllexport Method eClass_FindMethod(Class _class, const char * name, Modul } // Construct an instance -static bool ConstructInstance(void * instance, Class _class, Class from) +static bool ConstructInstance(void * instance, Class _class, Class from, bool bindingsAlloc) { if(_class.templateClass) _class = _class.templateClass; if(_class.base && from != _class.base) { - if(!ConstructInstance(instance, _class.base, from)) + if(!ConstructInstance(instance, _class.base, from, false)) return false; } if(_class.Initialize) @@ -4639,7 +4750,12 @@ static bool ConstructInstance(void * instance, Class _class, Class from) } if(_class.Constructor) { - if(!_class.Constructor(instance)) + bool result; + if(_class.bindingsClass) + result = ((bool (*)(void *, bool))(void *)_class.Constructor)(instance, bindingsAlloc); + else + result = _class.Constructor(instance); + if(!result) { for(; _class; _class = _class.base) { @@ -4650,11 +4766,22 @@ static bool ConstructInstance(void * instance, Class _class, Class from) return false; } } + + #ifdef USE_ATOMICS + atomic_fetch_add(&(_class.templateClass ? _class.templateClass : _class).count, 1); + #else +#if !defined(_NOMUTEX) && !defined(ECERE_BOOTSTRAP) + memMutex.Wait(); +#endif (_class.templateClass ? _class.templateClass : _class).count++; +#if !defined(_NOMUTEX) && !defined(ECERE_BOOTSTRAP) + memMutex.Release(); +#endif + #endif return true; } -public dllexport void * eInstance_New(Class _class) +static void * Instance_New(Class _class, bool bindingsAlloc) { Instance instance = null; if(_class) @@ -4716,7 +4843,7 @@ public dllexport void * eInstance_New(Class _class) // Copy the virtual table initially instance._vTbl = _class._vTbl; } - if(instance && !ConstructInstance(instance, _class, null)) + if(instance && !ConstructInstance(instance, _class, null, bindingsAlloc)) { _free(instance); instance = null; @@ -4727,6 +4854,16 @@ public dllexport void * eInstance_New(Class _class) return instance; } +public dllexport void * eInstance_New(Class _class) +{ + return Instance_New(_class, true); +} + +public dllexport void * eInstance_NewEx(Class _class, bool bindingsAlloc) +{ + return Instance_New(_class, bindingsAlloc); +} + public dllexport void eInstance_Evolve(Instance * instancePtr, Class _class) { if(_class && instancePtr && *instancePtr) @@ -4836,7 +4973,7 @@ public dllexport void eInstance_Evolve(Instance * instancePtr, Class _class) instance._vTbl = _class._vTbl; // We don't want to reconstruct the portion already constructed... - if(!ConstructInstance(instance, _class, fromClass)) + if(!ConstructInstance(instance, _class, fromClass, false)) { _free(instance); *instancePtr = null; @@ -4881,7 +5018,7 @@ public dllexport void eInstance_Delete(Instance instance) watchers->Delete(watcher); } } - + // FIXME: Fix this comment highlighting!!! /*// Loop through properties to delete all watchers? Might slow down destruction... { Property _property; @@ -4913,11 +5050,24 @@ public dllexport void eInstance_Delete(Instance instance) for(_class = instance._class; _class; _class = base) { + int cCount; + if(_class.templateClass) _class = _class.templateClass; base = _class.base; - (_class.templateClass ? _class.templateClass : _class).count--; - if(_class.type == normalClass && !_class.count && !_class.module) + + #ifdef USE_ATOMICS + cCount = (int)atomic_fetch_sub(&(_class.templateClass ? _class.templateClass : _class).count, 1); + #else +#if !defined(_NOMUTEX) && !defined(ECERE_BOOTSTRAP) + memMutex.Wait(); +#endif + cCount = --(_class.templateClass ? _class.templateClass : _class).count; +#if !defined(_NOMUTEX) && !defined(ECERE_BOOTSTRAP) + memMutex.Release(); +#endif + #endif + if(!cCount && _class.type == normalClass && !_class.module) { #ifdef MEMINFO // printf("Now Destructing class %s\n", _class.name); @@ -5326,18 +5476,17 @@ public dllexport void eClass_Resize(Class _class, int newSize) // F000F000 will mean a pointer size alignment public dllexport DataMember eClass_AddDataMember(Class _class, const char * name, const char * type, unsigned int size, unsigned int alignment, AccessMode declMode) { - if(_class && name) + if(_class && (name || (!name && !size && !type && alignment))) { - if(!_class.members.FindString(name)) + if(!name || !_class.members.FindString(name)) { - DataMember dataMember; - if(alignment) { bool pointerAlignment = alignment == 0xF000F000; bool force64Bits = (_class.module.application.isGUIApp & 2) ? true : false; bool force32Bits = (_class.module.application.isGUIApp & 4) ? true : false; - if((force32Bits || force64Bits) && !strcmp(_class.name, "AVLNode") && !strcmp(name, "__ecerePrivateData0")) + if((force32Bits || force64Bits) && + name && !strcmp(_class.name, "AVLNode") && !strcmp(name, "__ecerePrivateData0")) { if(force64Bits) { @@ -5369,21 +5518,24 @@ public dllexport DataMember eClass_AddDataMember(Class _class, const char * name _class.memberOffset += alignment - (_class.memberOffset % alignment); } - dataMember = DataMember { - name = CopyString(name); - dataTypeString = CopyString(type); - id = _class.memberID++; - _class = _class; - offset = _class.memberOffset; - memberOffset = size; - memberAccess = declMode; - membersAlpha.CompareKey = (void *)BinaryTree::CompareString; - }; - _class.membersAndProperties.Add(dataMember); - _class.memberOffset += size; - - _class.members.Add((BTNode)BTNamedLink { name = dataMember.name, data = dataMember }); - return dataMember; + if(name) + { + DataMember dataMember + { + name = CopyString(name); + dataTypeString = CopyString(type); + id = _class.memberID++; + _class = _class; + offset = _class.memberOffset; + memberOffset = size; + memberAccess = declMode; + membersAlpha.CompareKey = (void *)BinaryTree::CompareString; + }; + _class.membersAndProperties.Add(dataMember); + _class.memberOffset += size; + _class.members.Add((BTNode)BTNamedLink { name = dataMember.name, data = dataMember }); + return dataMember; + } } } return null; @@ -5391,10 +5543,8 @@ public dllexport DataMember eClass_AddDataMember(Class _class, const char * name // F000F000 will mean a pointer size alignment public dllexport DataMember eMember_AddDataMember(DataMember member, const char * name, const char * type, unsigned int size, unsigned int alignment, AccessMode declMode) { - if(name && !member.membersAlpha.FindString(name)) + if((name && !member.membersAlpha.FindString(name)) || (!name && !type && !size && alignment)) { - DataMember dataMember; - if(alignment) { bool pointerAlignment = alignment == 0xF000F000; @@ -5412,25 +5562,29 @@ public dllexport DataMember eMember_AddDataMember(DataMember member, const char if(member.memberOffset % alignment) member.memberOffset += alignment - (member.memberOffset % alignment); } - dataMember = DataMember { - name = CopyString(name); - _class = member._class; - dataTypeString = CopyString(type); - id = member.memberID++; - offset = (member.type == unionMember) ? 0 : member.memberOffset; - memberAccess = declMode; - membersAlpha.CompareKey = (void *)BinaryTree::CompareString; - }; - member.members.Add(dataMember); - if(member.type == unionMember) + if(name) { - if(size > member.memberOffset) - member.memberOffset = size; + DataMember dataMember + { + name = CopyString(name); + _class = member._class; + dataTypeString = CopyString(type); + id = member.memberID++; + offset = (member.type == unionMember) ? 0 : member.memberOffset; + memberAccess = declMode; + membersAlpha.CompareKey = (void *)BinaryTree::CompareString; + }; + member.members.Add(dataMember); + if(member.type == unionMember) + { + if(size > member.memberOffset) + member.memberOffset = size; + } + else + member.memberOffset += size; + member.membersAlpha.Add((BTNode)BTNamedLink { name = dataMember.name, data = dataMember }); + return dataMember; } - else - member.memberOffset += size; - member.membersAlpha.Add((BTNode)BTNamedLink { name = dataMember.name, data = dataMember }); - return dataMember; } return null; } @@ -5603,8 +5757,10 @@ static Module Module_Load(Module fromModule, const char * name, AccessMode impor else { const char * libLocation = null; -#if defined(__ANDROID__) +#if defined(__ANDROID__) && !defined(__LUMIN__) libLocation = AndroidInterface_GetLibLocation(fromModule.application); +#elif defined(__LUMIN__) + libLocation = ""; #endif library = Instance_Module_Load(libLocation, name, &Load, &Unload); } @@ -6290,7 +6446,7 @@ public dllexport void eProperty_SelfWatch(Class _class, const char * name, void } } -public dllexport void eInstance_Watch(void * instance, Property _property, void * object, void (*callback)(void *, void *)) +public dllexport void eInstance_Watch(Instance instance, Property _property, void * object, void (*callback)(void *, void *)) { if(_property.isWatchable) { @@ -6507,7 +6663,7 @@ static void LoadCOM(Module module) applicationClass.offset = 12 + 4 + 20 + 20 + 20 + 20 + 4 + 4 + 4 + 4 + 4 + 4 + 4 + (16 + 4 + 4 + 4*16) + (16 + 4 + 4 + 4*16); applicationClass.structSize = applicationClass.offset + (4+4+4+4 + 20 + 4 + 88); } - eClass_AddVirtualMethod(applicationClass, "Main", "void()", null, publicAccess); + eClass_AddVirtualMethod(applicationClass, "Main", "void()", DefaultFunction, publicAccess); eClass_AddDataMember(applicationClass, "argc", "int", sizeof(int), 4, publicAccess); eClass_AddDataMember(applicationClass, "argv", "const char **", pointerSize, pointerSize, publicAccess); eClass_AddDataMember(applicationClass, "exitCode", "int", sizeof(int), 4, publicAccess); @@ -6616,7 +6772,6 @@ static void LoadCOM(Module module) eSystem_RegisterFunction("isupper", "int isupper(int c)", isupper, module, baseSystemAccess); eSystem_RegisterFunction("isprint", "int isprint(int c)", isprint, module, baseSystemAccess); eSystem_RegisterFunction("isblank", "int isblank(int c)", isblank, module, baseSystemAccess); - } public dllexport Application __ecere_COM_Initialize(bool guiApp, int argc, char * argv[]) @@ -6844,7 +6999,7 @@ public enum Platform } }; - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { if(this >= firstPlatform && this <= lastPlatform) { @@ -7499,3 +7654,17 @@ public void queryMemInfo(char * string) #endif #endif } + +public void eSystem_LockMem() +{ +#if !defined(_NOMUTEX) && (!defined(DISABLE_MEMMGR) || !defined(USE_ATOMICS)) + memMutex.Wait(); +#endif +} + +public void eSystem_UnlockMem() +{ +#if !defined(_NOMUTEX) && (!defined(DISABLE_MEMMGR) || !defined(USE_ATOMICS)) + memMutex.Release(); +#endif +} diff --git a/ecere/src/gfx/3D/Camera.ec b/ecere/src/gfx/3D/Camera.ec index 663ce6fd08..b8dc38c771 100644 --- a/ecere/src/gfx/3D/Camera.ec +++ b/ecere/src/gfx/3D/Camera.ec @@ -23,9 +23,9 @@ public: property Object target { set { target = value; } get { return target; } }; property FovDirection fovDirection { set { fovDirection = value; } get { return fovDirection; } }; property float aspectRatio { set { aspectRatio = value; } get { return aspectRatio; } }; - property Size focal { get { value = { focalX, focalY }; } }; + property Size focal { get { value = { focalX, focalY }; } set { focalX = value.w; focalY = value.h; } }; - void Setup(int width, int height, Point origin) + void Setup(int width, int height, const Point origin) { if(this) { diff --git a/ecere/src/gfx/3D/Matrix.ec b/ecere/src/gfx/3D/Matrix.ec index ab6818b518..94bdd9ec2b 100644 --- a/ecere/src/gfx/3D/Matrix.ec +++ b/ecere/src/gfx/3D/Matrix.ec @@ -7,14 +7,15 @@ public union Matrix double array[16]; double m[4][4]; - const char * OnGetString(char * string, void * fieldData, bool * needClass) + const char * OnGetString(char * string, void * fieldData, ObjectNotationType * onType) { + bool spacing = false; //true; int y, x; string[0] = 0; - strcat(string, "{ "); + strcat(string, spacing ? "{\n" : "{ "); for(y = 0; y < 4; y++) { - strcat(string, "{ "); + strcat(string, spacing ? " { " : "{ "); for(x = 0; x < 4; x++) { char member[256]; @@ -24,8 +25,9 @@ public union Matrix } strcat(string, " }"); if(y < 3) strcat(string, ", "); + if(spacing) strcat(string, "\n"); } - strcat(string, " }"); + strcat(string, spacing ? "}\n" : " }"); return string; } @@ -35,7 +37,7 @@ public union Matrix m[0][0]=m[1][1]=m[2][2]=m[3][3]=1; } - void Transpose(Matrix source) + void Transpose(const Matrix source) { int i,j; for(i=0; i<4; i++) @@ -43,9 +45,8 @@ public union Matrix m[j][i] = source.m[i][j]; } - void Multiply(Matrix a, Matrix b) + void Multiply(const Matrix a, const Matrix b) { -#if 1 // We need a full matrix multiplication for the Projection matrix m[0][0]=a.m[0][0]*b.m[0][0] + a.m[0][1]*b.m[1][0] + a.m[0][2]*b.m[2][0] + a.m[0][3]*b.m[3][0]; m[0][1]=a.m[0][0]*b.m[0][1] + a.m[0][1]*b.m[1][1] + a.m[0][2]*b.m[2][1] + a.m[0][3]*b.m[3][1]; @@ -66,7 +67,10 @@ public union Matrix m[3][1]=a.m[3][0]*b.m[0][1] + a.m[3][1]*b.m[1][1] + a.m[3][2]*b.m[2][1] + a.m[3][3]*b.m[3][1]; m[3][2]=a.m[3][0]*b.m[0][2] + a.m[3][1]*b.m[1][2] + a.m[3][2]*b.m[2][2] + a.m[3][3]*b.m[3][2]; m[3][3]=a.m[3][0]*b.m[0][3] + a.m[3][1]*b.m[1][3] + a.m[3][2]*b.m[2][3] + a.m[3][3]*b.m[3][3]; -#else + } + + void Multiply3x4(const Matrix a, const Matrix b) + { m[0][0]=a.m[0][0]*b.m[0][0] + a.m[0][1]*b.m[1][0] + a.m[0][2]*b.m[2][0]; m[0][1]=a.m[0][0]*b.m[0][1] + a.m[0][1]*b.m[1][1] + a.m[0][2]*b.m[2][1]; m[0][2]=a.m[0][0]*b.m[0][2] + a.m[0][1]*b.m[1][2] + a.m[0][2]*b.m[2][2]; @@ -86,10 +90,9 @@ public union Matrix m[3][1]=a.m[3][0]*b.m[0][1] + a.m[3][1]*b.m[1][1] + a.m[3][2]*b.m[2][1] + b.m[3][1]; m[3][2]=a.m[3][0]*b.m[0][2] + a.m[3][1]*b.m[1][2] + a.m[3][2]*b.m[2][2] + b.m[3][2]; m[3][3]=1; -#endif } - void RotationQuaternion(Quaternion quat) + void RotationQuaternion(const Quaternion quat) { double xx = quat.x*quat.x, yy = quat.y*quat.y, zz = quat.z*quat.z; double xy = quat.x*quat.y, xz = quat.x*quat.z, yz = quat.y*quat.z; @@ -114,32 +117,22 @@ public union Matrix void Translate(double tx, double ty, double tz) { - Matrix tmat; - Matrix mat1; - - FillBytesBy4(tmat, 0, sizeof(Matrix) >> 2); - tmat.m[0][0]=tmat.m[1][1]=tmat.m[2][2]=tmat.m[3][3]=1; - tmat.m[3][0]=tx; tmat.m[3][1]=ty; tmat.m[3][2]=tz; - mat1.Multiply(this, tmat); - this = mat1; + m[3][0] += tx; m[3][1] += ty; m[3][2] += tz; } void Scale(double sx, double sy, double sz) { - Matrix smat; - Matrix mat1; - FillBytesBy4(smat, 0, sizeof(Matrix) >> 2); - smat.m[0][0]=sx; smat.m[1][1]=sy; smat.m[2][2]=sz; smat.m[3][3]=1; - mat1.Multiply(this, smat); - this = mat1; + m[0][0] *= sx; m[1][0] *= sx; m[2][0] *= sx; m[3][0] *= sx; + m[0][1] *= sy; m[1][1] *= sy; m[2][1] *= sy; m[3][1] *= sy; + m[0][2] *= sz; m[1][2] *= sz; m[2][2] *= sz; m[3][2] *= sz; } - void Rotate(Quaternion quat) + void Rotate(const Quaternion quat) { Matrix rmat; Matrix mat1; rmat.RotationQuaternion(quat); - mat1.Multiply(this, rmat); + mat1.Multiply3x4(this, rmat); this = mat1; } @@ -171,14 +164,15 @@ public union Matrix return result; } - void Inverse(Matrix source) + void Inverse(const Matrix source) { - double det = source.Determinant(); + double det = source.Determinant(); // FIXME: Get should be fine with const objects // if(Abs(det) < 0.0005) if(Abs(det) < 0.00000000000001) Identity(); else { + double invDet = 1.0 / det; int i, j, sign; for ( i = 0; i < 4; i++ ) for ( j = 0; j < 4; j++ ) @@ -202,14 +196,31 @@ public union Matrix - msub3[0][1] * ( msub3[1][0]*msub3[2][2] - msub3[2][0]*msub3[1][2] ) + msub3[0][2] * ( msub3[1][0]*msub3[2][1] - msub3[2][0]*msub3[1][1] ); - m[j][i] = m3det * sign / det; + m[j][i] = m3det * sign * invDet; } } } + void InverseTransposeTransform(const Matrix source) + { + Vector3D x { source.array[0], source.array[1], source.array[ 2] }; + Vector3D y { source.array[4], source.array[5], source.array[ 6] }; + Vector3D z { source.array[8], source.array[9], source.array[10] }; + Vector3D s2 + { + x.x * x.x + x.y * x.y + x.z * x.z, + y.x * y.x + y.y * y.y + y.z * y.z, + z.x * z.x + z.y * z.y + z.z * z.z + }; + double ix = 1.0 / s2.x, iy = 1.0 / s2.y, iz = 1.0 / s2.z; + array[0] = x.x * ix; array[1] = x.y * ix; array[ 2] = x.z * ix; + array[4] = y.x * iy; array[5] = y.y * iy; array[ 6] = y.z * iy; + array[8] = z.x * iz; array[9] = z.y * iz; array[10] = z.z * iz; + } + void ToEuler(Euler euler) { - if(fabs(m[2][1]) <= 1.0 - 0.000005) + /*if(fabs(m[2][1]) <= 1.0 - 0.000005) { euler.yaw = atan2(-m[2][0], m[2][2]); euler.pitch = asin ( m[2][1]); @@ -221,6 +232,8 @@ public union Matrix euler.pitch = Pi/2; euler.roll = 0; } + */ + euler.FromMatrix(this, yxz); } property Quaternion @@ -228,4 +241,71 @@ public union Matrix set { RotationQuaternion(value); } get { value.RotationMatrix(this); } } +private: + property Vector3D translation + { + get { value = { m[3][0], m[3][1], m[3][2] }; } + } + void extractScaling(Matrix r, Vector3D scaling) + { + Vector3D x { m[0][0], m[0][1], m[0][2] }; + Vector3D y { m[1][0], m[1][1], m[1][2] }; + Vector3D z { m[2][0], m[2][1], m[2][2] }; + Vector3D s + { + x = sqrt(x.x * x.x + x.y * x.y + x.z * x.z), + y = sqrt(y.x * y.x + y.y * y.y + y.z * y.z), + z =-sqrt(z.x * z.x + z.y * z.y + z.z * z.z) + }; + Vector3D orth; + + { double ix = 1.0 / s.x; x.x *= ix; x.y *= ix; x.z *= ix; } + { double iy = 1.0 / s.y; y.x *= iy; y.y *= iy; y.z *= iy; } + { double iz = 1.0 / s.z; z.x *= iz; z.y *= iz; z.z *= iz; } + + orth.CrossProduct(z, y); + if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(x.x)) || + (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(x.y)) || + (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(x.z))) + { + x = orth; + s.x *= -1; + } + + orth.CrossProduct(x, z); + if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(y.x)) || + (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(y.y)) || + (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(y.z))) + { + y = orth; + s.y *= -1; + } + + orth.CrossProduct(x, y); + if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(z.x)) || + (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(z.y)) || + (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(z.z))) + { + z = orth; + s.z *= -1; + } + + if(scaling != null) + scaling = s; + if(r != null) + { + r.m[0][0] = x.x, r.m[0][1] = x.y, r.m[0][2] = x.z, r.m[0][3] = 0; + r.m[1][0] = y.x, r.m[1][1] = y.y, r.m[1][2] = y.z, r.m[1][3] = 0; + r.m[2][0] = z.x, r.m[2][1] = z.y, r.m[2][2] = z.z, r.m[2][3] = 0; + r.m[3][0] = 0, r.m[3][1] = 0, r.m[3][2] = 0, r.m[3][3] = 1; + } + } + property Vector3D scaling + { + get { extractScaling(null, value); } + } + property Quaternion orientation // This assumes it is a rotation matrix only! + { + get { value.RotationMatrix(this); } + } }; diff --git a/ecere/src/gfx/3D/Mesh.ec b/ecere/src/gfx/3D/Mesh.ec index a43a29d20f..f16957bb75 100644 --- a/ecere/src/gfx/3D/Mesh.ec +++ b/ecere/src/gfx/3D/Mesh.ec @@ -2,8 +2,31 @@ namespace gfx3D; import "Display" -public class MeshFeatures { public bool vertices:1, normals:1, texCoords1:1, texCoords2:1, doubleNormals:1, doubleVertices:1, colors:1, lightVectors:1, tangents:1, intVertices:1; }; -public class PrimitiveGroupType { public: RenderPrimitiveType primitiveType:8; bool vertexRange:1, indices32bit:1; }; +#if !defined(ECERE_NOGL) +import "GLMultiDraw" +#include "gl123es.h" +#endif + +#ifdef ETC2_COMPRESS +default extern void etc2Free(void * data); +#endif + +public class MeshFeatures +{ +public: + bool vertices:1, normals:1, texCoords1:1, texCoords2:1, doubleNormals:1, doubleVertices:1, colors:1, lightVectors:1, tangents:1, intVertices:1; + // NOTE: neither of these two are currently kept in Mesh's flags member: + bool memAllocOnly:1, interleaved:1; + bool ownMEAB:1; +}; + +public class PrimitiveGroupType : uint32 +{ +public: + RenderPrimitiveType primitiveType:8; + bool vertexRange:1, indices32bit:1, sharedIndices:1; +}; + public enum RenderPrimitiveType : PrimitiveGroupType { dot, // Point, @@ -22,7 +45,23 @@ public enum RenderPrimitiveType : PrimitiveGroupType */ }; -public class MaterialFlags { public bool doubleSided:1, translucent:1, tile:1, noFog:1, singleSideLight:1, separateSpecular:1, cubeMap:1, noLighting:1; }; +public class MaterialFlags +{ +public: + bool doubleSided:1; + bool translucent:1; + bool tile:1; + bool noFog:1; + bool singleSideLight:1; + bool separateSpecular:1; + bool cubeMap:1; + bool noLighting:1; + bool partlyTransparent:1; + bool setupTextures:1; // NOTE: This is a status flag and should probably be elsewhere... + bool update:1; + bool constantColor:1; +}; + public class Material : struct { public: @@ -45,12 +84,43 @@ public: MaterialFlags flags; float uScale, vScale; +#if !defined(ECERE_NOGL) Shader shader; + #define LAST_MEMBER shader +#else + #define LAST_MEMBER vScale +#endif + + Material() + { + flags.setupTextures = true; + flags.update = true; + } + void Free() { delete name; } + + ~Material() + { + // NOTE: OldList::Delete() and FreeMaterial() is also used... + Free(); + } + + void OnCopy(Material b) + { + // NOTE: OnCopy is non-intuitive for class : struct ... + this = { }; + *this = *b; + name = CopyString(b.name); + } + + int OnCompare(Material b) + { + return memcmp(&opacity, &b.opacity, (byte *)((&LAST_MEMBER) + 1) - (byte *)&opacity); + } }; public class PrimitiveGroup : struct @@ -58,6 +128,7 @@ public class PrimitiveGroup : struct public: PrimitiveGroup prev, next; PrimitiveGroupType type; + int baseIndex; union { struct { union { uint16 * indices; uint * indices32; }; int nIndices; }; @@ -97,6 +168,7 @@ public struct PrimitiveSingle { public: PrimitiveGroupType type; + int baseIndex; union { struct { union { uint16 * indices; uint * indices32; }; int nIndices; }; @@ -110,12 +182,26 @@ private: Plane plane; }; +public struct MeshPart +{ + uint64 id; + uint start; + uint count; +}; + public class Mesh : struct { public: property Pointf * texCoords { get { return texCoords; } set { texCoords = value; } }; property int nVertices { get { return nVertices; } set { nVertices = value; } }; property Vector3Df * vertices { get { return vertices; } set { vertices = value; } }; + property int nIndices { get { return nIndices; } set { nIndices = value; } }; +#if !defined(ECERE_NOGL) + property GLMB meab { get { return meab; } set { meab = value; } }; +#endif + property int baseIndex { get { return baseIndex; } set { baseIndex = value; } }; + property int baseVertex { get { return baseVertex; } set { baseVertex = value; } }; + property uint32 * indices { get { return indices; } set { indices = value; } }; property Vector3Df * normals { get { return normals; } set { normals = value; } }; property Vector3Df * tangents { get { return tangents; } set { tangents = value; } }; property ColorRGBAf * colors { get { return colors; } set { colors = value; } }; @@ -123,6 +209,10 @@ public: property OldList groups { get { value = groups; } }; property MeshFeatures flags { get { return flags; } set { flags = value; } }; + // For intra-model attribution; in groups order, assuming triangles + // REVIEW: Should parts be in PrimitiveGroup instead? Picking and 'baseIndex' not currently supported? + property Array parts { get { return parts; } set { parts = value; } }; + void Free(MeshFeatures what) { if(this) @@ -132,19 +222,43 @@ public: if(!what) { int c; + bool ownMEAB = flags.ownMEAB; - flags = 0; + flags = 0; // Drivers relied on this being set to 0 before freeing if(driver) driver.FreeMesh(displaySystem, this); for(;(group = groups.first);) FreePrimitiveGroup(group); - for(c = 0; cdata) + { +#if !defined(ECERE_NOGL) + if(meab) + { + int baseIndex = prim->baseIndex; + ((OGLIndices)prim->data).buffer.buffer = 0; + if(baseIndex != -1) + { + uint iSize = prim->type.indices32bit ? sizeof(uint) : sizeof(uint16); + meab.freeBlock(BlockEntry { baseIndex * iSize, (baseIndex + prim->nIndices) * iSize-1 }); + prim->baseIndex = -1; + } + } +#endif + driver.FreeIndices(displaySystem, primitives[c]); + } + if(!prim->type.vertexRange) + delete prim->indices; } + // Free MEAB if it was allocated specifically for this mesh +#if !defined(ECERE_NOGL) + if(ownMEAB) meab.Free(), delete meab; +#endif + delete indices; delete primitives; nPrimitives = 0; nVertices = 0; @@ -157,6 +271,7 @@ public: if(driver) driver.FreeMesh(displaySystem, this); } + delete parts; } } @@ -169,6 +284,7 @@ public: if(driver.AllocateMesh == DisplayDriver::AllocateMesh) driver = (subclass(DisplayDriver))class(LFBDisplayDriver); if(driver.AllocateMesh(displaySystem, this, what, nVertices)) { + what.memAllocOnly = false; flags |= what; this.nVertices = nVertices; if(Lock(what)) @@ -205,7 +321,24 @@ public: if(this && group) { if(group.data) - driver.FreeIndices(displaySystem, group.data); + { +#if !defined(ECERE_NOGL) + if(meab) + { + int baseIndex = group.baseIndex; + ((OGLIndices)group.data).buffer.buffer = 0; + if(baseIndex != -1) + { + uint iSize = group.type.indices32bit ? sizeof(uint) : sizeof(uint16); + meab.freeBlock(BlockEntry { baseIndex * iSize, (baseIndex + group.nIndices) * iSize-1 }); + group.baseIndex = -1; + } + } +#endif + driver.FreeIndices(displaySystem, (PrimitiveSingle *)&group.type); + } + if(!group.type.vertexRange) + delete group.indices; groups.Delete(group); } } @@ -221,8 +354,9 @@ public: if(!(flags.vertexRange)) { group.nIndices = nIndices; - if(driver) + if(!flags.sharedIndices && driver) { + group.indices = (void *)(flags.indices32bit ? new uint32[nIndices] : new uint16[nIndices]); group.data = driver.AllocateIndices(displaySystem, nIndices, flags.indices32bit); if(group.data) { @@ -246,9 +380,9 @@ public: bool result = false; if(this && group) { - if(group.data) - group.indices = driver.LockIndices(displaySystem, group.data); - if(group.indices || group.type.vertexRange) + if(group.type.vertexRange) + result = true; + else if(group.data && driver.LockIndices(displaySystem, (PrimitiveSingle *)&group.type)) result = true; } return result; @@ -256,10 +390,21 @@ public: void UnlockPrimitiveGroup(PrimitiveGroup group) { - if(this && group && group.data) + if(this && group) { - driver.UnlockIndices(displaySystem, group.data, group.type.indices32bit, group.nIndices); - //group.indices = null; + bool shareIndicesTweak = false; + if(group.type.sharedIndices && !group.indices && indices) + { + shareIndicesTweak = true; + group.indices = (uint16 *)((byte *)indices + (group.baseIndex * (group.type.indices32bit ? 4 : 2))); + } +#if !defined(ECERE_NOGL) + driver.UnlockIndices(displaySystem, (PrimitiveSingle *)&group.type, group.type.indices32bit, group.nIndices, meab); +#else + driver.UnlockIndices(displaySystem, (PrimitiveSingle *)&group.type, group.type.indices32bit, group.nIndices, null); +#endif + if(shareIndicesTweak) + group.indices = null; } } @@ -268,8 +413,25 @@ public: if(this && primitive) { if(primitive.data) - driver.FreeIndices(displaySystem, primitive.data); - primitive.data = null; + { +#if !defined(ECERE_NOGL) + if(meab) + { + int baseIndex = primitive.baseIndex; + ((OGLIndices)primitive.data).buffer.buffer = 0; + if(baseIndex != -1) + { + uint iSize = primitive.type.indices32bit ? sizeof(uint) : sizeof(uint16); + meab.freeBlock(BlockEntry { baseIndex * iSize, (baseIndex + primitive.nIndices) * iSize-1 }); + primitive.baseIndex = -1; + } + } +#endif + driver.FreeIndices(displaySystem, primitive); + primitive.data = null; + } + if(!primitive.type.vertexRange) + delete primitive.indices; } } @@ -279,6 +441,8 @@ public: if(this && primitive) { primitive.type = flags; + if(!flags.vertexRange) + primitive.indices = (void *)(flags.indices32bit ? new uint32[nIndices] : new uint16[nIndices]); primitive.data = driver.AllocateIndices(displaySystem, nIndices, flags.indices32bit); primitive.nIndices = nIndices; if(primitive.data) @@ -296,8 +460,11 @@ public: { if(this && primitive) { - driver.UnlockIndices(this.displaySystem, primitive.data, primitive.type.indices32bit, primitive.nIndices); - //primitive.indices = null; +#if !defined(ECERE_NOGL) + driver.UnlockIndices(this.displaySystem, primitive, primitive.type.indices32bit, primitive.nIndices, meab); +#else + driver.UnlockIndices(this.displaySystem, primitive, primitive.type.indices32bit, primitive.nIndices, null); +#endif } } @@ -306,26 +473,49 @@ public: bool result = false; if(this && primitive) { - primitive.indices = driver.LockIndices(displaySystem, primitive.data); - if(primitive.indices) + if(driver.LockIndices(displaySystem, primitive)) result = true; } return result; } + // TODO: Make this a runtime option to force smoothing! + // #define NORMALS_MERGE_VERTICES + void ComputeNormals(void) + { + ComputeNormals2(true, true); + } + + void ComputeNormals2(bool computeNormals, bool computeTangents) { int c; - int * numShared = new0 int[nVertices]; + //int * numShared = new0 int[nVertices]; double * weightSum = new0 double[nVertices]; PrimitiveGroup group; - if(Allocate({ normals = true, tangents = texCoords != null }, nVertices, displaySystem)) + if(Allocate({ interleaved = flags.interleaved, + normals = true, tangents = (texCoords != null || (flags.texCoords1 && flags.interleaved)) && + computeTangents }, nVertices, displaySystem)) { - Vector3Df * normals = this.normals; +#ifdef NORMALS_MERGE_VERTICES + Map ixMap { }; + Map vMap { }; +#endif + float * vertices = (float *)this.vertices; + float * normals = flags.interleaved ? vertices + 3 : (float *)this.normals; + float * texCoords = flags.interleaved ? vertices + 5 : (float *)this.texCoords; + uint vStride = flags.interleaved ? 8 : 3, tStride = flags.interleaved ? 8 : 2; Vector3Df * tangents = this.tangents; - Pointf * texCoords = this.texCoords; - FillBytes(normals, 0, nVertices * sizeof(Vector3Df)); + int i; + if(computeNormals) + { + if(vStride == 3) + FillBytes(normals, 0, nVertices * sizeof(Vector3Df)); + else + for(i = 0; i < nVertices; i++) + memset(&normals[i * vStride], 0, sizeof(Vector3Df)); + } if(tangents) FillBytes(tangents, 0, 2*nVertices * sizeof(Vector3Df)); for(group = groups.first; group; group = group.next) @@ -334,9 +524,10 @@ public: int offset = 0; int strip = 0; int nPoints, nIndex; - uint16 * indices16 = group.indices; - uint32 * indices32 = group.indices32; - bool i32bit = group.type.indices32bit; + bool i32Bit = group.type.indices32bit; + uint32 * indices32 = i32Bit ? + (group.type.sharedIndices && this.indices ? this.indices + group.baseIndex : group.indices32) : null; + uint16 * indices16 = i32Bit ? null : group.indices; if(group.type.primitiveType == triangles) nIndex = nPoints = 3; @@ -359,39 +550,54 @@ public: else continue; */ - for(c = offset; cAdd((Vector3Df *)&normals[ix0 * vStride], planeNormal); //numShared[ix0]++; + ((Vector3Df *)&normals[ix1 * vStride])->Add((Vector3Df *)&normals[ix1 * vStride], planeNormal); //numShared[ix1]++; + ((Vector3Df *)&normals[ix2 * vStride])->Add((Vector3Df *)&normals[ix2 * vStride], planeNormal); //numShared[ix2]++; + } + weightSum[ix0] += 1.0; // TODO: Review weightSums + weightSum[ix1] += 1.0; + weightSum[ix2] += 1.0; } else if(group.type.primitiveType == triStrip || group.type.primitiveType == quadStrip) { - plane.FromPointsf(vertices[group.indices[c-1-strip]], - vertices[group.indices[c-2+strip]], - vertices[group.indices[c]]); + // TODO: Tangents not handled here, compute weights not done here + uint ix0 = (i32Bit ? indices32[c-1-strip] : indices16[c-1-strip]); + uint ix1 = (i32Bit ? indices32[c-2+strip] : indices16[c-2+strip]); + uint ix2 = (i32Bit ? indices32[c] : indices16[c]); + plane.FromPointsf( + (Vector3Df *)&vertices[ix0 * vStride], + (Vector3Df *)&vertices[ix1 * vStride], + (Vector3Df *)&vertices[ix2 * vStride]); planeNormal = { (float) plane.normal.x, (float) plane.normal.y, (float) plane.normal.z }; - normals[group.indices[c-1-strip]].Add(normals[group.indices[c-1-strip]], planeNormal); - numShared[group.indices[c-1-strip]]++; - normals[group.indices[c-2+strip]].Add(normals[group.indices[c-2+strip]], planeNormal); - numShared[group.indices[c-2+strip]]++; - normals[group.indices[c]].Add(normals[group.indices[c]], planeNormal); - numShared[group.indices[c]]++; + if(computeNormals) + { + ((Vector3Df *)&normals[ix0 * vStride])->Add((Vector3Df *)&normals[ix0 * vStride], planeNormal); //numShared[ix0]++; + ((Vector3Df *)&normals[ix1 * vStride])->Add((Vector3Df *)&normals[ix1 * vStride], planeNormal); //numShared[ix1]++; + ((Vector3Df *)&normals[ix2 * vStride])->Add((Vector3Df *)&normals[ix2 * vStride], planeNormal); //numShared[ix2]++; + } + weightSum[ix0] += 1.0; // TODO: Review weightSums + weightSum[ix1] += 1.0; + weightSum[ix2] += 1.0; strip ^= 1; } @@ -399,67 +605,95 @@ public: { if(group.type.vertexRange) { - plane.FromPointsf(vertices[c+2], - vertices[c+1], - vertices[c]); + // TODO: Tangents not handled here, compute weights not done here + plane.FromPointsf((Vector3Df *)&vertices[(c+2) * vStride], + (Vector3Df *)&vertices[(c+1) * vStride], + (Vector3Df *)&vertices[ c * vStride]); planeNormal = { (float) plane.normal.x, (float) plane.normal.y, (float) plane.normal.z }; - for(i = c; iAdd((Vector3Df *)&normals[ix * vStride], planeNormal); + //numShared[ix]++; + weightSum[ix] += 1.0; // TODO: Review weightSums + weightSum[ix] += 1.0; + weightSum[ix] += 1.0; + } } else { Vector3D edges[4], rEdges[4]; double weights[4]; - computeNormalWeights(nIndex, vertices, indices32, i32bit, c, weights, edges, rEdges); + computeNormalWeights(nIndex, vertices, vStride, indices32, i32Bit, c, weights, edges, rEdges); - plane.FromPointsf(vertices[i32bit ? indices32[c+2] : indices16[c+2]], - vertices[i32bit ? indices32[c+1] : indices16[c+1]], - vertices[i32bit ? indices32[c] : indices16[c]]); + plane.FromPointsf((Vector3Df *)&vertices[vStride * (i32Bit ? indices32[c+2] : indices16[c+2])], + (Vector3Df *)&vertices[vStride * (i32Bit ? indices32[c+1] : indices16[c+1])], + (Vector3Df *)&vertices[vStride * (i32Bit ? indices32[c ] : indices16[c ])]); planeNormal = { (float) plane.normal.x, (float) plane.normal.y, (float) plane.normal.z }; - for(i = c; ix - p0->x, p1->y - p0->y, p1->z - p0->z }; Vector3D v02 { p2->x - p0->x, p2->y - p0->y, p2->z - p0->z }; Pointf t01 { t1->x - t0->x, t1->y - t0->y }; Pointf t02 { t2->x - t0->x, t2->y - t0->y }; - //if(Abs(t01.x) > 0.99) t01.x = 0; - //if(Abs(t02.x) > 0.99) t02.x = 0; - - double f = w / (t01.x * t02.y - t02.x * t01.y); - Vector3Df * tan1 = &tangents[index*2+0]; - Vector3Df * tan2 = &tangents[index*2+1]; - tan1->x += f * (v01.x * t02.y - v02.x * t01.y); - tan1->y += f * (v01.y * t02.y - v02.y * t01.y); - tan1->z += f * (v01.z * t02.y - v02.z * t01.y); - - tan2->x += f * (v02.x * t01.x - v01.x * t02.x); - tan2->y += f * (v02.y * t01.x - v01.y * t02.x); - tan2->z += f * (v02.z * t01.x - v01.z * t02.x); + float ff = (t01.x * t02.y - t02.x * t01.y); + if(ff) + { + float f = (float)(w / ff); + Vector3Df * tan1 = &tangents[index*2+0]; + Vector3Df * tan2 = &tangents[index*2+1]; + tan1->x += f * (v01.x * t02.y - v02.x * t01.y); + tan1->y += f * (v01.y * t02.y - v02.y * t01.y); + tan1->z += f * (v01.z * t02.y - v02.z * t01.y); + tan2->x += f * (v01.x * t02.y - v02.x * t01.y); + tan2->y += f * (v01.y * t02.y - v02.y * t01.y); + tan2->z += f * (v01.z * t02.y - v02.z * t01.y); + } } } } @@ -471,12 +705,19 @@ public: { int i; PrimitiveSingle * primitive = &primitives[c]; - Plane plane; Vector3Df planeNormal; - plane.FromPointsf(vertices[primitive->indices[2]], - vertices[primitive->indices[1]], - vertices[primitive->indices[0]]); + bool i32Bit = primitive->type.indices32bit; + double weights[4]; + Vector3D edges[4], rEdges[4]; + uint32 * indices32 = i32Bit ? // baseIndex set but not used for Singles? + (primitive->type.sharedIndices && this.indices ? this.indices /*+ primitive->baseIndex*/ : primitive->indices32) : + null; + uint16 * indices16 = i32Bit ? null : primitive->indices; + + plane.FromPointsf((Vector3Df *)&vertices[vStride * (i32Bit ? indices32[2] : indices16[2])], + (Vector3Df *)&vertices[vStride * (i32Bit ? indices32[1] : indices16[1])], + (Vector3Df *)&vertices[vStride * (i32Bit ? indices32[0] : indices16[0])]); planeNormal = { (float) plane.normal.x, (float) plane.normal.y, (float) plane.normal.z }; if(primitive->material.flags.doubleSided && plane.d < 0) @@ -485,29 +726,106 @@ public: planeNormal.y *= -1; planeNormal.z *= -1; } + // baseIndex set but not used for Singles? + computeNormalWeights(primitive->nIndices, vertices, vStride, indices32, + i32Bit, 0 /*primitive->baseIndex*/, weights, edges, rEdges); for(i = 0; inIndices; i++) { - normals[primitive->indices[i]].Add(normals[primitive->indices[i]], planeNormal); - numShared[primitive->indices[i]] ++; + uint ix = primitive->indices[i]; + double w = weights[i]; + uint index = i32Bit ? indices32[i] : indices16[i]; + +#ifdef NORMALS_MERGE_VERTICES + index = resolveIndex(index, vMap, ixMap, (Vector3Df *)&vertices[vStride * index], plane); +#endif + + weightSum[ix] += w; // TODO: Review weightSums + + if(computeNormals) + { + ((Vector3Df *)&normals[vStride * ix])->Add((Vector3Df *)&normals[vStride * ix], planeNormal); + } + + if(tangents) + { + uint ix0 = i; + uint prev = i ? i - 1 : primitive->nIndices-1; + uint next = i < primitive->nIndices-1 ? i + 1 : 0; + uint ix1 = i32Bit ? indices32[next] : indices16[next]; + uint ix2 = i32Bit ? indices32[prev] : indices16[prev]; + Vector3Df * p0 = (Vector3Df *)&vertices[vStride * ix0]; + Vector3Df * p1 = (Vector3Df *)&vertices[vStride * ix1]; + Vector3Df * p2 = (Vector3Df *)&vertices[vStride * ix2]; + Pointf * t0 = (void *)&texCoords[tStride * ix0]; // FIXME: (Pointf *) causes bad .sym + Pointf * t1 = (void *)&texCoords[tStride * ix1]; + Pointf * t2 = (void *)&texCoords[tStride * ix2]; + Vector3D v01 { p1->x - p0->x, p1->y - p0->y, p1->z - p0->z }; + Vector3D v02 { p2->x - p0->x, p2->y - p0->y, p2->z - p0->z }; + Pointf t01 { t1->x - t0->x, t1->y - t0->y }; + Pointf t02 { t2->x - t0->x, t2->y - t0->y }; + float ff = t01.x * t02.y - t02.x * t01.y; + if(ff) + { + float f = (float)(w / ff); + Vector3Df * tan1 = &tangents[index*2+0]; + Vector3Df * tan2 = &tangents[index*2+1]; + + tan1->x += f * (v01.x * t02.y - v02.x * t01.y); + tan1->y += f * (v01.y * t02.y - v02.y * t01.y); + tan1->z += f * (v01.z * t02.y - v02.z * t01.y); + + tan2->x += f * (v02.x * t01.x - v01.x * t02.x); + tan2->x += f * (v02.y * t01.x - v01.y * t02.x); + tan2->x += f * (v02.z * t01.x - v01.z * t02.x); + } + } + //numShared[ix]++; } } for(c = 0; cScale(n, s), n->Normalize(n); - if(tangents) + if(!weightSum[c]) + s = 1.0; // Unused vertices following merging? + if(computeNormals) + { + Vector3Df * n = (Vector3Df *)&normals[vStride * c]; + n->Scale(n, s), n->Normalize(n); + } + if(computeTangents && tangents) { Vector3Df * t1 = &tangents[2*c], * t2 = &tangents[2*c+1]; t1->Scale(t1, s), t1->Normalize(t1); t2->Scale(t2, s), t2->Normalize(t2); } } - delete numShared; + +#ifdef NORMALS_MERGE_VERTICES + if(computeNormals) + for(i = 0; i < nVertices; i++) + { + uint ix = ixMap[i]; + *(Vector3Df *)&normals[i * vStride] = *(Vector3Df *)&normals[ix * vStride]; + } + + if(computeTangents && tangents) + { + for(i = 0; i < nVertices; i++) + { + uint ix = ixMap[i]; + tangents[2*i] = tangents[2*ix]; + tangents[2*i+1] = tangents[2*ix+1]; + } + } + delete ixMap; + delete vMap; +#endif + + //delete numShared; delete weightSum; - Unlock({ normals = true, tangents = true }); + Unlock({ interleaved = flags.interleaved, normals = computeNormals, tangents = computeTangents }); } } @@ -525,6 +843,66 @@ public: } } + bool UnapplyTranslucency(Object object) + { + bool result = false; + if(this) + { + int c; + + // Merge non translucent primitives into groups + for(c = 0; cmaterial || !object) ? primitive->material : object.material; + if(true) //!material || !(material.flags.translucent)) + { + int t; + PrimitiveGroup group; + int nIndices = primitive->nIndices; + for(t = c+1; ttype == primitive->type && prim->material == primitive->material) + nIndices += prim->nIndices; + } + group = AddPrimitiveGroup(primitive->type, nIndices); + if(group) + { + bool use32 = group.type.indices32bit; + nIndices = 0; + group.material = material; + for(t = c; ttype && group.material == primitive->material) + { + if(group.type.sharedIndices) + CopyBytesBy4(indices + group.baseIndex + nIndices, primitive->indices32, primitive->nIndices); + else if(use32) + CopyBytesBy4(group.indices32 + nIndices, primitive->indices32, primitive->nIndices); + else + CopyBytesBy2(group.indices + nIndices, primitive->indices, primitive->nIndices); + nIndices += primitive->nIndices; + CopyBytes(primitives + t, primitives + t + 1, (nPrimitives - t - 1) * sizeof(PrimitiveSingle)); + nPrimitives--; + t--; + } + } + UnlockPrimitiveGroup(group); + } + } + else + c++; + } + primitives = renew primitives PrimitiveSingle[this.nPrimitives]; + result = true; + if(object) + object.flags.translucent = nPrimitives ? true : false; + } + return result; + } + bool ApplyTranslucency(Object object) { bool result = false; @@ -552,6 +930,7 @@ public: group = AddPrimitiveGroup(primitive->type, nIndices); if(group) { + bool use32 = primitive->type.indices32bit; nIndices = 0; group.material = material; for(t = c; ttype && group.material == primitive->material) { - CopyBytesBy2(group.indices + nIndices,primitive->indices,primitive->nIndices); - nIndices +=primitive->nIndices; + if(use32) + CopyBytesBy4(group.indices32 + nIndices, primitive->indices32, primitive->nIndices); + else + CopyBytesBy2(group.indices + nIndices, primitive->indices, primitive->nIndices); + nIndices += primitive->nIndices; CopyBytes(primitives + t, primitives + t + 1, (nPrimitives - t - 1) * sizeof(PrimitiveSingle)); nPrimitives--; t--; @@ -607,72 +989,147 @@ public: primitives = renew primitives PrimitiveSingle[this.nPrimitives + nPrimitives]; - for(c = offset; cindices[0] = (uint16)(group.first + c); - primitive->indices[1] = (uint16)(group.first + c+1); - primitive->indices[2] = (uint16)(group.first + c+2); - } - if(group.type.primitiveType == quads) - primitive->indices[3] = (uint16)(group.first + c+3); + PrimitiveSingle * primitive = &primitives[this.nPrimitives++]; - if(group.type.primitiveType == triFan) + if(AllocatePrimitive(primitive, group.type & ~PrimitiveGroupType { vertexRange = true }, nPoints)) + { + if(group.type.vertexRange) { - primitive->indices[0] = (uint16)group.first; - primitive->indices[1] = (uint16)(group.first + c-1); - primitive->indices[2] = (uint16)(group.first + c); + if(group.type.primitiveType == triangles || group.type.primitiveType == quads) + { + primitive->indices32[0] = (uint32)(group.first + c); + primitive->indices32[1] = (uint32)(group.first + c+1); + primitive->indices32[2] = (uint32)(group.first + c+2); + } + if(group.type.primitiveType == quads) + primitive->indices32[3] = (uint32)(group.first + c+3); + + if(group.type.primitiveType == triFan) + { + primitive->indices32[0] = (uint32)group.first; + primitive->indices32[1] = (uint32)(group.first + c-1); + primitive->indices32[2] = (uint32)(group.first + c); + } + else if(group.type.primitiveType == triStrip) + { + primitive->indices32[0] = (uint32)(group.first + c-1-strip); + primitive->indices32[1] = (uint32)(group.first + c-2+strip); + primitive->indices32[2] = (uint32)(group.first + c); + strip ^= 1; + } } - else if(group.type.primitiveType == triStrip) + else { - primitive->indices[0] = (uint16)(group.first + c-1-strip); - primitive->indices[1] = (uint16)(group.first + c-2+strip); - primitive->indices[2] = (uint16)(group.first + c); - strip ^= 1; + uint32 * indices32 = group.type.sharedIndices ? indices + group.baseIndex : group.indices32; + if(group.type.primitiveType == triangles || group.type.primitiveType == quads) + CopyBytesBy4(primitive->indices32, indices32 + c, nIndex); + + if(group.type.primitiveType == triFan) + { + primitive->indices32[0] = indices32[0]; + primitive->indices32[1] = indices32[c-1]; + primitive->indices32[2] = indices32[c]; + } + else if(group.type.primitiveType == triStrip) + { + primitive->indices32[0] = indices32[c-1-strip]; + primitive->indices32[1] = indices32[c-2+strip]; + primitive->indices32[2] = indices32[c]; + strip ^= 1; + } } + primitive->material = group.material; + + primitive->plane.FromPointsf( + vertices[primitive->indices32[2]], + vertices[primitive->indices32[1]], + vertices[primitive->indices32[0]]); + + primitive->middle.Add(vertices[primitive->indices32[0]], vertices[primitive->indices32[1]]); + primitive->middle.Add(primitive->middle, vertices[primitive->indices32[2]]); + if(group.type == quads) + primitive->middle.Add(primitive->middle, vertices[primitive->indices32[3]]); + primitive->middle.x /= nPoints; + primitive->middle.y /= nPoints; + primitive->middle.z /= nPoints; + + UnlockPrimitive(primitive); } - else - { - if(group.type.primitiveType == triangles || group.type.primitiveType == quads) - CopyBytesBy2(primitive->indices, group.indices + c, nIndex); + } + } + else + { + for(c = offset; cindices[0] = group.indices[0]; - primitive->indices[1] = group.indices[c-1]; - primitive->indices[2] = group.indices[c]; + if(group.type.primitiveType == triangles || group.type.primitiveType == quads) + { + primitive->indices[0] = (uint16)(group.first + c); + primitive->indices[1] = (uint16)(group.first + c+1); + primitive->indices[2] = (uint16)(group.first + c+2); + } + if(group.type.primitiveType == quads) + primitive->indices[3] = (uint16)(group.first + c+3); + + if(group.type.primitiveType == triFan) + { + primitive->indices[0] = (uint16)group.first; + primitive->indices[1] = (uint16)(group.first + c-1); + primitive->indices[2] = (uint16)(group.first + c); + } + else if(group.type.primitiveType == triStrip) + { + primitive->indices[0] = (uint16)(group.first + c-1-strip); + primitive->indices[1] = (uint16)(group.first + c-2+strip); + primitive->indices[2] = (uint16)(group.first + c); + strip ^= 1; + } } - else if(group.type.primitiveType == triStrip) + else { - primitive->indices[0] = group.indices[c-1-strip]; - primitive->indices[1] = group.indices[c-2+strip]; - primitive->indices[2] = group.indices[c]; - strip ^= 1; + if(group.type.primitiveType == triangles || group.type.primitiveType == quads) + CopyBytesBy2(primitive->indices, group.indices + c, nIndex); + + if(group.type.primitiveType == triFan) + { + primitive->indices[0] = group.indices[0]; + primitive->indices[1] = group.indices[c-1]; + primitive->indices[2] = group.indices[c]; + } + else if(group.type.primitiveType == triStrip) + { + primitive->indices[0] = group.indices[c-1-strip]; + primitive->indices[1] = group.indices[c-2+strip]; + primitive->indices[2] = group.indices[c]; + strip ^= 1; + } } + primitive->material = group.material; + + primitive->plane.FromPointsf( + vertices[primitive->indices[2]], + vertices[primitive->indices[1]], + vertices[primitive->indices[0]]); + + primitive->middle.Add(vertices[primitive->indices[0]], vertices[primitive->indices[1]]); + primitive->middle.Add(primitive->middle, vertices[primitive->indices[2]]); + if(group.type == quads) + primitive->middle.Add(primitive->middle, vertices[primitive->indices[3]]); + primitive->middle.x /= nPoints; + primitive->middle.y /= nPoints; + primitive->middle.z /= nPoints; + + UnlockPrimitive(primitive); } - primitive->material = group.material; - - primitive->plane.FromPointsf( - vertices[primitive->indices[2]], - vertices[primitive->indices[1]], - vertices[primitive->indices[0]]); - - primitive->middle.Add(vertices[primitive->indices[0]], vertices[primitive->indices[1]]); - primitive->middle.Add(primitive->middle, vertices[primitive->indices[2]]); - if(group.type == quads) - primitive->middle.Add(primitive->middle, vertices[primitive->indices[3]]); - primitive->middle.x /= nPoints; - primitive->middle.y /= nPoints; - primitive->middle.z /= nPoints; - - UnlockPrimitive(primitive); } } FreePrimitiveGroup(group); @@ -696,6 +1153,134 @@ public: data = value; } +#if !defined(ECERE_NOGL) + bool Upload(DisplaySystem displaySystem, bool uploadTextures, GLMB mab, GLMB meab, int nAT, GLArrayTexture * mAT) + { + bool result = false; + PrimitiveGroup g; + bool clearData = false; + + if(!this.displaySystem) + { + driver = displaySystem.driver; + this.displaySystem = displaySystem; + clearData = true; + } + this.mab = mab; + this.meab = meab; + + Unlock(0); + + delete vertices; + delete texCoords; + delete normals; + delete colors; + delete lightVectors; + + for(g = groups.first; g; g = g.next) + { + Material mat = g.material; + if(!g.type.vertexRange) + { + if(clearData) + g.data = null; + UnlockPrimitiveGroup(g); + } + if(mat && uploadTextures) + { + if(nAT && mAT != null) + { + int i; + for(i = 0; i < Min(1, nAT); i++) + { + Bitmap bitmap = i == 0 ? mat.baseMap : null; + if(bitmap && bitmap.displaySystem != displaySystem) + { + Bitmap convBitmap = bitmap; + GLArrayTexture * at = &mAT[i]; + if(convBitmap.pixelFormat != pixelFormatRGBAGL && convBitmap.pixelFormat != pixelFormatETC2RGBA8) + convBitmap = bitmap.ProcessDD((bool)2, 0, false, 16384, false); //oglSystem.maxTextureSize, !capabilities.nonPow2Textures); + if(convBitmap) + { + if(!at->texture) + { +#if !defined(__EMSCRIPTEN__) && ((!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3)) + bool sRGB2Linear = bitmap.sRGB2Linear; + int internalFormat = convBitmap.pixelFormat == pixelFormatETC2RGBA8 ? + (sRGB2Linear ? GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : GL_COMPRESSED_RGBA8_ETC2_EAC) : + (sRGB2Linear ? GL_SRGB8_ALPHA8 : GL_RGBA8); + #else + int internalFormat = convBitmap.pixelFormat == pixelFormatETC2RGBA8 ? 0 : GL_RGBA; +#endif + + // TOCHECK: Shouldn't the overall bitmap width be set? + // Bitmap bmp = convBitmap.bitmaps && convBitmap.numMipMaps ? convBitmap.bitmaps[0] : convBitmap; + at->_init(convBitmap.numMipMaps ? convBitmap.numMipMaps : 1, + at->width ? at->width : MODELS_TEXTUREARRAY_SIZE, + at->height ? at->height : MODELS_TEXTUREARRAY_SIZE, + at->numLayers ? at->numLayers : 64, + internalFormat, + false); + } + + if(convBitmap.bitmaps) + { + int layer = at->allocateLayer(0); + int j; + int numLevels = at->numLevels; + int skipLevel = Max(0, convBitmap.numMipMaps - numLevels); + if(layer != -1) + { + for(j = 0; j < convBitmap.numMipMaps; j++) + { + Bitmap bmp = convBitmap.bitmaps[j]; + if(bmp) + { + int level = j - skipLevel; + if(level >= 0) + { + if(convBitmap /*bmp*/.pixelFormat == pixelFormatETC2RGBA8) + at->setLayerCompressed(level, 0, 0, layer, bmp.picture, bmp.sizeBytes, 0); + else + at->setLayer(level, 0, 0, layer, bmp.picture, 0); + } +#ifdef ETC2_COMPRESS + if(convBitmap.pixelFormat == pixelFormatETC2RGBA8) + { + etc2Free(bmp.picture); + bmp.picture = null; + } + else +#endif + delete bmp.picture; + delete bmp; + convBitmap.bitmaps[j] = null; // TOCHECK: ? + } + } + delete convBitmap.bitmaps; + + bitmap.displaySystem = displaySystem; + bitmap.driver = displaySystem.driver; + bitmap.driverData = (void *)(intptr)layer; // TOFIX: *not* a texture in this case! Don't free as one. + } + } + } + + if(convBitmap != bitmap) + delete convBitmap; + } + } + } + else if(mat.baseMap && mat.baseMap.displaySystem != displaySystem) + mat.baseMap.MakeMipMaps(displaySystem); + } + delete g.indices; + } + result = true; + return result; + } +#endif + private: void SetMinMaxRadius(void) @@ -703,13 +1288,17 @@ private: int c; float xRadius, yRadius, zRadius; + float * v = (float *)vertices; + int increment = flags.interleaved ? 8 : 3; + + if(!v) return; min = { MAXFLOAT, MAXFLOAT, MAXFLOAT }; max = {-MAXFLOAT,-MAXFLOAT,-MAXFLOAT }; - for(c = 0; c 1E20 || x < -1E20 || y > 1E20 || y < -1E20 || z > 1E20 || z < -1E20); else @@ -762,7 +1351,7 @@ private: Pointf * texCoords; ColorRGBAf * colors; ColorRGB * lightVectors; - OldList groups; + public OldList groups; // To be accessible faster... int nPrimitives; PrimitiveSingle * primitives; Vector3Df min, max; @@ -773,9 +1362,18 @@ private: DisplaySystem displaySystem; subclass(DisplayDriver) driver; void * data; +#if !defined(ECERE_NOGL) + GLMB mab, meab; +#endif + uint baseVertex; + Array parts; + + // Mesh-wide indices + uint * indices; + int baseIndex, nIndices; }; -void computeNormalWeights(int n, Vector3Df * vertices, uint * indices, bool ix32Bit, int base, double * weights, Vector3D * edges, Vector3D * rEdges) +void computeNormalWeights(int n, float * vertices, uint vStride, uint * indices, bool ix32Bit, int base, double * weights, Vector3D * edges, Vector3D * rEdges) { int i; for(i = 0; i < n; i++) @@ -789,7 +1387,7 @@ void computeNormalWeights(int n, Vector3Df * vertices, uint * indices, bool ix32 else ix0 = ((uint16*)indices)[base+ix0], ix1 = ((uint16*)indices)[base+ix1]; } - p0 = &vertices[ix0], p1 = &vertices[ix1]; + p0 = (Vector3Df *)&vertices[ix0 * vStride], p1 = (Vector3Df *)&vertices[ix1 * vStride]; edges[i] = { p1->x - p0->x, p1->y - p0->y, p1->z - p0->z }; edges[i].Normalize(edges[i]); rEdges[i].Scale(edges[i], -1); @@ -797,3 +1395,66 @@ void computeNormalWeights(int n, Vector3Df * vertices, uint * indices, bool ix32 for(i = 0; i < n; i++) weights[i] = acos(Min(1.0, Max(-1.0, edges[i].DotProduct(rEdges[i ? i-1 : n-1])))) / ((n-2) * Pi); } + +#ifdef NORMALS_MERGE_VERTICES +struct SharedVertex +{ + Vector3Df v, n; + int count; + + int OnCompare(SharedVertex b) + { + float dx = v.x - b.v.x; + float dy = v.y - b.v.y; + float dz = v.z - b.v.z; + double dot; + + if(dx > 0.00001) return 1; + if(dx <-0.00001) return -1; + if(dy > 0.00001) return 1; + if(dy <-0.00001) return -1; + if(dz > 0.00001) return 1; + if(dz <-0.00001) return -1; + + dot = n.DotProduct(b.n); + + if(dot > 0.40) + return 0; + if(n.x > b.n.x) return 1; + if(n.x < b.n.x) return -1; + if(n.y > b.n.y) return 1; + if(n.y < b.n.y) return -1; + if(n.z > b.n.z) return 1; + if(n.z < b.n.z) return -1; + + return 0; + } +}; + +static uint resolveIndex(uint index, Map vMap, Map ixMap, Vector3Df vertex, Plane plane) +{ + uint result; + //return index; + SharedVertex v { vertex, { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z }, 1 }; + MapIterator it { map = vMap }; + if(it.Index(v, true)) + { + SharedVertex k = it.key; + result = it.data; + // Update to the average... + it.Remove(); + k.n.Normalize( + { + (k.n.x * k.count + v.n.x)/(k.count+1), + (k.n.y * k.count + v.n.y)/(k.count+1), + (k.n.z * k.count + v.n.z)/(k.count+1) + }); + k.count++; + vMap[k] = result; + } + else + it.data = result = index; + ixMap[index] = result; + return result; +} +#endif diff --git a/ecere/src/gfx/3D/Object.ec b/ecere/src/gfx/3D/Object.ec index 56c68cc5ba..7151fb36f6 100644 --- a/ecere/src/gfx/3D/Object.ec +++ b/ecere/src/gfx/3D/Object.ec @@ -1,11 +1,16 @@ namespace gfx3D; +#if defined(__linux__) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__i386__) +asm(".symver pow,pow@GLIBC_2.2.5"); +#endif + import "Display" public enum FrustumPlacement { outside, inside, intersecting }; public class ObjectFormat { +public: // Lack of public here causes strange errors defining multiple foramts in same file and invoking Load class_data const char * extension; class_property const char * extension { @@ -13,7 +18,9 @@ public class ObjectFormat get { return class_data(extension); } } - virtual bool ::Load(Object object, const char * fileName, DisplaySystem displaySystem); + virtual bool ::Load(Object object, const char * fileName, DisplaySystem displaySystem, void * options); + virtual bool ::Save(Object object, const char * fileName, void * options); + virtual Array ::listTextures(File f, const char * fileName, void * options); }; // TODO: Review these: @@ -30,6 +37,74 @@ public struct Transform Vector3D position; Quaternion orientation; Vector3Df scaling; + + void getMatrix3x4f(float m[12]) + { + Quaternion q = orientation; + Vector3Df s = scaling; + double xx = q.x*q.x, yy = q.y*q.y, zz = q.z*q.z; + double xy = q.x*q.y, xz = q.x*q.z, yz = q.y*q.z; + double wx = q.w*q.x, wy = q.w*q.y, wz = q.w*q.z; + + m[ 0] = (float)(s.x * (1 - 2 * ( yy + zz ))); + m[ 1] = (float)(s.x * ( 2 * ( xy - wz ))); + m[ 2] = (float)(s.x * ( 2 * ( xz + wy ))); + + m[ 3] = (float)(s.y * ( 2 * ( xy + wz ))); + m[ 4] = (float)(s.y * (1 - 2 * ( xx + zz ))); + m[ 5] = (float)(s.y * ( 2 * ( yz - wx ))); + + m[ 6] = (float)(s.z * ( 2 * ( xz - wy ))); + m[ 7] = (float)(s.z * ( 2 * ( yz + wx ))); + m[ 8] = (float)(s.z * (1 - 2 * ( xx + yy ))); + + m[ 9] = (float)position.x; + m[10] = (float)position.y; + m[11] = (float)position.z; + } + + property Matrix + { + set + { + Vector3D s; + Matrix r; + // bool flipWindings = Sgn(s.x) * Sgn(s.y) * Sgn(s.z) < 0; + value.extractScaling(r, s); + scaling = { (float)s.x, (float)s.y, (float)s.z }; + orientation = r.orientation; + position = value.translation; + } + get + { + double * m = value.array; + Quaternion q = orientation; + Vector3Df s = scaling; + double xx = q.x*q.x, yy = q.y*q.y, zz = q.z*q.z; + double xy = q.x*q.y, xz = q.x*q.z, yz = q.y*q.z; + double wx = q.w*q.x, wy = q.w*q.y, wz = q.w*q.z; + + m[ 0] = s.x * (1 - 2 * ( yy + zz )); + m[ 1] = s.x * ( 2 * ( xy - wz )); + m[ 2] = s.x * ( 2 * ( xz + wy )); + m[ 3] = 0; + + m[ 4] = s.y * ( 2 * ( xy + wz )); + m[ 5] = s.y * (1 - 2 * ( xx + zz )); + m[ 6] = s.y * ( 2 * ( yz - wx )); + m[ 7] = 0; + + m[ 8] = s.z * ( 2 * ( xz - wy )); + m[ 9] = s.z * ( 2 * ( yz + wx )); + m[10] = s.z * (1 - 2 * ( xx + yy )); + m[11] = 0; + + m[12] = position.x; + m[13] = position.y; + m[14] = position.z; + m[15] = 1; + } + } }; /*static float ease(float t, float a, float b) @@ -50,22 +125,23 @@ public struct Transform return 1.0f - (k/b)*t*t; }*/ -public enum FrameTrackType : uint16 { position = 1, rotation, scaling, fov, roll, colorChange, morph, hotSpot, fallOff, hide }; +public enum FrameTrackType : uint16 { position = 1, rotation, scaling, fov, roll, colorChange, morph, hotSpot, fallOff, hide, rYaw, rPitch, rRoll }; public class FrameTrackBits { +public: FrameTrackType type; bool loop:1; }; public struct FrameKey { - unsigned int frame; + uint frame; float tension, continuity, bias; float easeFrom, easeTo; union { - Vector3Df position; + Vector3Df position; // TOCHECK: Should this be made a Vector3D? Quaternion orientation; Vector3Df scaling; float roll; @@ -81,10 +157,12 @@ enum SplinePart { splinePoint, splineA, splineB }; public class FrameTrack : struct { FrameTrack prev, next; +public: FrameTrackBits type; - unsigned int numKeys; + uint numKeys; FrameKey * keys; +private: void Free(void) { delete keys; @@ -95,6 +173,123 @@ public class FrameTrack : struct Free(); } + // REVIEW: This method does not use the key setting for angles? + float GetAngle(SplinePart what, unsigned int n) + { + float value; + FrameKey *kn_1, *kn, *kn1; + float pn_1, pn, pn1; + int d1, d2; + + kn = &keys[n]; + pn = kn->roll; + + if(what == splinePoint) + value = pn; + else + { + if(n == 0) + { + kn1 = &keys[1]; + pn1 = kn1->roll; + + if(numKeys == 2) + { + value = pn1 - pn; + //value *= 1.0f - kn->tension; + return value; + } + if(type.loop) + { + kn_1 = &keys[numKeys-2]; + d1 = keys[numKeys-1].frame - kn_1->frame; + d2 = kn1->frame - kn->frame; + } + else + { + float a1; + value = pn1 - pn; + value *= 1.5f; + + a1 = GetAngle(splineA, 1); + a1 *= 0.5f; + + value -= a1; + value *= 1.0f;// - kn->tension; + return value; + } + } + else if(n == numKeys-1) + { + kn_1 = &keys[n-1]; + pn_1 = kn_1->roll; + + if(numKeys == 2) + { + value = pn - pn_1; + value *= 1.0f;// - kn->tension; + return value; + } + if(type.loop) + { + kn1 = &keys[1]; + d1 = kn->frame - kn_1->frame; + d2 = kn1->frame - keys[0].frame; + } + else + { + float bn_1; + value = pn - pn_1; + value *= 1.5f; + + bn_1 = GetFloat(splineB, n-1); + bn_1 *= 0.5f; + + value -= bn_1; + value *= 1.0f;// - kn->tension; + return value; + } + } + else + { + kn_1 = &keys[n-1]; + kn1 = &keys[n+1]; + d1 = kn->frame - kn_1->frame; + d2 = kn1->frame - kn->frame; + } + { + float /*C, */adjust; + float part1, part2; + + pn_1 = kn_1->roll; + pn1 = kn1->roll; + + if(what == splineA) + { + //C = kn->continuity; + adjust = (float)d1; + } + else + { + //C = -kn->continuity; + adjust = (float)d2; + } + adjust /= d1 + d2; + adjust = 0.5f /*+ (1.0f - Abs(C))*/*(adjust - 0.5f); + + part1 = pn - pn_1; + part1 *= (1.0f /*+ kn->bias*/)*(1.0f /*- C*/); + + part2 = pn1 - pn; + part2 *= (1.0f /*- kn->bias*/)*(1.0f /*+ C*/); + + value = part1 + part2; + value *= (1.0f/* - kn->tension*/)*adjust; + } + } + return value; + } + float GetFloat(SplinePart what, unsigned int n) { float value; @@ -477,14 +672,75 @@ public class FrameTrack : struct } return value; } + + float InterpolateAngle(float prevValue, float nextValue, int prev, int next, float t) + { + float value; + if(!t) + value = prevValue; + else + { + float p1 = prevValue, p2 = nextValue; + + float r1 = GetAngle(splineB, prev); + float r2 = GetAngle(splineA, next); + + + if(p2 - p1 > 180) + p2 -= 360; + else if(p2 - p1 < -180) + p2 += 360; + + p1 *= 2*t*t*t - 3*t*t + 1; + p2 *= -2*t*t*t + 3*t*t; + + r1 *= t*t*t - 2*t*t + t; + r2 *= t*t*t - t*t; + + value = p1 + r1 + p2 + r2; + + //value = p1 * (1-t) + p2 * (t); + //value = p1 + (p2-p1) * t; + } + return value; + } +}; + +struct MaterialAndType +{ + Material material; + PrimitiveGroupType type; + + int OnCompare(MaterialAndType b) + { + if((uintptr)material < (uintptr)b.material) return -1; + if((uintptr)material > (uintptr)b.material) return 1; + if(type < b.type) return -1; + if(type > b.type) return 1; + return 0; + } }; -static bool FindMaterialAndType(Mesh mesh, Material material, PrimitiveGroupType type) +static int compareGroupMaterial(PrimitiveGroup a, PrimitiveGroup b, void * data) +{ + uintptr ma = (uintptr)a.material, mb = (uintptr)b.material; + if(ma < mb) return -1; + if(ma > mb) return 1; + return 0; +} + +static bool FindMaterialAndType(Map map, Mesh mesh, Material material, PrimitiveGroupType type) { + if(map.GetAtPosition({ material, type }, false, null)) + { + return true; + } + /* PrimitiveGroup group; for(group = mesh.groups.first; group; group = group.next) if(group.material == material && group.type == type) return true; + */ return false; } @@ -599,6 +855,11 @@ public: } void Duplicate(Object model) + { + Duplicate2(model, false); + } + + void Duplicate2(Object model, bool takeOwnership) { if(model) { @@ -606,7 +867,13 @@ public: name = CopyString(model.name); flags = model.flags; - flags.ownMesh = false; + if(flags.ownMesh) + { + if(takeOwnership) + model.flags.ownMesh = false; + else + flags.ownMesh = false; + } flags.transform = false; mesh = model.mesh; /* @@ -624,13 +891,13 @@ public: Object child { parent = this }; child.localMatrix = modelChild.localMatrix; child.transform = modelChild.transform; - child.Duplicate(modelChild); + child.Duplicate2(modelChild, takeOwnership); children.AddName(child); } } } - void Free(DisplaySystem displaySystem) + void Free(const DisplaySystem displaySystem) { if(this) { @@ -648,10 +915,12 @@ public: } if(flags.ownMesh && mesh) { - DisplaySystem meshDisplaySystem = mesh.displaySystem; + //DisplaySystem meshDisplaySystem = mesh.displaySystem; mesh.Free(0); + /* if(meshDisplaySystem) meshDisplaySystem.RemoveMesh(mesh); + */ delete mesh; } @@ -661,7 +930,35 @@ public: } } + Array ::listTextures(File file, const char * fileName, const char * type, void * options) + { + char ext[MAX_EXTENSION]; + subclass(ObjectFormat) format; + OldLink link; + Array textures = null; + + if(!type && fileName) + type = strlwr(GetExtension(fileName, ext)); + + for(link = class(ObjectFormat).derivatives.first; link; link = link.next) + { + format = link.data; + if(format.extension && !strcmp(format.extension, type)) + break; + } + if(!link) format = null; + + if(format) + textures = format.listTextures(file, fileName, options); + return textures; + } + bool Load(const char * fileName, const char * type, DisplaySystem displaySystem) + { + return LoadEx(fileName, type, displaySystem, null); + } + + bool LoadEx(const char * fileName, const char * type, DisplaySystem displaySystem, void * options) { char ext[MAX_EXTENSION]; subclass(ObjectFormat) format; @@ -681,7 +978,40 @@ public: if(format) { - if((format.Load(this, fileName, displaySystem))) + if((format.Load(this, fileName, displaySystem, options))) + result = true; + } + /*if(!result) + ErrorLogCode(GERR_LOAD_OBJECT_FAILED, fileName);*/ + return result; + } + + bool Save(const char * fileName, const char * type) + { + return SaveEx(fileName, type, null); + } + + bool SaveEx(const char * fileName, const char * type, void * options) + { + char ext[MAX_EXTENSION]; + subclass(ObjectFormat) format; + OldLink link; + bool result = false; + + if(!type && fileName) + type = strlwr(GetExtension(fileName, ext)); + + for(link = class(ObjectFormat).derivatives.first; link; link = link.next) + { + format = link.data; + if(format.extension && !strcmp(format.extension, type)) + break; + } + if(!link) format = null; + + if(format) + { + if((format.Save(this, fileName, options))) result = true; } /*if(!result) @@ -713,8 +1043,7 @@ public: for(child = children.first; child; child = child.next) { Object result = child.Find(name); - if(result) - return result; + if(result) return result; } } } @@ -746,7 +1075,7 @@ public: if(mesh) { FillBytes(mesh, 0, sizeof(class Mesh)); - displaySystem.AddMesh(mesh); + //displaySystem.AddMesh(mesh); } matrix.Identity(); return mesh; @@ -765,6 +1094,7 @@ public: if(result) object.parent = this; object.flags.transform = true; + object.flags.root = false; } return result; } @@ -780,8 +1110,37 @@ public: } } - // TODO: Add support to Merge Vertex Colors mesh feature + void AddFrameTrack(FrameTrack track) + { + tracks.Add(track); + } + + void Remove(Object child) + { + if(this && child && child.parent == this) + { + children.Remove(child); + child.parent = null; + } + } + + void Delete(Object child, DisplaySystem displaySystem) + { + if(this) + { + children.Remove(child); + child.Free(displaySystem); + delete child; + } + } + bool Merge(DisplaySystem displaySystem) + { + return _Merge(displaySystem, true); + } + + // TODO: Add support to Merge Vertex Colors mesh feature + private bool _Merge(DisplaySystem displaySystem, bool lastLevel) { bool result = false; @@ -794,6 +1153,8 @@ public: MeshFeatures flags = 0; Mesh objectMesh = this.flags.mesh ? mesh : null; bool freeMesh = this.flags.ownMesh; + Map map { }; + bool canMerge = true; // Count total number of vertices if(objectMesh) @@ -804,17 +1165,108 @@ public: for(child = children.first; child; child = child.next) { - child.Merge(displaySystem); + child._Merge(displaySystem, false); if(child.flags.mesh && child.mesh) { nVertices += child.mesh.nVertices; flags |= child.mesh.flags; this.flags.computeLightVectors |= child.flags.computeLightVectors; + + if(nVertices > 65535 || + fabs(child.localMatrix.m[3][0]) > 400 || + fabs(child.localMatrix.m[3][1]) > 400 || + fabs(child.localMatrix.m[3][2]) > 400) + canMerge = false; + } + else if(child.children.count) + canMerge = false; + } + if(!canMerge) + { + Object next; +#ifdef _DEBUG + printf("WARNING: More than 64k vertices or translation > 400 -- not merging\n"); +#endif + for(child = children.first; child; child = next) + { + next = child.next; + if(child.flags.mesh && child.mesh) + { + PrimitiveGroup group; + Mesh mesh = child.mesh; + int i; + if(!mesh.displaySystem && displaySystem) + { + mesh.driver = displaySystem.driver; + mesh.displaySystem = displaySystem; + for(i = 0; i < mesh.nPrimitives; i++) + mesh.UnlockPrimitive(mesh.primitives[i]); + for(group = mesh.groups.first; group; group = group.next) + { + if(!(group.type.vertexRange)) + { + // FIXME: GL driver specifics -- Should no longer need this; Should be able to merge now with baseVertex +#if !defined(ECERE_NOGL) + OGLIndices oglIndices { nIndices = group.nIndices }; + group.data = oglIndices; +#endif + } + mesh.UnlockPrimitiveGroup(group); + } + mesh.Unlock(0); + + delete *&mesh.vertices; + delete *&mesh.texCoords; + delete *&mesh.normals; + } + } + if(child.children.count) + { + Object c, n; + for(c = child.children.first; c; c = n) + { + n = c.next; + if(child.transform.position.x == 0 && + child.transform.position.y == 0 && + child.transform.position.z == 0 && + c.transform.position.x == 0 && + c.transform.position.y == 0 && + c.transform.position.z == 0) /* && + c.transform.orientation.x == 0 && + c.transform.orientation.y == 0 && + c.transform.orientation.z == 0 && + c.transform.scaling.x == 1 && + c.transform.scaling.y == 1 && + c.transform.scaling.z == 1)*/ + { + Quaternion angle; + Matrix m; + m.Multiply(c.localMatrix, child.localMatrix); + angle.Multiply(c.transform.orientation, child.transform.orientation); + + child.children.Remove(c); + Add(c); + c.localMatrix = m; + c.flags.localMatrixSet = true; + c.UpdateTransform(); + } + } + if(!child.children.count && !child.flags.mesh) + { + children.Remove(child); + delete child; + } + } } + delete map; + return false; } if(!nVertices) + { + delete map; return true; + } if(this.flags.camera) delete camera; @@ -822,36 +1274,35 @@ public: mesh = Mesh { }; this.flags.ownMesh = true; this.flags.mesh = true; - displaySystem.AddMesh(mesh); + //displaySystem.AddMesh(mesh); - if(mesh.Allocate(flags, nVertices, displaySystem)) + if(mesh.Allocate(flags, nVertices, lastLevel ? displaySystem : null)) { int c; int nTriangles = 0; int vertexOffset = 0; PrimitiveGroup group = null; - // Merge vertices - nVertices = 0; + + // Merge vertices if(objectMesh) { - for(c = 0; cMultMatrix((Vector3Df *)cv, matrix); + ((Vector3Df *)(mv + 3))->MultMatrix((Vector3Df *)(cv + 3), matrix); + if(cTangents) + { + mTangents[2*nVertices+0].MultMatrix(cTangents[2*c+0], normalMatrix); + mTangents[2*nVertices+1].MultMatrix(cTangents[2*c+1], normalMatrix); + } + nVertices++; + } + } + else + { + Vector3Df * mNormals = mesh.normals; + Pointf * mTexCoords = mesh.texCoords; + Vector3Df * cNormals = child.mesh.normals; + Pointf * cTexCoords = child.mesh.texCoords; + + if(cTexCoords) + memcpy(mTexCoords + nVertices, cTexCoords, sizeof(Pointf) * cCount); + for(c = 0; c < cCount; c++) + { + mVertices[nVertices].MultMatrix(cVertices[c], matrix); + if(cNormals) + mNormals[nVertices].MultMatrix(cNormals[c], normalMatrix); + if(cTangents) + { + mTangents[2*nVertices+0].MultMatrix(cTangents[2*c+0], normalMatrix); + mTangents[2*nVertices+1].MultMatrix(cTangents[2*c+1], normalMatrix); + } + nVertices++; } - nVertices++; } } } @@ -907,7 +1390,8 @@ public: { if(!foundGroup && !(group.type.vertexRange)) { - if(!FindMaterialAndType(mesh, group.material, group.type)) + // TOCHECK: There might be a much more efficent way to do this? + if(!FindMaterialAndType(map, mesh, group.material, group.type)) { material = group.material; type = group.type; @@ -928,7 +1412,7 @@ public: { if(!foundGroup && !(group.type.vertexRange)) { - if(!FindMaterialAndType(mesh, group.material ? group.material : child.material, group.type)) + if(!FindMaterialAndType(map, mesh, group.material ? group.material : child.material, group.type)) { material = group.material ? group.material : child.material; type = group.type; @@ -945,9 +1429,14 @@ public: // Merge with all similar groups if(foundGroup) { + // FIXME: This doesn't work with sharedIndices PrimitiveGroup newGroup = mesh.AddPrimitiveGroup(type, nIndices); + MapIterator it { map = map }; if(newGroup) { + if(!it.Index({ material, type }, true)) + it.data = newGroup; + newGroup.material = material; nIndices = 0; @@ -961,11 +1450,17 @@ public: { int c; if(group.type.indices32bit) + { + uint32 * indices32 = group.indices32 ? group.indices32 : mesh.indices + group.baseIndex; for(c = 0; cdata = null; if(triangle->type.indices32bit) + { + uint32 * indices32 = src->indices32 ? src->indices32 : mesh.indices + src->baseIndex; for(i = 0; inIndices; i++) - triangle->indices32[i] = src->indices32[i] + vertexOffset; + triangle->indices32[i] = indices32[i] + vertexOffset; + } else + { + uint16 * indices = src->indices; for(i = 0; inIndices; i++) - triangle->indices[i] = (uint16)(src->indices[i] + vertexOffset); - mesh.UnlockPrimitive(triangle); + triangle->indices[i] = (uint16)(indices[i] + vertexOffset); + } + if(lastLevel) + mesh.UnlockPrimitive(triangle); } vertexOffset += objectMesh.nVertices; } @@ -1103,15 +1612,18 @@ public: if(triangle->type.indices32bit) { + uint32 * indices32 = src->indices32 ? src->indices32 : mesh.indices + src->baseIndex; for(i = 0; inIndices; i++) - triangle->indices32[i] = src->indices32[i] + vertexOffset; + triangle->indices32[i] = indices32[i] + vertexOffset; } else { + uint16 * indices = src->indices; for(i = 0; inIndices; i++) - triangle->indices[i] = (uint16)(src->indices[i] + vertexOffset); + triangle->indices[i] = (uint16)(indices[i] + vertexOffset); } - mesh.UnlockPrimitive(triangle); + if(lastLevel) + mesh.UnlockPrimitive(triangle); } vertexOffset += child.mesh.nVertices; } @@ -1129,17 +1641,36 @@ public: mesh.ApplyTranslucency(this); // this.flags.translucent = true; + // NOTE: This was problematic here as groups is OldList!!! + //mesh.groups.Sort(compareGroupMaterial, null); + (&mesh.groups)->Sort(compareGroupMaterial, null); + result = true; - mesh.Unlock(flags); + SetMinMaxRadius(true); + + if(lastLevel) + mesh.Unlock(flags); } + + delete map; + if(freeMesh && objectMesh) { + /* if(objectMesh.displaySystem) objectMesh.displaySystem.RemoveMesh(objectMesh); + */ delete objectMesh; } - SetMinMaxRadius(true); + + // TOOD: Make this an option? + if(lastLevel && displaySystem && mesh && !mesh.nPrimitives) + { + delete *&mesh.vertices; + delete *&mesh.texCoords; + delete *&mesh.normals; + } } return result; } @@ -1287,7 +1818,8 @@ public: return result; } - property Transform transform { set { transform = value; eulerOrientation = transform.orientation; } get { value = transform; } }; + property Transform transform { set { transform = value; if(rotationOrder == yxz) eulerOrientation = transform.orientation; } get { value = transform; } }; + property Euler eulerOrientation { set { eulerOrientation = value; } get { value = eulerOrientation; } } property Material material { set { material = value; } get { return material; } }; property Vector3Df max { get { value = max; } }; property Vector3Df min { get { value = min; } }; @@ -1416,6 +1948,9 @@ private: { Object child; FrameTrack track; + Euler euler { }; + bool eulerRotation = false; + bool hasPitch = false, hasRoll = false, hasYaw = false; for(track = tracks.first; track; track = track.next) { @@ -1452,6 +1987,9 @@ private: case rotation: track.InterpolateQuat(transform.orientation, prevKey->orientation, nextKey->orientation, prev, next, t); break; + case rYaw: eulerRotation = true; hasYaw = true; euler.yaw = track.InterpolateAngle(prevKey->roll, nextKey->roll, prev, next, t); break; + case rPitch: eulerRotation = true; hasPitch = true; euler.pitch = track.InterpolateAngle(prevKey->roll, nextKey->roll, prev, next, t); break; + case rRoll: eulerRotation = true; hasRoll = true; euler.roll = track.InterpolateAngle(prevKey->roll, nextKey->roll, prev, next, t); break; // Cameras case roll: roll = track.InterpolateFloat(prevKey->roll, nextKey->roll, prev, next, t); @@ -1487,6 +2025,64 @@ private: } } } + if(eulerRotation) + { + Matrix a, b; + Matrix rYaw, rPitch, rRoll; + Quaternion q; + + if(!hasYaw) + euler.yaw = eulerOrientation.yaw; + else + eulerOrientation.yaw = euler.yaw; + if(!hasPitch) + euler.pitch = eulerOrientation.pitch; + else + eulerOrientation.pitch = euler.pitch; + if(!hasRoll) + euler.roll = eulerOrientation.roll; + else + eulerOrientation.roll = euler.roll; + + rYaw.RotationQuaternion(Euler { yaw = euler.yaw }); + rPitch.RotationQuaternion(Euler { pitch = euler.pitch }); + rRoll.RotationQuaternion(Euler { roll = euler.roll }); + + switch(rotationOrder) + { + case xyz: + a.Multiply(rYaw, rPitch); + b.Multiply(rRoll, a); + break; + case xzy: + a.Multiply(rRoll, rPitch); + b.Multiply(rYaw, a); + break; + case yxz: + a.Multiply(rPitch, rYaw); + b.Multiply(rRoll, a); + break; + case yzx: + a.Multiply(rRoll, rYaw); + b.Multiply(rPitch, a); + break; + case zyx: + a.Multiply(rYaw, rRoll); + b.Multiply(rPitch, a); + break; + case zxy: + default: + a.Multiply(rPitch, rRoll); + b.Multiply(rYaw, a); + break; + } + + q.RotationMatrix(b); + transform.orientation = q; + } + flags.localMatrixSet = false; + flags.transform = true; + UpdateTransform(); for(child = children.first; child; child = child.next) child._Animate(frame); @@ -1495,7 +2091,7 @@ private: } // Private for now - FrustumPlacement InsideFrustum(Plane * planes) + public FrustumPlacement InsideFrustum(Plane * planes) { FrustumPlacement result = inside; @@ -1504,7 +2100,8 @@ private: for(p = 0; p<6; p++) { Plane * plane = &planes[p]; - double dot = plane->normal.DotProduct(wcenter); + //double dot = plane->normal.DotProduct(wcenter); + double dot = plane->normal.x * wcenter.x + plane->normal.y * wcenter.y + plane->normal.z * wcenter.z; double distance = dot + plane->d; if(distance < -wradius) { @@ -1537,7 +2134,8 @@ private: int numGoodPoints = 0; for(i = 0; i < 8; ++i) { - double dot = plane->normal.DotProduct(box[i]); + // double dot = plane->normal.DotProduct(box[i]); + double dot = plane->normal.x * box[i].x + plane->normal.y * box[i].y + plane->normal.z * box[i].z; double distance = dot + plane->d; if(distance > -1) numGoodPoints++; @@ -1604,15 +2202,73 @@ private: public property Light light { - set - { - light = value; - } - get - { - value = light; - } + set { light = value; } + get { value = light; } } Euler eulerOrientation; + EulerRotationOrder rotationOrder; rotationOrder = zxy; + + public property EulerRotationOrder rotationOrder + { + set { rotationOrder = value; } + get { return rotationOrder; } + } + + DisplaySystem displaySystem; + float mvMatrix[16]; // Model-view matrix + + public property DisplaySystem displaySystem + { +#if !defined(ECERE_NOGL) + set { Upload(value, null, null, 0, null); } +#endif + get { return displaySystem; } + } + +#if !defined(ECERE_NOGL) + public void Upload(DisplaySystem displaySystem, GLMB mab, GLMB meab, int nAT, GLArrayTexture * mAT) + { + Object o; + + this.displaySystem = displaySystem; + if(flags.mesh && mesh) + mesh.Upload(displaySystem, true, mab, meab, nAT, mAT); + for(o = children.first; o; o = o.next) + o.Upload(displaySystem, mab, meab, nAT, mAT); + } +#endif + + void setTransform(Matrix sm, Matrix svm, Vector3D cp) // Start-up matrix, Start-up X View Matrix, Camera Position + { + float * mv = mvMatrix; + double * wm = matrix.array, * m; + float tx = 0, ty = 0, tz = 0; + if(flags.viewSpace) + m = sm.array; + else + { + double x = wm[12] - cp.x, y = wm[13] - cp.y, z = wm[14] - cp.z; + m = svm.array; + tx = (float)(x * m[0] + y * m[4] + z * m[ 8] + m[12]); + ty = (float)(x * m[1] + y * m[5] + z * m[ 9] + m[13]); + tz = (float)(x * m[2] + y * m[6] + z * m[10] + m[14]); + } + mv[ 0]=(float)(wm[0]*m[0] + wm[1]*m[4] + wm[2]*m[ 8]); + mv[ 1]=(float)(wm[0]*m[1] + wm[1]*m[5] + wm[2]*m[ 9]); + mv[ 2]=(float)(wm[0]*m[2] + wm[1]*m[6] + wm[2]*m[10]); + mv[ 3]=0; + mv[ 4]=(float)(wm[4]*m[0] + wm[4]*m[4] + wm[6]*m[ 8]); + mv[ 5]=(float)(wm[4]*m[1] + wm[4]*m[5] + wm[6]*m[ 9]); + mv[ 6]=(float)(wm[4]*m[2] + wm[4]*m[6] + wm[6]*m[10]); + mv[ 7]=0; + mv[ 8]=(float)(wm[8]*m[0] + wm[9]*m[4] + wm[10]*m[ 8]); + mv[ 9]=(float)(wm[8]*m[1] + wm[9]*m[5] + wm[10]*m[ 9]); + mv[10]=(float)(wm[8]*m[2] + wm[9]*m[6] + wm[10]*m[10]); + mv[11]=0; + mv[12]=tx; + mv[13]=ty; + mv[14]=tz; + mv[15]=1; + } }; diff --git a/ecere/src/gfx/3D/Plane.ec b/ecere/src/gfx/3D/Plane.ec index bc203d2e69..4c0d637a22 100644 --- a/ecere/src/gfx/3D/Plane.ec +++ b/ecere/src/gfx/3D/Plane.ec @@ -44,7 +44,7 @@ public struct Plane }; double d; - void FromPoints(Vector3D v1, Vector3D v2, Vector3D v3) + void FromPoints(const Vector3D v1, const Vector3D v2, const Vector3D v3) { Vector3D a, b; @@ -56,7 +56,7 @@ public struct Plane d = -normal.DotProduct(v1); } - void FromPointsf(Vector3Df v1, Vector3Df v2, Vector3Df v3) + void FromPointsf(const Vector3Df v1, const Vector3Df v2, const Vector3Df v3) { Vector3D v1d { (double)v1.x, (double)v1.y, (double)v1.z }; Vector3D v2d { (double)v2.x, (double)v2.y, (double)v2.z }; @@ -71,7 +71,7 @@ public struct Plane d = -normal.DotProduct(v1d); } - void MultMatrix(Plane source, Matrix inverseTranspose) + void MultMatrix(const Plane source, const Matrix inverseTranspose) { a = source.a * inverseTranspose.m[0][0] + source.b * inverseTranspose.m[1][0] + @@ -91,7 +91,7 @@ public struct Plane source.d * inverseTranspose.m[3][3]; } - void IntersectLine(Line line, Vector3D result) + void IntersectLine(const Line line, Vector3D result) { double divisor = a * line.delta.x + b * line.delta.y + c * line.delta.z; @@ -108,7 +108,7 @@ public struct Plane d * line.delta.z ) / divisor; } - void IntersectLinef(Line line, Vector3Df result) + void IntersectLinef(const Line line, Vector3Df result) { double divisor = a * line.delta.x + b * line.delta.y + c * line.delta.z; @@ -125,7 +125,7 @@ public struct Plane d * line.delta.z ) / divisor); } - void FromPointNormal(Vector3D normal, Vector3D point) + void FromPointNormal(const Vector3D normal, const Vector3D point) { this.normal = normal; d = -(normal.x * point.x + normal.y * point.y + normal.z * point.z); diff --git a/ecere/src/gfx/3D/Quaternion.ec b/ecere/src/gfx/3D/Quaternion.ec index 17be26284c..e7baddb0bd 100644 --- a/ecere/src/gfx/3D/Quaternion.ec +++ b/ecere/src/gfx/3D/Quaternion.ec @@ -2,68 +2,7 @@ namespace gfx3D; import "Display" -public struct Euler -{ - Degrees yaw, pitch, roll; - property Quaternion - { - set - { - double y = 2 * ( value.y*value.z + value.w*value.x ); - if(fabs(y) <= 1.0 - 0.000005) - { - double x = 2 * ( value.x*value.z - value.w*value.y ); - double z = 1 - 2 * ( value.x*value.x + value.y*value.y ); - Angle yaw = -atan2(x, z); - Angle pitch = atan2(y, sqrt(x * x + z * z)); - double sYaw = sin( yaw / 2 ); - double cYaw = cos( yaw / 2 ); - double sPitch = sin( pitch / 2 ); - double cPitch = cos( pitch / 2 ); - Quaternion yp = { cPitch * cYaw, sPitch * cYaw, cPitch * sYaw, sPitch * sYaw }; - double rollW = yp.w * value.w + yp.x * value.x + yp.y * value.y + yp.z * value.z; - double rollZ = yp.w * value.z + yp.x * value.y - yp.y * value.x - yp.z * value.w; - - this.yaw = yaw; - this.pitch = pitch; - this.roll = atan2(rollZ, rollW) * 2; - } - else - { - // 90 degrees pitch case - double sin45 = sin(Pi/4); - double yawW = sin45 * value.w + sin45 * value.x; - double yawY = sin45 * value.y + sin45 * value.z; - - this.yaw = atan2(yawY, yawW) * 2; - this.pitch = Pi/2; - this.roll = 0; - } - } - get - { - double sYaw = sin( yaw / 2 ); - double cYaw = cos( yaw / 2 ); - double sPitch = sin( pitch / 2 ); - double cPitch = cos( pitch / 2 ); - double sRoll = sin( roll / 2 ); - double cRoll = cos( roll / 2 ); - Quaternion yp = { cPitch * cYaw, sPitch * cYaw, cPitch * sYaw, sPitch * sYaw }; - - value.w = yp.w * cRoll - yp.z * sRoll; - value.x = yp.x * cRoll - yp.y * sRoll; - value.y = yp.y * cRoll + yp.x * sRoll; - value.z = yp.z * cRoll + yp.w * sRoll; - } - }; - - void Add(Euler e1, Euler e2) - { - yaw = e1.yaw + e2.yaw; - pitch = e1.pitch + e2.pitch; - roll = e1.roll + e2.roll; - } -}; +public enum EulerRotationOrder { xyz, xzy, yxz, yzx, zxy, zyx }; public struct Quaternion { @@ -75,7 +14,7 @@ public struct Quaternion w = 1; } - void Normalize(Quaternion source) + void Normalize(const Quaternion source) { double m = sqrt(source.x * source.x + source.y * source.y + @@ -93,7 +32,7 @@ public struct Quaternion w = x = y = z = 0; } - void Multiply(Quaternion q1, Quaternion q2) + void Multiply(const Quaternion q1, const Quaternion q2) { w = q1.w * q2.w - q2.x * q1.x - q1.y * q2.y - q1.z * q2.z; x = q1.w * q2.x + q2.w * q1.x + q1.y * q2.z - q1.z * q2.y; @@ -101,7 +40,7 @@ public struct Quaternion z = q1.w * q2.z + q2.w * q1.z + q1.x * q2.y - q1.y * q2.x; } - void Divide(Quaternion q1, Quaternion q2) + void Divide(const Quaternion q1, const Quaternion q2) { w = q2.w * q1.w + q2.x * q1.x + q2.y * q1.y + q2.z * q1.z; x = q2.w * q1.x - q2.x * q1.w + q2.y * q1.z - q2.z * q1.y; @@ -109,7 +48,7 @@ public struct Quaternion z = q2.w * q1.z + q2.x * q1.y - q2.y * q1.x - q2.z * q1.w; } - void RotationAxis(Vector3D axis, Degrees angle) + void RotationAxis(const Vector3D axis, Degrees angle) { double sa = sin( angle / 2 ); double ca = cos( angle / 2 ); @@ -120,37 +59,64 @@ public struct Quaternion w = ca; } - void RotationYawPitchRoll(Euler euler) + void RotationYawPitchRoll(const Euler euler) { - Quaternion rotation, result; + double cy = cos(euler.yaw * 0.5); + double sy = sin(euler.yaw * 0.5); + double cp = cos(euler.pitch * 0.5); + double sp = sin(euler.pitch * 0.5); + double cr = cos(euler.roll * 0.5); + double sr = sin(euler.roll * 0.5); + + w = cr * cp * cy - sr * sp * sy; + x = cr * sp * cy - sr * cp * sy; + y = cr * cp * sy + sr * sp * cy; + z = cr * sp * sy + sr * cp * cy; + } - result.Yaw(euler.yaw); - rotation.Pitch(euler.pitch); - Multiply(rotation, result); - rotation.Roll(euler.roll); - result.Multiply(rotation, this); - Normalize(result); + void RotationEuler(Euler euler, EulerRotationOrder rotationOrder) + { + Quaternion qPitch, qYaw, qRoll; + qPitch.RotationAxis({ 1,0,0 }, euler.pitch); + qYaw.RotationAxis ({ 0,1,0 }, euler.yaw); + qRoll.RotationAxis ({ 0,0,1 }, euler.roll); + rotateQuats(qPitch, qYaw, qRoll, rotationOrder); + } + + static void rotateQuats(Quaternion qPitch, Quaternion qYaw, Quaternion qRoll, EulerRotationOrder rotationOrder) + { + Quaternion q, a; + switch(rotationOrder) + { + case xyz: a.Multiply(qYaw, qPitch); q.Multiply(qRoll, a); break; + case xzy: a.Multiply(qRoll, qPitch); q.Multiply(qYaw, a); break; + case yxz: a.Multiply(qPitch, qYaw); q.Multiply(qRoll, a); break; + case yzx: a.Multiply(qRoll, qYaw); q.Multiply(qPitch, a); break; + case zyx: a.Multiply(qYaw, qRoll); q.Multiply(qPitch, a); break; + default: + case zxy: a.Multiply(qPitch, qRoll); q.Multiply(qYaw, a); break; + } + this = q; } - void RotationDirection(Vector3D direction) + void RotationDirection(const Vector3D direction) { Angle yaw = -atan2(direction.x, direction.z); Angle pitch = atan2(direction.y, sqrt(direction.x * direction.x + direction.z * direction.z)); YawPitch(yaw, pitch); } - void RotationMatrix(Matrix m) + void RotationMatrix(const Matrix m) { - double t = m.m[0][0] + m.m[1][1] + m.m[2][2] + 1.0; + double t = m.m[0][0] + m.m[1][1] + m.m[2][2]; if(t > 0) { - double s = sqrt(t) * 2; - - w = (double) (0.25f * s); - x = (double) (( m.m[2][1] - m.m[1][2] ) / s); - y = (double) (( m.m[0][2] - m.m[2][0] ) / s); - z = (double) (( m.m[1][0] - m.m[0][1] ) / s); + double s = sqrt(t + 1.0) * 2; + w = 0.25 * s; + x = ( m.m[2][1] - m.m[1][2] ) / s; + y = ( m.m[0][2] - m.m[2][0] ) / s; + z = ( m.m[1][0] - m.m[0][1] ) / s; } else { @@ -165,11 +131,11 @@ public struct Quaternion k = nxt[j]; s = sqrt(m.m[i][i] - (m.m[j][j] + m.m[k][k]) + 1.0) * 2; - w = (double) ((m.m[k][j] - m.m[j][k]) / s); + w = (m.m[k][j] - m.m[j][k]) / s; - q[i] = (double) (0.25f * s); - q[j] = (double) ((m.m[j][i] - m.m[i][j]) / s); - q[k] = (double) ((m.m[k][i] - m.m[i][k]) / s); + q[i] = 0.25 * s; + q[j] = (m.m[j][i] + m.m[i][j]) / s; + q[k] = (m.m[k][i] + m.m[i][k]) / s; x = q[0]; y = q[1]; @@ -179,7 +145,7 @@ public struct Quaternion #define DELTA 0 - void Slerp(Quaternion from, Quaternion to, float t) + void Slerp(const Quaternion from, const Quaternion to, float t) { double to1[4]; double omega, cosom, sinom, scale0, scale1; @@ -303,19 +269,147 @@ public struct Quaternion void ToDirection(Vector3D direction) { - /* - Vector3Df vector { 0,0,1 }; +/* + Vector3D vector { 0,0,1 }; Matrix mat; mat.RotationQuaternion(this); - direction.Transform(vector, mat); - */ + direction.MultMatrix(vector, mat); +*/ + direction.x = (double)( 2 * ( x*z - w*y )); direction.y = (double)( 2 * ( y*z + w*x )); direction.z = (double)(1 - 2 * ( x*x + y*y )); } - void Inverse(Quaternion source) + void Inverse(const Quaternion source) { this = { -source.w, source.x, source.y, source.z }; } }; + +static struct RotationOrderProperties +{ + int yaw, pitch, roll; // order of yaw, pitch & roll in the rotation order + int t0y, t0x, s0y, s0x; // atan2() terms and signs for 1st component + int t1, s1; // term for determining Gimbal lock and resolving 2nd component with asin() + int t2y, t2x, s2y, s2x; // atan2() terms and signs for 3rd component + int fy, fx, sfy, sfx; // Gimbal lock fallback terms & signs for atan2() +}; + +static RotationOrderProperties rotProps[EulerRotationOrder] = +{ + // YAW PITCH ROLL | t0y t0x s0y s0x | t1 s1 | t2y t2x s2y s2x | fy fx sfy sfx + { 1, 0, 2, 2*4+1, 2*4+2, 1, 1, 2*4+0, -1, 1*4+0, 0*4+0, 1, 1, 0*4+1, 0*4+2, 1, 1 }, // xyz + { 2, 0, 1, 1*4+2, 1*4+1, -1, 1, 1*4+0, 1, 2*4+0, 0*4+0, -1, 1, 0*4+2, 0*4+1, 1, -1 }, // xzy + { 0, 1, 2, 2*4+0, 2*4+2, -1, 1, 2*4+1, 1, 0*4+1, 1*4+1, -1, 1, 0*4+2, 0*4+0, -1, -1 }, // yxz + { 0, 2, 1, 0*4+2, 0*4+0, 1, 1, 0*4+1, -1, 2*4+1, 1*4+1, 1, 1, 1*4+2, 1*4+0, 1, 1 }, // yzx + { 2, 1, 0, 1*4+0, 1*4+1, 1, 1, 1*4+2, -1, 0*4+2, 2*4+2, 1, 1, 0*4+1, 0*4+0, 1, -1 }, // zxy + { 1, 2, 0, 0*4+1, 0*4+0, -1, 1, 0*4+2, 1, 1*4+2, 2*4+2, -1, 1, 1*4+0, 1*4+1, -1, -1 } // zyx +}; + + +// FIXME: Having this before Quaternion class now causes problems!!! (Since adding FromQuaternion() ?) +public struct Euler +{ + Degrees yaw, pitch, roll; + + void FromMatrix(const Matrix m, EulerRotationOrder order) + { + Degrees values[3]; + RotationOrderProperties p = rotProps[order]; + double v = m.array[p.t1]; + if(fabs(v) <= 1.0 - 0.000005) + { + values[0] = atan2(p.s0y * m.array[p.t0y], p.s0x * m.array[p.t0x]); + //values[1] = atan2(p.s1 * v, sqrt(1.0 - v * v)); + //values[1] = atan2(p.s1 * m.array[t1], sqrt(m.array[p.t0y]*m.array[p.t0y] + m.array[p.t0x]*m.array[p.t0x])); + values[1] = asin(p.s1 * v); + values[2] = atan2(p.s2y * m.array[p.t2y], p.s2x * m.array[p.t2x]); + } + else + { + values[0] = Pi + atan2(p.sfy * m.array[p.fy], p.sfx * m.array[p.fx]); + values[1] = (v * p.s1) < 0 ? -Pi/2 : Pi/2; + values[2] = 0; + } + this = { values[p.yaw], values[p.pitch], values[p.roll] }; + } + + void FromQuaternion(const Quaternion q, EulerRotationOrder order) + { + Matrix m; + m.RotationQuaternion(q); + FromMatrix(m, order); + } + + property Quaternion + { + // NOTE: This assumes yaw, pitch, roll (yxz) order + set + { + double y = 2 * ( value.y*value.z + value.w*value.x ); + if(fabs(y) <= 1.0 - 0.000005) + { + double x = 2 * ( value.x*value.z - value.w*value.y ); + double z = 1 - 2 * ( value.x*value.x + value.y*value.y ); + Angle yaw = -atan2(x, z); + Angle pitch = atan2(y, sqrt(x * x + z * z)); + double sYaw = sin( yaw / 2 ); + double cYaw = cos( yaw / 2 ); + double sPitch = sin( pitch / 2 ); + double cPitch = cos( pitch / 2 ); + Quaternion yp = { cPitch * cYaw, sPitch * cYaw, cPitch * sYaw, sPitch * sYaw }; + double rollW = yp.w * value.w + yp.x * value.x + yp.y * value.y + yp.z * value.z; + double rollZ = yp.w * value.z + yp.x * value.y - yp.y * value.x - yp.z * value.w; + + this.yaw = yaw; + this.pitch = pitch; + this.roll = atan2(rollZ, rollW) * 2; + } + else + { + this.pitch = Sgn(y) * Pi/2; + this.roll = 0; + + // 90 degrees pitch case + if(y < 0) + { + double sin45 = sin(Pi/4); + double yawW = sin45 * value.w - sin45 * value.x; + double yawY = sin45 * value.y - sin45 * value.z; + this.yaw = atan2(yawY, yawW) * 2; + } + else + { + double sin45 = sin(Pi/4); + double yawW = sin45 * value.w + sin45 * value.x; + double yawY = sin45 * value.y + sin45 * value.z; + this.yaw = atan2(yawY, yawW) * 2; + } + + } + } + get + { + double sYaw = sin( yaw / 2 ); + double cYaw = cos( yaw / 2 ); + double sPitch = sin( pitch / 2 ); + double cPitch = cos( pitch / 2 ); + double sRoll = sin( roll / 2 ); + double cRoll = cos( roll / 2 ); + Quaternion yp = { cPitch * cYaw, sPitch * cYaw, cPitch * sYaw, sPitch * sYaw }; + + value.w = yp.w * cRoll - yp.z * sRoll; + value.x = yp.x * cRoll - yp.y * sRoll; + value.y = yp.y * cRoll + yp.x * sRoll; + value.z = yp.z * cRoll + yp.w * sRoll; + } + }; + + void Add(Euler e1, Euler e2) + { + yaw = e1.yaw + e2.yaw; + pitch = e1.pitch + e2.pitch; + roll = e1.roll + e2.roll; + } +}; diff --git a/ecere/src/gfx/3D/Vector3D.ec b/ecere/src/gfx/3D/Vector3D.ec index dd087a350a..4fa64f5950 100644 --- a/ecere/src/gfx/3D/Vector3D.ec +++ b/ecere/src/gfx/3D/Vector3D.ec @@ -6,47 +6,47 @@ public struct Vector3D { double x, y, z; - void Add(Vector3D vector1, Vector3D vector2) + void Add(const Vector3D vector1, const Vector3D vector2) { x = vector1.x + vector2.x; y = vector1.y + vector2.y; z = vector1.z + vector2.z; } - void Subtract(Vector3D vector1, Vector3D vector2) + void Subtract(const Vector3D vector1, const Vector3D vector2) { x = vector1.x - vector2.x; y = vector1.y - vector2.y; z = vector1.z - vector2.z; } - void Scale(Vector3D vector1, double s) + void Scale(const Vector3D vector1, double s) { x = vector1.x * s; y = vector1.y * s; z = vector1.z * s; } - double DotProduct(Vector3D vector2) + double DotProduct(const Vector3D vector2) { return x * vector2.x + y * vector2.y + z * vector2.z; } - double DotProductf(Vector3Df vector2) + double DotProductf(const Vector3Df vector2) { return x * vector2.x + y * vector2.y + z * vector2.z; } - void CrossProduct(Vector3D vector1, Vector3D vector2) + void CrossProduct(const Vector3D vector1, const Vector3D vector2) { x = vector1.y * vector2.z - vector1.z * vector2.y; y = vector1.z * vector2.x - vector1.x * vector2.z; z = vector1.x * vector2.y - vector1.y * vector2.x; } - void Normalize(Vector3D source) + void Normalize(const Vector3D source) { - double m = source.length; + double m = source.length; // FIXME -- get should be fine with const objects if(m) { x = source.x/m; @@ -57,83 +57,57 @@ public struct Vector3D x = y = z = 0; } - void MultMatrix(Vector3D source, Matrix matrix) + void MultMatrix(const Vector3D source, const Matrix matrix) { x = source.x * matrix.m[0][0] + source.y * matrix.m[1][0] + source.z * matrix.m[2][0] + matrix.m[3][0]; y = source.x * matrix.m[0][1] + source.y * matrix.m[1][1] + source.z * matrix.m[2][1] + matrix.m[3][1]; z = source.x * matrix.m[0][2] + source.y * matrix.m[1][2] + source.z * matrix.m[2][2] + matrix.m[3][2]; } - void MultMatrixf(Vector3Df source, Matrix matrix) + void MultMatrixf(const Vector3Df source, const Matrix matrix) { x = source.x * matrix.m[0][0] + source.y * matrix.m[1][0] + source.z * matrix.m[2][0] + matrix.m[3][0]; y = source.x * matrix.m[0][1] + source.y * matrix.m[1][1] + source.z * matrix.m[2][1] + matrix.m[3][1]; z = source.x * matrix.m[0][2] + source.y * matrix.m[1][2] + source.z * matrix.m[2][2] + matrix.m[3][2]; } - void DivideMatrix(Vector3D source, Matrix matrix) + void MultQuaternion(const Vector3D s, const Quaternion quat) { - /* - solve( + Vector3D v { quat.x, quat.y, quat.z }; + double w = quat.w, a = w*w - (v.x*v.x+v.y*v.y+v.z*v.z) /*DotProduct(v)*/, dotVS = v.x*s.x+v.y*s.y+v.z*s.z /*v.DotProduct(s)*/; + Vector3D cross { - vectorX=sourceX*m00+sourceY*m10+sourceZ*m20+m30, - vectorY=sourceZ*m01+sourceY*m11+sourceZ*m21+m31, - vectorZ=sourceX*m02+sourceY*m12+sourceZ*m22+m32 - }, { sourceX, sourceY, sourceZ }); - */ - - double var1 = - matrix.m[2][0] * matrix.m[0][2] * matrix.m[1][1] - - matrix.m[0][2] * matrix.m[2][1] * matrix.m[1][0] - - matrix.m[2][2] * matrix.m[0][0] * matrix.m[1][1] - - matrix.m[0][2] * matrix.m[0][1] * matrix.m[1][0] - + matrix.m[2][1] * matrix.m[0][0] * matrix.m[1][2] - + matrix.m[0][1] * matrix.m[0][0] * matrix.m[1][2]; - - x = ( - - matrix.m[2][2] * source.x * matrix.m[1][1] - + matrix.m[2][2] * matrix.m[1][0] * source.y - - matrix.m[2][2] * matrix.m[1][0] * matrix.m[3][1] - + matrix.m[2][2] * matrix.m[3][0] * matrix.m[1][1] - - matrix.m[2][0] * matrix.m[3][2] * matrix.m[1][1] - + source.x * matrix.m[2][1] * matrix.m[1][2] - + source.x * matrix.m[0][1] * matrix.m[1][2] - - matrix.m[1][0] * matrix.m[0][1] * source.z - + matrix.m[1][0] * matrix.m[2][1] * matrix.m[3][2] - + matrix.m[1][0] * matrix.m[0][1] * matrix.m[3][2] - - matrix.m[1][0] * matrix.m[2][1] * source.z - - matrix.m[3][0] * matrix.m[2][1] * matrix.m[1][2] - - matrix.m[2][0] * matrix.m[1][2] * source.y - + matrix.m[2][0] * matrix.m[1][2] * matrix.m[3][1] - + matrix.m[2][0] * source.z * matrix.m[1][1] - - matrix.m[3][0] * matrix.m[0][1] * matrix.m[1][2] - ) / var1; - - y = - ( - - matrix.m[2][0] * matrix.m[0][2] * source.y - + matrix.m[2][1] * matrix.m[0][0] * matrix.m[3][2] - + matrix.m[2][0] * matrix.m[0][2] * matrix.m[3][1] - + matrix.m[0][1] * matrix.m[0][0] * matrix.m[3][2] - - matrix.m[2][1] * matrix.m[0][0] * source.z - - matrix.m[0][2] * matrix.m[2][1] * matrix.m[3][0] - + matrix.m[0][2] * matrix.m[0][1] * source.x - - matrix.m[0][2] * matrix.m[0][1] * matrix.m[3][0] - + matrix.m[0][2] * matrix.m[2][1] * source.x - + matrix.m[2][2] * matrix.m[0][0] * source.y - - matrix.m[0][1] * matrix.m[0][0] * source.z - - matrix.m[2][2] * matrix.m[0][0] * matrix.m[3][1] - ) / var1; + s.y * v.z - s.z * v.y, + s.z * v.x - s.x * v.z, + s.x * v.y - s.y * v.x + }; + //cross.CrossProduct(s, v); + x = (float)(2 * dotVS * v.x + a * s.x + 2 * w * cross.x); + y = (float)(2 * dotVS * v.y + a * s.y + 2 * w * cross.y); + z = (float)(2 * dotVS * v.z + a * s.z + 2 * w * cross.z); + } - z = ( - source.x * matrix.m[0][2] * matrix.m[1][1] - + matrix.m[0][0] * matrix.m[3][2] * matrix.m[1][1] - + matrix.m[0][0] * matrix.m[1][2] * source.y - - matrix.m[0][0] * matrix.m[1][2] * matrix.m[3][1] - - matrix.m[0][0] * source.z * matrix.m[1][1] - - matrix.m[1][0] * matrix.m[0][2] * source.y - + matrix.m[1][0] * matrix.m[0][2] * matrix.m[3][1] - - matrix.m[3][0] * matrix.m[0][2] * matrix.m[1][1] - ) / var1; + void DivideMatrix(const Vector3D v, const Matrix m) + { + /* v be x,y,z; m be { { e,f,g,...}, {h,i,j,...}, {k,l,m,...}, {...} }; this be a,b,c; solve for a,b,c: + x=a*e+b*h+c*k + y=a*f+b*i+c*l + z=a*g+b*j+c*m + */ + double vx = v.x - m.m[3][0]; + double vy = v.y - m.m[3][1]; + double vz = v.z - m.m[3][2]; + double fm_gl = m.m[0][1] * m.m[2][2] - m.m[0][2] * m.m[2][1]; + double fz_gy = m.m[0][1] * vz - m.m[0][2] * vy; + double gi_fj = m.m[0][2] * m.m[1][1] - m.m[0][1] * m.m[1][2]; + double iz_jy = m.m[1][1] * vz - m.m[1][2] * vy; + double jl_im = m.m[1][2] * m.m[2][1] - m.m[1][1] * m.m[2][2]; + double my_lz = m.m[2][2] * vy - m.m[2][1] * vz; + double invDiv = 1.0 / (m.m[0][0] * jl_im + m.m[1][0] * fm_gl + m.m[2][0] * gi_fj); + + x = invDiv * (m.m[1][0] * my_lz + m.m[2][0] * iz_jy + vx * jl_im); + y = -invDiv * (m.m[0][0] * my_lz + m.m[2][0] * fz_gy - vx * fm_gl); + z = invDiv * (m.m[0][0] *-iz_jy + m.m[1][0] * fz_gy + vx * gi_fj); } property double length { get { return (double)sqrt(x * x + y * y + z * z); } }; @@ -175,40 +149,40 @@ public struct Vector3Df { float x, y, z; - void Add(Vector3Df vector1, Vector3Df vector2) + void Add(const Vector3Df vector1, const Vector3Df vector2) { x = vector1.x + vector2.x; y = vector1.y + vector2.y; z = vector1.z + vector2.z; } - void Subtract(Vector3Df vector1, Vector3Df vector2) + void Subtract(const Vector3Df vector1, const Vector3Df vector2) { x = vector1.x - vector2.x; y = vector1.y - vector2.y; z = vector1.z - vector2.z; } - void Scale(Vector3Df vector1, float s) + void Scale(const Vector3Df vector1, float s) { x = vector1.x * s; y = vector1.y * s; z = vector1.z * s; } - double DotProduct(Vector3Df vector2) + double DotProduct(const Vector3Df vector2) { return (double)x * (double)vector2.x + (double)y * (double)vector2.y + (double)z * (double)vector2.z; } - void CrossProduct(Vector3Df vector1, Vector3Df vector2) + void CrossProduct(const Vector3Df vector1, const Vector3Df vector2) { x = vector1.y * vector2.z - vector1.z * vector2.y; y = vector1.z * vector2.x - vector1.x * vector2.z; z = vector1.x * vector2.y - vector1.y * vector2.x; } - void Normalize(Vector3Df source) + void Normalize(const Vector3Df source) { /* float m = source.length; @@ -227,14 +201,31 @@ public struct Vector3Df z = source.z * i; } - void MultMatrix(Vector3Df source, Matrix matrix) + void MultMatrix(const Vector3Df source, const Matrix matrix) { x = (float)(source.x * matrix.m[0][0] + source.y * matrix.m[1][0] + source.z * matrix.m[2][0] + matrix.m[3][0]); y = (float)(source.x * matrix.m[0][1] + source.y * matrix.m[1][1] + source.z * matrix.m[2][1] + matrix.m[3][1]); z = (float)(source.x * matrix.m[0][2] + source.y * matrix.m[1][2] + source.z * matrix.m[2][2] + matrix.m[3][2]); } - void DivideMatrix(Vector3Df source, Matrix matrix) + void MultQuaternion(const Vector3Df source, const Quaternion quat) + { + Vector3D s { source.x, source.y, source.z }; + Vector3D v { quat.x, quat.y, quat.z }; + double w = quat.w, a = w*w - (v.x*v.x+v.y*v.y+v.z*v.z) /*DotProduct(v)*/, dotVS = v.x*s.x+v.y*s.y+v.z*s.z /*v.DotProduct(s)*/; + Vector3D cross + { + s.y * v.z - s.z * v.y, + s.z * v.x - s.x * v.z, + s.x * v.y - s.y * v.x + }; + //cross.CrossProduct(s, v); + x = (float)(2 * dotVS * v.x + a * s.x + 2 * w * cross.x); + y = (float)(2 * dotVS * v.y + a * s.y + 2 * w * cross.y); + z = (float)(2 * dotVS * v.z + a * s.z + 2 * w * cross.z); + } + + void DivideMatrix(const Vector3Df source, const Matrix matrix) { /* solve( diff --git a/ecere/src/gfx/3D/meshes/Cube.ec b/ecere/src/gfx/3D/meshes/Cube.ec index 845444e753..baf7caf48c 100644 --- a/ecere/src/gfx/3D/meshes/Cube.ec +++ b/ecere/src/gfx/3D/meshes/Cube.ec @@ -14,7 +14,7 @@ import "Object" // TOFIX: If this is not here, Cube/Object gets registered as public class Cube : Object { public: - bool Create(DisplaySystem displaySystem) + bool Create(const DisplaySystem displaySystem) { bool result = false; if(this) @@ -89,7 +89,7 @@ public: if(c == 3) strcpy(name, "glass.bmp"); */ sprintf(name, "Cube Face %d", c+1); - material = displaySystem.AddNamedMaterial(name); + material = displaySystem ? displaySystem.AddNamedMaterial(name) : null; if(material) { material.flags = { noFog = true, doubleSided = true, translucent = true }; diff --git a/ecere/src/gfx/3D/meshes/SkyBox.ec b/ecere/src/gfx/3D/meshes/SkyBox.ec index 4d61e5b55c..ea8b6805aa 100644 --- a/ecere/src/gfx/3D/meshes/SkyBox.ec +++ b/ecere/src/gfx/3D/meshes/SkyBox.ec @@ -88,7 +88,7 @@ public: material = displaySystem.AddNamedMaterial(materialName); if(material) { - material.flags = { noFog = true, cubeMap = true, noLighting = true }; + material.flags = { noFog = true, cubeMap = true, noLighting = true, setupTextures = true, update = true }; material.opacity = 1; material.emissive.r = material.emissive.g = material.emissive.b = 1; material.baseMap = cubeMap; @@ -139,7 +139,7 @@ public: material = displaySystem.AddNamedMaterial(materialName); if(material) { - material.flags = { noFog = true, noLighting = true }; + material.flags = { noFog = true, noLighting = true, setupTextures = true, update = true }; material.opacity = 1; material.emissive.r = material.emissive.g = material.emissive.b = 1; material.baseMap = Bitmap { }; diff --git a/ecere/src/gfx/3D/models/Object3DSFormat.ec b/ecere/src/gfx/3D/models/Object3DSFormat.ec index c85b210c92..901285d885 100644 --- a/ecere/src/gfx/3D/models/Object3DSFormat.ec +++ b/ecere/src/gfx/3D/models/Object3DSFormat.ec @@ -2,13 +2,17 @@ namespace gfx3D::models; import "Object" -#if defined(__EMSCRIPTEN__) +#if defined(__linux__) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__i386__) +asm(".symver pow,pow@GLIBC_2.2.5"); +#endif + +#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) #if !defined(_GLES2) #define _GLES2 #endif #endif -#if (defined(__ANDROID__) || defined(__ODROID__)) && !defined(_GLES) +#if defined(__ODROID__) && !defined(_GLES) #define _GLES #endif @@ -398,7 +402,7 @@ static void ComputeNormals(Mesh mesh, FileInfo * info, Object object) Plane plane; uint * indices = face->indices; Vector3D edges[3], rEdges[3]; - computeNormalWeights(3, mVertices, indices, true, 0, face->dots, edges, rEdges); + computeNormalWeights(3, (float *)mVertices, 3, indices, true, 0, face->dots, edges, rEdges); plane.FromPointsf(mVertices[indices[2]], mVertices[indices[1]], mVertices[indices[0]]); face->normal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z }; @@ -747,7 +751,7 @@ static bool ReadFacesListChunks(FileInfo * info, Object object) count = ReadWORD(info->f); strcat(matName, name); - mat = displaySystem.GetMaterial(matName); + mat = displaySystem ? displaySystem.GetMaterial(matName) : null; faces = info->matFaces[(uintptr)mat]; if(!faces) info->matFaces[(uintptr)mat] = faces = { }; @@ -852,7 +856,7 @@ static bool ReadTriMesh(FileInfo * info, Object object) { Material mat = (Material)&m; Array faces = m; - if(mat.flags.translucent) + if(mat && mat.flags.translucent) { mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + faces.count]; for(i : faces) @@ -946,6 +950,9 @@ static bool ReadTriMesh(FileInfo * info, Object object) info->matFaces.Free(); delete info->matFaces; } + + // TODO: Make this an option + // mesh.ComputeNormals(); break; } case TRI_LOCAL: @@ -1166,17 +1173,17 @@ static bool ReadMap(FileInfo * info, Material mat) bool translucent = false; if(!mat.baseMap) { - mat.baseMap = displaySystem.GetTexture(location); + mat.baseMap = displaySystem ? displaySystem.GetTexture(location) : null; if(!mat.baseMap) { mat.baseMap = Bitmap { }; - if(displaySystem.GetTexture(bumpName)) + if(displaySystem && displaySystem.GetTexture(bumpName)) { mat.bumpMap = null; // Bad bump map if it's the same as the diffuse map... } if(!mat.baseMap.Load(location, null, null) || !mat.baseMap.Convert(null, pixelFormat888, null) || - !displaySystem.AddTexture(location, mat.baseMap)) + (displaySystem && !displaySystem.AddTexture(location, mat.baseMap))) { delete mat.baseMap; } @@ -1230,7 +1237,7 @@ static bool ReadMap(FileInfo * info, Material mat) case MAP_OPTIONS: { MapOptions options = (MapOptions)ReadWORD(info->f); - if(!options.dontTile) mat.flags.tile = true; + if(!options.dontTile) { mat.flags.tile = true; mat.flags.setupTextures = true; } break; } case MAP_1_U_SCALE: @@ -1589,15 +1596,24 @@ static bool ReadEditChunks(FileInfo * info, void * data) Material mat; ReadChunks(ReadMaterial, info, material); - mat = info->displaySystem.AddNamedMaterial(material.name); + mat = info->displaySystem ? info->displaySystem.AddNamedMaterial(material.name) : { }; if(mat) { if(material.baseMap) + { + material.baseMap.keepData = true; // WARNING: Texture will not save properly without this if DS used material.baseMap.MakeMipMaps(info->displaySystem); + } if(material.bumpMap) + { + material.bumpMap.keepData = true; // WARNING: Texture will not save properly without this if DS used material.bumpMap.MakeMipMaps(info->displaySystem); + } if(material.specularMap) + { + material.specularMap.keepData = true; // WARNING: Texture will not save properly without this if DS used material.specularMap.MakeMipMaps(info->displaySystem); + } // COPY_NITEM(mat, material); CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem)); @@ -2222,7 +2238,7 @@ class Object3DSFormat : ObjectFormat { class_property(extension) = "3ds"; - bool Load(Object object, const char * fileName, DisplaySystem displaySystem) + bool Load(Object object, const char * fileName, DisplaySystem displaySystem, void * options) { bool result = false; if(fileName) diff --git a/ecere/src/gfx/3D/models/e3d.ec b/ecere/src/gfx/3D/models/e3d.ec new file mode 100644 index 0000000000..87fadc8984 --- /dev/null +++ b/ecere/src/gfx/3D/models/e3d.ec @@ -0,0 +1,67 @@ +import "e3dRead" +import "e3dWrite" + +public class E3DFormat : ObjectFormat +{ + class_property(extension) = "e3d"; + + static bool Load(Object object, const char * fileName, DisplaySystem displaySystem, void * options) + { + File f = FileOpen(fileName, read); + if(f) + { + readE3D(f, fileName, object, displaySystem, options); + delete f; + return true; + } + return false; + } + + bool Save(Object object, const char * fileName, void * options) + { + File f = FileOpen(fileName, write); + if(f) + { + E3DWriteContext ctx { }; //, texturesByID = options.texturesByID }; + + StripLastDirectory(fileName, ctx.path); + + writeE3D(f, object, ctx); + + delete ctx; + delete f; + return true; + } + return false; + } + + static Array listTextures(File modelFile, const String fileName, E3DOptions options) + { + Array textures { }; + char path[MAX_LOCATION]; + E3DContext ctx { path = path }; + + if(options != null) + { + ctx.texturesByID = options.texturesByID; + ctx.materials = options.materials; + ctx.texturesQuery = options.texturesQuery; + ctx.positiveYUp = options.positiveYUp; + ctx.resolution = options.resolution; + ctx.compressedTextures = options.compressedTextures; + ctx.skipTexturesProcessing = options.skipTexturesProcessing; + } + else + ctx.texturesByID = { }; + + if(fileName) + StripLastDirectory(fileName, path); + else + path[0] = 0; + listTexturesReadBlocks(ctx, modelFile, 0, 0, modelFile.GetSize(), null, textures); + + delete ctx; + + return textures; + } +}; diff --git a/ecere/src/gfx/3D/models/e3d/e3dDefs.ec b/ecere/src/gfx/3D/models/e3d/e3dDefs.ec new file mode 100644 index 0000000000..7acd0acb18 --- /dev/null +++ b/ecere/src/gfx/3D/models/e3d/e3dDefs.ec @@ -0,0 +1,161 @@ +#ifdef ECERE_STATIC +public import static "ecere" +#else +public import "ecere" +#endif + +enum E3DBlockType : uint16 +{ + version = 0x0001, + + lzma = 0x0010, + + meshes = 0x1000, + mesh = 0x1010, + meshID = 0x1020, + meshBBox = 0x1021, + attributes = 0x2000, + attrVertices = 0x2010, // float x,y,z vertices + attrVerticesDbl = 0x2011, // double x,y,z vertices + attrQVertices = 0x2018, // quantized x,y,z 16-bit vertices (first, deltas) + attrNormals = 0x2020, // 10_10_10_2 + attrTexCoords = 0x2030, // float u,v + attrTexCoords2 = 0x2031, // float u,v (2nd) + attrTexCoords3 = 0x2032, // float u,v (3rd) + attrTexCoords4 = 0x2033, // float u,v (4th) + attrTexCoords5 = 0x2034, // float u,v (5th) + attrTexCoords6 = 0x2035, // float u,v (6th) + attrTexCoords7 = 0x2036, // float u,v (7th) + attrTexCoords8 = 0x2037, // float u,v (8th) + attrColors = 0x2070, // byte r,g,b,a + attrTangentsSign = 0x2080, // 10_10_10_2 -- first extra bit: sign for co-tangent + attrTangentsBT = 0x2081, // 2x 10_10_10_2 + attrSkin = 0x2090, // skin bones and weights + attrInterleaved = 0x2800, // currently supporting: [vertices, normals, texCoords] + triFaces16 = 0x1030, + triFaces32 = 0x1031, + facesMaterials = 0x1040, + bones = 0x1050, + parts = 0x1060, + + nodes = 0x3000, + meshNode = 0x3010, + nodeID = 0x3020, + nodeName = 0x3021, + scaling = 0x3030, + orientation = 0x3031, + position = 0x3032, + + // Can have sub-nodes! + + cameraNode = 0x3011, + lightNode = 0x3012, + + materials = 0x8000, + material = 0x8010, + materialID = 0x8011, + materialName = 0x8012, + materialGroup = 0x8013, // Value of 0: not computed... + + materialFlags = 0x8020, // Flags + opacity = 0x8021, // float + refractionRelIndex = 0x8022, // refraction index of material / RI of containing medium + reflectivity = 0x8023, // float + phongShininess = 0x8024, // float + + // Colors + diffuse = 0x8030, + specular = 0x8031, + emissive = 0x8032, + ambient = 0x8034, // default to diffuse if not set? + + // Maps + // use textureID or textureName for the maps: + // use attrTexCoords2..attrTexCoords8 to use different texCoords + emissiveMap = 0x8100, + normalMap = 0x8101, + heightmap = 0x8102, // float for parallax mapping + ambientOcclusionMap = 0x8103, // float + + //normalMapXY = 0x8110, // 1-sqrt() -- 2 color option + + phongDiffuseMap = 0x8200, // include opacity + phongSpecularMap = 0x8201, + phongAmbientMap = 0x8202, + + // PBR Roughness Metal + pbrRMAlbedo = 0x8300, // include opacity + pbrRMRoughnessMetalness = 0x8301, // 8 bit each (16 bits) + // pbrRMRoughnessMetalnessAO = 0x8310, // 8 bit each (24 bits) + // pbrRMNormalXYRMMap = 0x8311, 10_10_10_2 + + // PBR Specular + pbrSpecDiffuseMap = 0x8400, // include opacity + pbrSpecSpecularGlossMap = 0x8401, // Color + 8 bit + + textures = 0x9000, + texture = 0x9001, + textureID = 0x9002, + textureName = 0x9003, + texturePNG = 0x9101, + textureJPG = 0x9102, + textureJPG2K = 0x9103, + + animations = 0xA000 +}; + +struct E3DBlockHeader +{ + E3DBlockType type __attribute__((packed)); + uint32 size __attribute__((packed)); +}; + +class E3DMaterialFlags : uint32 +{ +public: + bool doubleSided:1; + bool partlyTransparent:1; + bool translucent:1; + bool wrapU:1; + bool wrapV:1; +}; + +void vecfUnpack10i(uint32 sum, Vector3Df dst, Vector3Df v2) +{ + int part = ( sum >> 0 ) & 1023; + dst.x = ( part & 512 ) ? (float)( 1024 - part ) * (-1.0f/512.0f) : (float)part * (1.0f/511.0f); + + part = ( sum >> 10 ) & 1023; + dst.y = ( part & 512 ) ? (float)( 1024 - part ) * (-1.0f/512.0f) : (float)part * (1.0f/511.0f); + + part = ( sum >> 20 ) & 1023; + dst.z = ( part & 512 ) ? (float)( 1024 - part ) * (-1.0f/512.0f) : (float)part * (1.0f/511.0f); + + if(v2 != null) + { + part = ( sum >> 30 ) & 3; // TODO: Compute this properly + v2.z = ( part & 2 ) ? (float)( 4 - part ) * (-1.0f/2.0f) : (float)part; + } +} + +uint32 vecfPack10i(Vector3Df src, Vector3Df v2) +{ + uint32 sum = 0; + int part; + float v = Min( 1.0f, Max( -1.0f, src.x ) ); + part = v > 0.0f ? (int)( ( v * 511.0f ) + 0.5f ) : (int)( ( v * 512.0f ) - 0.5f ); + sum |= ( (uint32_t)part & 1023 ) << 0; + v = Min( 1.0f, Max( -1.0f, src.y ) ); + part = v > 0.0f ? (int)( ( v * 511.0f ) + 0.5f ) : (int)( ( v * 512.0f ) - 0.5f ); + sum |= ( (uint32_t)part & 1023 ) << 10; + v = Min( 1.0f, Max( -1.0f, src.z ) ); + part = v > 0.0f ? (int)( ( v * 511.0f ) + 0.5f ) : (int)( ( v * 512.0f ) - 0.5f ); + sum |= ( (uint32_t)part & 1023 ) << 20; + if(v2 != null) + { + v = Min( 1.0f, Max( -1.0f, v2.x ) ); // TODO: Compute this properly + part = v > 0.0f ? (int)( ( v * 1.0f ) + 0.5f ) : (int)( ( v * 2.0f ) - 0.5f ); + sum |= ( (uint32_t)part & 3 ) << 30; + } + return sum; +} diff --git a/ecere/src/gfx/3D/models/e3d/e3dRead.ec b/ecere/src/gfx/3D/models/e3d/e3dRead.ec new file mode 100644 index 0000000000..b9a16d199e --- /dev/null +++ b/ecere/src/gfx/3D/models/e3d/e3dRead.ec @@ -0,0 +1,1085 @@ +#ifdef ECERE_STATIC +public import static "ecere" +#else +public import "ecere" +#endif + +import "e3dDefs" + +#define SHARE_INDICES // Define to use mesh.indices and group.baseIndex + +static struct E3DBlockHeader +{ + E3DBlockType type __attribute__((packed)); + uint32 size __attribute__((packed)); +}; + +#define Bool __Bool +#include +#undef Bool + +static byte * decodeLZMA(byte * data, uint encodedSize, uint size, void * options) +{ + byte * uncompressed = new byte[size]; + if(uncompressed) + { + size_t encSize = encodedSize; + size_t destLen = size; + if(LzmaUncompress(uncompressed, &destLen, data + LZMA_PROPS_SIZE, &encSize, data, LZMA_PROPS_SIZE) != SZ_OK) + { + // delete uncompressed buffer upon failure to avoid memory leaks + delete uncompressed; +#ifdef _DEBUG + PrintLn("WARNING: E3D: lzma block could not be decoded!"); +#endif + } + } + return uncompressed; +} + +static String readString(File f) +{ + uint16 len = 0; + char * s = null; + f.Read(&len, sizeof(uint16), 1); + if(len) + { + s = new char[len+1]; + f.Read(s, 1, len); + s[len] = 0; + } + return s; +} + +#if defined(__GNOSIS3__) +TempFile downloadFile(const String url); +#else +File downloadFile(const String url) { return FileOpen(url, read); } +#endif + +// TODO: Review how to handle all this properly... +void freeE3DMaterial(Material material) +{ + // TOCHECK: Are we somehow holding on to textures to re-use them? + if(material.baseMap) + delete material.baseMap; + if(material.envMap) + delete material.envMap; + if(material.bumpMap) + delete material.bumpMap; + if(material.specularMap) + delete material.specularMap; + if(material.reflectMap) + delete material.reflectMap; +} + +void freeE3DMeshMaterials(Mesh mesh) +{ + PrimitiveGroup group; + + for(group = mesh.groups.first; group; group = group.next) + { + if(group.material) + { + freeE3DMaterial(group.material); + group.material = null; + // We are freeing materials separately? + //delete group.material; + } + } +} + +void freeE3DObjectMaterials(Object object) +{ + Object o; + + if(object.mesh) + freeE3DMeshMaterials(object.mesh); + + for(o = object.firstChild; o; o = o.next) + freeE3DObjectMaterials(o); +} + +// Right now this is global and requires a lock... Support supplying optional textures ID map ? +static Mutex texMutex { }; + +class E3DContext : struct +{ + Map texturesByID; + const String path; + AVLTree materials; + + Map materialsByID { }; // Right now this is per E3D... Support supplying optional materials map? Resolve later? + Map meshesByID { }; + Map meshOwned { }; + const String texturesQuery; + Mutex saveCompressedMutex; + + int curTextureID; + bool positiveYUp; + int resolution; + bool compressedTextures; + bool skipTexturesProcessing; +} + +static void readBlocks(E3DContext ctx, File f, DisplaySystem displaySystem, E3DBlockType containerType, uint64 pbStart, uint64 end, void * data) +{ + Object object = data; // data is most often the Mesh... + Mesh mesh = data; + uint64 pos = pbStart; + static int indent = 0; + indent++; + while(end - pos >= sizeof(E3DBlockHeader)) + { + E3DBlockHeader header; + if(f.Read(&header, sizeof(E3DBlockHeader), 1) == 1) + { + uint64 bEnd = pos + header.size; + bool readSubBlocks = false; + void * subData = data; +#if 0 //def _DEBUG + int i; + for(i = 0; i < indent; i++) Print(" "); + PrintLn(header.type, "(", header.size, " bytes)"); +#endif + + pos += sizeof(E3DBlockHeader); + + switch(header.type) + { + case lzma: + { + uint cSize = (uint)(bEnd - pos - sizeof(uint)), size; + byte * cData; + TempFile tmp { }; + f.Read(&size, sizeof(uint), 1); + cData = new byte[cSize]; //size]; + f.Read(cData, 1, cSize); //bEnd - pos); + tmp.buffer = decodeLZMA(cData, cSize, size, null); + if(tmp.buffer) + { + tmp.size = size; + readBlocks(ctx, tmp, displaySystem, containerType, 0, size, subData); + } + delete cData; + delete tmp; + break; + } + case nodes: + case meshes: + case materials: + case textures: + readSubBlocks = true; + break; + case meshNode: + { + if(containerType == nodes) + { + object.flags.root = true; + object.transform.orientation = { 1,0,0,0 }; + object.transform.scaling = { 1,1,1 }; + } + else + { + Object o { }; + object.Add(o); + subData = o; + o.transform.orientation = { 1,0,0,0 }; + o.transform.scaling = { 1,1,1 }; + } + readSubBlocks = true; + break; + } + case scaling: + { + Vector3Df v; + f.Read(&v, sizeof(Vector3Df), 1); + object.transform.scaling = v; + break; + } + case position: + { + Vector3D v; + f.Read(&v, sizeof(Vector3D), 1); + object.transform.position = v; + if(ctx.positiveYUp) + v.y *= -1; + break; + } + case orientation: + { + Quaternion v; + f.Read(&v, sizeof(Quaternion), 1); + object.transform.orientation = v; + break; + } + case material: + { + Material mat = null; + mat.OnCopy(GetDefaultMaterial()); + mat.flags.separateSpecular = true; + subData = mat; + readSubBlocks = true; + break; + } + case materialID: + { + int id = 0; + f.Read(&id, sizeof(int), 1); + ctx.materialsByID[id] = data; + break; + } + case materialFlags: + { + Material mat = data; + E3DMaterialFlags flags; + f.Read(&flags, sizeof(uint), 1); + mat.flags = { doubleSided = flags.doubleSided, translucent = flags.translucent, + tile = flags.wrapU || flags.wrapV, partlyTransparent = flags.partlyTransparent, setupTextures = true }; + // if(flags.wrapU != flags.wrapV) PrintLn("warning!"); + break; + } + case opacity: { Material mat = data; f.Read(&mat.opacity, sizeof(float), 1); break; } + case phongShininess: { Material mat = data; f.Read(&mat.power, sizeof(float), 1); break; } + case diffuse: { Material mat = data; f.Read(&mat.diffuse, sizeof(ColorRGB), 1); break; } + case specular: { Material mat = data; f.Read(&mat.specular, sizeof(ColorRGB), 1); break; } + case ambient: { Material mat = data; f.Read(&mat.ambient, sizeof(ColorRGB), 1); break; } + case emissive: { Material mat = data; f.Read(&mat.emissive, sizeof(ColorRGB), 1); break; } + case phongDiffuseMap: { Material mat = data; subData = &mat.baseMap; readSubBlocks = true; break; } + case normalMap: { Material mat = data; subData = &mat.bumpMap; readSubBlocks = true; break; } + case phongSpecularMap: { Material mat = data; subData = &mat.specularMap; readSubBlocks = true; break; } + // case phongAmbientMap: { Material mat = data; subData = &mat.amb; readSubBlocks = true; break; } + //case pbrSpecDiffuseMap: { Material mat = data; subData = &mat.reflectMap; readSubBlocks = true; break; } + case texture: + { + Bitmap bitmap { }; + subData = bitmap; + incref bitmap; + readSubBlocks = true; + break; + } + case textureName: + { + char * name = readString(f); + Bitmap bitmap; + + if(containerType == texture) + bitmap = data; + else + { + *(Bitmap *)data = bitmap = { }; + } + if(bitmap && !ctx.skipTexturesProcessing) + { + char ext[MAX_EXTENSION]; + char path[MAX_LOCATION]; + File f = null; + const String format = null; + + GetExtension(name, ext); + + if(ctx.texturesQuery && ctx.curTextureID) + { + int id = ctx.curTextureID; + const String authKey = strstr(ctx.texturesQuery, "?authKey="); + int l = authKey ? (int)(authKey - ctx.texturesQuery) : strlen(ctx.texturesQuery); + bool rest = strstr(ctx.texturesQuery, "/textures") ? true : false; + + if(ctx.compressedTextures) strcpy(ext, "etc2"); + + if(ctx.resolution > 0) + { + if(rest) + { + memcpy(path, ctx.texturesQuery, l); + path[l] = 0; + sprintf(path + l, "%d.%s?resolution=%d", id, ext, ctx.resolution); + if(authKey) + strcatf(path, "&%s", authKey + 1); + } + else + sprintf(path, "%s%d&outputFormat=%s&resolution=%d", + ctx.texturesQuery, id, ext, ctx.resolution); // TODO: jpg option... + } + else + { + if(rest) + { + memcpy(path, ctx.texturesQuery, l); + path[l] = 0; + sprintf(path + l, "%d.%s", id, ext); + if(authKey) + strcat(path, authKey); + } + else + sprintf(path, "%s%d&outputFormat=%s", ctx.texturesQuery, id, ext); + } + + if(ctx.saveCompressedMutex) + ctx.saveCompressedMutex.Wait(); + f = FileOpen(path, read); + if(f) + format = ext; + } + else // what of this section? + { + bool isHTTP = strstr(ctx.path, "http://") == path || strstr(ctx.path, "https://") == path; + strcpy(path, ctx.path); + PathCat(path, name); + StripExtension(path); + + if(!isHTTP && ctx.saveCompressedMutex) + ctx.saveCompressedMutex.Wait(); + + if(ctx.resolution) + { + strcat(path, "-256"); + if(ctx.compressedTextures) + { + ChangeExtension(path, "etc2", path); + f = isHTTP ? downloadFile(path) : FileOpen(path, read); + if(f) + format = "etc2"; + } + if(!f) + { + ChangeExtension(path, ext, path); + f = isHTTP ? downloadFile(path) : FileOpen(path, read); + if(f) + format = ext; + } + } + + if(!f) + { + strcpy(path, ctx.path); + PathCat(path, name); + if(ctx.compressedTextures) + { + ChangeExtension(path, "etc2", path); + f = isHTTP ? downloadFile(path) : FileOpen(path, read); + if(f) + format = "etc2"; + } + if(!f) + { + ChangeExtension(path, ext, path); + f = isHTTP ? downloadFile(path) : FileOpen(path, read); + if(f) + format = ext; + } + } + if(isHTTP && ctx.saveCompressedMutex) + ctx.saveCompressedMutex.Wait(); + } + if(f) + { + if(bitmap.LoadFromFile(f, format, null)) + { + if(ctx.saveCompressedMutex) + ctx.saveCompressedMutex.Release(); + if(bitmap.pixelFormat != pixelFormatETC2RGBA8 && !ctx.skipTexturesProcessing) + { + Bitmap bmp = bitmap.ProcessDD((bool)2, 0, ctx.compressedTextures, 16384, true); + bitmap.Copy2(bmp, true); + delete bmp; + + if(ctx.compressedTextures) + { + ChangeExtension(path, "etc2", path); + if(ctx.saveCompressedMutex) + ctx.saveCompressedMutex.Wait(); + bitmap.Save(path, null, null); + if(ctx.saveCompressedMutex) + ctx.saveCompressedMutex.Release(); + } + } + bitmap.MakeMipMaps(displaySystem); + } + else if(ctx.saveCompressedMutex) + ctx.saveCompressedMutex.Release(); + } + else if(ctx.saveCompressedMutex) + ctx.saveCompressedMutex.Release(); + delete f; + } + delete name; + break; + } + case textureID: + { + int id = 0; + f.Read(&id, sizeof(int), 1); + texMutex.Wait(); + if(containerType == texture) + { + if(ctx.texturesByID) + { + if(ctx.texturesByID[id]) + { + delete (Bitmap)data; + readSubBlocks = false; + } + else + { + Bitmap bitmap = data; +#ifdef _DEBUG + if(!bitmap._refCount) + PrintLn("WARNING: E3D/textureID bitmap with 0 refs"); +#endif + //incref bitmap; + ctx.curTextureID = id; + ctx.texturesByID[id] = bitmap; + } + } + } + else + { + Bitmap * bPtr = data; + *bPtr = ctx.texturesByID ? ctx.texturesByID[id] : null; + if(*bPtr) incref (*bPtr); + } + texMutex.Release(); + break; + } + case textureJPG: + case texturePNG: + { + Bitmap bitmap = data; + if(bitmap) + { + TempFile file { }; + int count = (int)(bEnd - pos); + byte * r = new byte[count]; + f.Read(r, 1, count); + file.buffer = r; + file.size = count; + bitmap.LoadFromFile(file, header.type == texturePNG ? "png" : "jpg", null); //displaySystem); + bitmap.MakeMipMaps(displaySystem); + file.StealBuffer(); + delete file; + delete r; + } + break; + } + case E3DBlockType::mesh: + { + subData = Mesh { }; + readSubBlocks = true; + break; + } + case meshID: + { + int id = 0; + f.Read(&id, sizeof(int), 1); + if(containerType == meshNode) + { + object.mesh = ctx.meshesByID[id]; + object.flags.mesh = true; + if(!ctx.meshOwned[id]) + { + object.flags.ownMesh = true; + object.SetMinMaxRadius(true); + ctx.meshOwned[id] = true; + } + else + { + object.flags.ownMesh = false; + object.SetMinMaxRadius(false); + } + } + else + ctx.meshesByID[id] = data; + break; + } + case attributes: + { + int nVertices = 0; + if(mesh && f.Read(&nVertices, sizeof(nVertices), 1)) + { + // TOFIX: forcing interleaved here... + mesh.Allocate({ /*interleaved = true, */vertices = true }, nVertices, displaySystem); + readSubBlocks = true; + } + pos += sizeof(uint); + break; + } + case attrInterleaved: + { + E3DBlockType type; + MeshFeatures features = 0; + MeshFeatures allocedFeatures; + int nVertices = mesh.nVertices; + // bool qVerts = false; + uint vSize = 0; + uint vOffset = 0, nOffset = 0, t1Offset = 0, t2Offset = 0, tnOffset = 0, cOffset = 0; + bool signBitan = false; + while(pos < bEnd && f.Read(&type, sizeof(E3DBlockType), 1)) + { + uint16 offset = 0; + f.Read(&offset, sizeof(uint16), 1); + pos += 4; + if(!type) + { + vSize = offset; + break; + } + switch(type) + { + case attrQVertices: /*qVerts = true; */features.vertices = true; vOffset = offset; break; + case attrVertices: features.vertices = true; vOffset = offset; break; + case attrTexCoords: features.texCoords1 = true; t1Offset = offset; break; + case attrTexCoords2: features.texCoords2 = true; t2Offset = offset; break; + case attrNormals: features.normals = true; nOffset = offset; break; + case attrColors: features.colors = true; cOffset = offset; break; + case attrTangentsSign: features.tangents = true; tnOffset = offset; signBitan = true; break; + case attrTangentsBT: features.tangents = true; tnOffset = offset; break; + } + } + allocedFeatures = features; + allocedFeatures.vertices = true; + if(allocedFeatures == { vertices = true, normals = true, texCoords1 = true }) + allocedFeatures.interleaved = true; // Loading this common format as interleaved for now... + if(vSize && mesh.Allocate(allocedFeatures, nVertices, displaySystem)) + { + bool loadIL = false; + Vector3Df * vertex = null, * normal = null, * tangent = null, * bitangent = null; + Pointf * texCoord = null, * texCoord2 = null; + ColorRGBAf * color = null; + int i; + + if(allocedFeatures.interleaved) + { + loadIL = true; + vertex = features.vertices ? (Vector3Df*)((byte *)mesh.vertices) : null; + normal = features.normals ? (Vector3Df*)((byte *)mesh.vertices + 12) : null; + texCoord = features.texCoords1 ? (Pointf*)((byte *)mesh.vertices + 24) : null; + //tangent = features.tangents ? (Vector3Df*)((byte *)mesh.vertices + tnOffset) : null; + //color = features.colors ? (Vector3Df*)((byte *)mesh.vertices + cOffset) : null; + } + else + { + if(features.vertices) vertex = (Vector3Df*)((byte *)mesh.vertices); + if(features.normals) normal = (Vector3Df*)((byte *)mesh.normals); + if(features.tangents) tangent = (Vector3Df*)((byte *)mesh.tangents); + if(!signBitan && features.tangents) bitangent = mesh.tangents+1; + if(features.texCoords1) texCoord = (Pointf*)((byte *)mesh.texCoords); + // if(features.texCoords2) texCoord2 = (Pointf*)((byte *)mesh.texCoords2); + if(features.colors) color = (ColorRGBAf*)((byte *)mesh.colors + cOffset); + } + + for(i = 0; i < nVertices; i++) + { + uint64 vStart = pos; + if(vertex) + { + f.Seek(vStart + vOffset, start), f.Read(vertex, sizeof(Vector3Df), 1); + if(ctx.positiveYUp) + vertex->y *= -1; + } + if(normal) + { + uint32 n; + f.Seek(vStart + nOffset, start), f.Read(&n, sizeof(uint32), 1); + vecfUnpack10i(n, normal, null); + } + if(texCoord) + f.Seek(vStart + t1Offset, start), f.Read(texCoord, sizeof(Pointf), 1); + if(texCoord2) + f.Seek(vStart + t2Offset, start), f.Read(texCoord2, sizeof(Pointf), 1); + if(features.colors) + { + ColorRGBA c; + f.Seek(vStart + cOffset, start), f.Read(&c, sizeof(uint32), 1); + *color = { c.r/255.0f, c.g/255.0f, c.b/255.0f, c.a/255.0f }; + } + if(tangent) + { + uint32 t, bt; + f.Seek(vStart + tnOffset, start), f.Read(&t, sizeof(uint32), 1); + if(!signBitan) f.Read(&bt, sizeof(uint32), 1); + vecfUnpack10i(t, tangent, null); + vecfUnpack10i(bt, bitangent, null); + } + pos += vSize; + + if(vertex) vertex = (void *)((byte *)vertex + (loadIL ? 32 : 12)); + if(normal) normal = (void *)((byte *)normal + (loadIL ? 32 : 12)); + if(color) color = (void *)((byte *)color + (loadIL ? 32 : 16)); + if(texCoord) texCoord = (void *)((byte *)texCoord + (loadIL ? 32 : 8)); + if(texCoord2)texCoord2= (void *)((byte *)texCoord2+ (loadIL ? 32 : 8)); + if(tangent) tangent = (void *)((byte *)tangent + (loadIL ? 32 : signBitan ? 12 : 24)); + if(bitangent)bitangent= (void *)((byte *)bitangent+ (loadIL ? 32 : 24)); + } + } + break; + } + case triFaces16: + { + int nFaces = 0; + + f.Read(&nFaces, sizeof(nFaces), 1); + +#ifdef SHARE_INDICES + // Assuming triangles for now + { + uint16 * indices16 = new uint16[nFaces * 3]; + int i; + + // TODO: 16 bit indices here? + mesh.indices = new uint32[nFaces * 3]; + mesh.nIndices = nFaces * 3; + f.Read(indices16, sizeof(uint16), nFaces * 3); + for(i = 0; i < nFaces * 3; i++) + mesh.indices[i] = indices16[i]; + delete indices16; + } + if(displaySystem) + { + uint indexSize = sizeof(uint32); + uint size = mesh.nIndices * indexSize; + BlockEntry block; + + if(!mesh.meab) + { + mesh.meab = { }; + mesh.flags.ownMEAB = true; + } + block = mesh.meab.allocate(elements, size); + mesh.baseIndex = block ? block.start / indexSize : -1; + if(mesh.baseIndex != -1) + mesh.meab.ab.upload(block.start, size, mesh.indices); + } + + { + PrimitiveGroup g = mesh.AddPrimitiveGroup({ triangles, indices32bit = true, sharedIndices = true }, nFaces * 3); + if(displaySystem) + g.baseIndex = mesh.baseIndex; + } +#else + // Assuming triangles for now + { + PrimitiveGroup g = mesh.AddPrimitiveGroup({ triangles }, nFaces * 3); + if(mesh.LockPrimitiveGroup(g)) + { + f.Read(g.indices, sizeof(uint16), nFaces * 3); + mesh.UnlockPrimitiveGroup(g); + } + } +#endif + // if(mesh.normals) mesh.ComputeNormals(); + mesh.Unlock(0); + break; + } + case triFaces32: + { + int nFaces = 0; + f.Read(&nFaces, sizeof(nFaces), 1); + +#ifdef SHARE_INDICES + // Assuming triangles for now + mesh.indices = new uint32[nFaces * 3]; + mesh.nIndices = nFaces * 3; + f.Read(mesh.indices, sizeof(uint32), nFaces * 3); + + if(displaySystem) + { + uint indexSize = sizeof(uint32); + uint size = mesh.nIndices * indexSize; + BlockEntry block; + + if(!mesh.meab) + { + mesh.meab = { }; + mesh.flags.ownMEAB = true; + } + block = mesh.meab.allocate(elements, size); + mesh.baseIndex = block ? block.start / indexSize : -1; + if(mesh.baseIndex != -1) + mesh.meab.ab.upload(block.start, size, mesh.indices); + } + + mesh.AddPrimitiveGroup({ triangles, indices32bit = true, sharedIndices = true }, nFaces * 3); +#else + { + PrimitiveGroup g = mesh.AddPrimitiveGroup({ triangles, indices32bit = true }, nFaces * 3); + if(mesh.LockPrimitiveGroup(g)) + { + // Assuming triangles for now + f.Read(g.indices, sizeof(uint32), nFaces * 3); + mesh.UnlockPrimitiveGroup(g); + } + } +#endif + //mesh.ComputeNormals(); + mesh.Unlock(0); + break; + } + case facesMaterials: + { + int start, count, id; + PrimitiveGroup group = mesh.groups.first; + bool is32 = group ? group.type.indices32bit : false; + while(pos < bEnd) + { + f.Read(&start, sizeof(int), 1); + f.Read(&count, sizeof(int), 1); + f.Read(&id, sizeof(int), 1); + pos += 3 * sizeof(int); + + if(count) + { + PrimitiveGroup g; + g = mesh.AddPrimitiveGroup({ triangles, indices32bit = is32 +#ifdef SHARE_INDICES + , sharedIndices = true +#endif + }, 3 * count); +#ifdef SHARE_INDICES + g.baseIndex = mesh.baseIndex + 3 * start; +#else + mesh.LockPrimitiveGroup(g); + if(is32) + memcpy(g.indices32, group.indices32 + 3*start, 3*count * sizeof(uint32)); + else + memcpy(g.indices, group.indices + 3*start, 3*count * sizeof(uint16)); + mesh.UnlockPrimitiveGroup(g); +#endif + + g.material = ctx.materialsByID[id]; + } + } + mesh.FreePrimitiveGroup(group); + break; + } + case parts: + { + uint nParts = (uint)((bEnd - pos) / sizeof(MeshPart)); + Array parts { size = nParts }; + mesh.parts = parts; + f.Read(parts.array, sizeof(MeshPart), parts.count); + break; + } + default: + //PrintLn(" ...skipping"); + break; + } + if(readSubBlocks) + readBlocks(ctx, f, displaySystem, header.type, pos, bEnd, subData); + if(header.type == material) + { + Material mat = subData; + // TODO: Default material? + if(!mat.ambient.r && !mat.ambient.g && !mat.ambient.b) + mat.ambient = mat.diffuse; + + if((mat.diffuse.r != 1.0 || mat.diffuse.g != 1.0 || mat.diffuse.b != 1.0) || + (mat.ambient.r != 1.0 || mat.ambient.g != 1.0 || mat.ambient.b != 1.0) || + (mat.specular.r != 1.0 || mat.specular.g != 1.0 || mat.specular.b != 1.0) || + (mat.emissive.r != 0.0 || mat.emissive.g != 0.0 || mat.emissive.b != 0.0)) + mat.flags.constantColor = true; + + if(ctx.materials) + { + MapIterator it { map = ctx.materialsByID }; + Iterator m { (void *)ctx.materials }; + //uint key; + //Material theM = null; + + while(it.Next() && it.data != mat); + //key = it.key; + + texMutex.Wait(); + if(m.Find(mat)) + { + if(mat) + freeE3DMaterial(mat); + + ctx.materialsByID.SetData(it.pointer, m.data); + } + else + { + AVLNode node = (AVLNode)ctx.materials.Add(mat); + Material mm = node.key; // AVLTree will copy the key now, which we don't want in this case + node.key = mat; + delete mm; + mat = null; + } + texMutex.Release(); + delete mat; + + /*for(i : ctx.materialsByID) + { + Material mm = i; + uint key = &i; + if(i.shader == 0xabababababababab) + printf("Bug"); + }*/ + } + } + if(header.type == meshNode) + { + Object o = subData; + if(o.mesh) + { + o.mesh.ApplyTranslucency(o); + // o.mesh.ComputeNormals(); + } + if(o.flags.root) + o.UpdateTransform(); + } + + f.Seek(bEnd, start); + pos = bEnd; + } + } + indent--; +} + +struct E3DOptions +{ + Map texturesByID; + AVLTree materials; // Not currently resolving IDs globally for materials... + const String texturesQuery; + bool positiveYUp; + int resolution; + bool compressedTextures; + bool skipTexturesProcessing; + + void * lookupTextureContext; + uint (* lookupTextureCB)(void * context, const String model, const String path, uint texID); + Mutex saveCompressedMutex; // TODO: It might be better to have callbacks for loading texures? +}; + +void listTexturesReadBlocks(E3DContext ctx, File f, E3DBlockType containerType, uint64 pbStart, uint64 end, void * data, Array textureList) +{ + uint64 pos = pbStart; + static int indent = 0; + indent++; + while(end - pos >= sizeof(E3DBlockHeader)) + { + E3DBlockHeader header; + if(f.Read(&header, sizeof(E3DBlockHeader), 1) == 1) + { + uint64 bEnd = pos + header.size; + bool readSubBlocks = false; + void * subData = data; +#if 0 //def _DEBUG + int i; + for(i = 0; i < indent; i++) Print(" "); + PrintLn(header.type, "(", header.size, " bytes)"); +#endif + + pos += sizeof(E3DBlockHeader); + + switch(header.type) + { + case lzma: //recurse? + { + uint cSize = (uint)(bEnd - pos - sizeof(uint)), size; + byte * cData; + TempFile tmp { }; + f.Read(&size, sizeof(uint), 1); + cData = new byte[cSize]; //size]; + f.Read(cData, 1, cSize); //bEnd - pos); + tmp.buffer = decodeLZMA(cData, cSize, size, null); + tmp.size = size; + listTexturesReadBlocks(ctx, tmp, containerType, 0, size, subData, textureList); + + delete cData; + delete tmp; + + break; + } + case nodes: + case meshes: + case materials: + case textures: + readSubBlocks = true; + break; + case meshNode: + //readSubBlocks = true; + break; + case scaling: + case position: + case orientation: + break; + case material: + //readSubBlocks = true; + break; + case materialID: + case materialFlags: + case opacity: + case phongShininess: + case diffuse: + case specular: + case ambient: + case emissive: + case phongDiffuseMap: + case normalMap: + case phongSpecularMap: + break; + // case phongAmbientMap: { Material mat = data; subData = &mat.amb; readSubBlocks = true; break; } + //case pbrSpecDiffuseMap: { Material mat = data; subData = &mat.reflectMap; readSubBlocks = true; break; } + case texture: + { + /*Bitmap bitmap { }; + subData = bitmap; + incref bitmap;*/ + readSubBlocks = true; + break; + } + case textureName: + { + char * name = readString(f); + /*Bitmap bitmap; + + if(containerType == texture) + bitmap = data; + else + { + *(Bitmap *)data = bitmap = { }; + } + if(bitmap)*/ + { + char ext[MAX_EXTENSION]; + char path[MAX_LOCATION]; + //const String format = null; + + GetExtension(name, ext); + + if(ctx.texturesQuery && ctx.curTextureID) + { + int id = ctx.curTextureID; + const String authKey = strstr(ctx.texturesQuery, "?authKey="); + int l = authKey ? (int)(authKey - ctx.texturesQuery) : strlen(ctx.texturesQuery); + bool rest = strstr(ctx.texturesQuery, "/textures") ? true : false; + + if(ctx.compressedTextures) strcpy(ext, "etc2"); + + if(ctx.resolution > 0) + { + if(rest) + { + memcpy(path, ctx.texturesQuery, l); + path[l] = 0; + sprintf(path + l, "%d.%s?resolution=%d", id, ext, ctx.resolution); + if(authKey) + strcatf(path, "&%s", authKey + 1); + } + else + sprintf(path, "%s%d&outputFormat=%s&resolution=%d", + ctx.texturesQuery, id, ext, ctx.resolution); // TODO: jpg option... + } + else + { + if(rest) + { + memcpy(path, ctx.texturesQuery, l); + path[l] = 0; + sprintf(path + l, "%d.%s", id, ext); + if(authKey) + strcat(path, authKey); + } + else + sprintf(path, "%s%d&outputFormat=%s", ctx.texturesQuery, id, ext); + } + + //if(!textureList) textureList = { }; + textureList.Add(CopyString(path)); + } + } + delete name; + break; + } + case textureID: + { + int id = 0; + f.Read(&id, sizeof(int), 1); + texMutex.Wait(); + if(containerType == texture) + { + if(ctx.texturesByID) + { + ctx.curTextureID = id; + /* + if(ctx.texturesByID[id]) // is this necessary? + { + //delete (Bitmap)data; + readSubBlocks = false; + } + else + { + + Bitmap bitmap = data; + incref bitmap; + ctx.curTextureID = id; + ctx.texturesByID[id] = bitmap; + }*/ + } + } + else + { + //Bitmap * bPtr = data; + //*bPtr = ctx.texturesByID ? ctx.texturesByID[id] : null; + } + texMutex.Release(); + break; + } + case textureJPG: + case texturePNG: + case E3DBlockType::mesh: + case meshID: + case attributes: + case attrInterleaved: + case triFaces16: + case triFaces32: + case facesMaterials: + break; + + default: + //PrintLn(" ...skipping"); + break; + } + + if(readSubBlocks) + { + // this sometimes has an address when it shouldn't + listTexturesReadBlocks(ctx, f, header.type, pos, bEnd, subData, textureList); + } + + f.Seek(bEnd, start); + pos = bEnd; + } + } + indent--; +} + +void readE3D(File f, const String fileName, Object object, DisplaySystem displaySystem, E3DOptions options) +{ + char path[MAX_LOCATION]; + E3DContext ctx { path = path }; + bool freeTexturesByID = false; + + if(options != null) + { + ctx.texturesByID = options.texturesByID ? options.texturesByID : { }; + ctx.materials = options.materials; + ctx.texturesQuery = options.texturesQuery; + ctx.positiveYUp = options.positiveYUp; + ctx.resolution = options.resolution; + ctx.compressedTextures = options.compressedTextures; + ctx.skipTexturesProcessing = options.skipTexturesProcessing; + ctx.saveCompressedMutex = options.saveCompressedMutex; + } + else + ctx.texturesByID = { }, freeTexturesByID = true; + + StripLastDirectory(fileName, path); + readBlocks(ctx, f, displaySystem, 0, 0, f.GetSize(), object); + + if(freeTexturesByID) + delete ctx.texturesByID; + + delete ctx; +} diff --git a/ecere/src/gfx/3D/models/e3d/e3dWrite.ec b/ecere/src/gfx/3D/models/e3d/e3dWrite.ec new file mode 100644 index 0000000000..3128f3202f --- /dev/null +++ b/ecere/src/gfx/3D/models/e3d/e3dWrite.ec @@ -0,0 +1,766 @@ +#ifdef ECERE_STATIC +public import static "ecere" +#else +public import "ecere" +#endif + +import "e3dDefs" + +struct E3DBlockHeader +{ + E3DBlockType type __attribute__((packed)); // FIXME: __attribute__((packed)) lost by module imports? + uint32 size __attribute__((packed)); +}; + +static bool externalTextures = false; //true; +static int JPEG_QUALITY = 75; +static bool compressed = true; + +#define Bool __Bool +#include +#undef Bool + +class E3DWriteContext : struct +{ + char path[MAX_LOCATION]; + Map texturesToID { }; + Map> meshFaceMaterials { }; + Array materials { }; + Array allMeshes { }; + Map meshToID { }; + Array textures { }; + Array texUsePNG { }; + int firstTexture; + AVLTree texUsed { }; + + ~E3DWriteContext() + { + materials.RemoveAll(); + allMeshes.RemoveAll(); + meshFaceMaterials.Free(); + meshToID.Free(); + + // NOTE: These may have been kept around to re-use textures... + textures.RemoveAll(); + texturesToID.Free(); + texUsePNG.Free(); + } +}; + +static byte * encodeLZMA(byte * data, uint size, uint * encodedSize, void * options) +{ + size_t destLen = (unsigned long)(size + size / 1000 + 12); + byte * compressed = new byte[5+destLen]; + if(compressed) + { + size_t outPropsSize = LZMA_PROPS_SIZE; + LzmaCompress(compressed + LZMA_PROPS_SIZE, &destLen, data, size, compressed, &outPropsSize, + 9, 1 << 26, 4, 4, 4, 64, 1); + *encodedSize = (uint)destLen + LZMA_PROPS_SIZE; + } + return compressed; +} + +void writeE3D(File f, Object object, E3DWriteContext ctx) +{ + Array mainBlocks { }; + + calculateMeshes(ctx, object); + writeE3DBlock(ctx, f, version, object, writeVersion); + if(ctx.textures.count) + mainBlocks.Add({ textures, object, writeTextures }); + if(ctx.materials.count) + mainBlocks.Add({ materials, object, writeMaterials }); + if(ctx.allMeshes.count) + mainBlocks.Add({ meshes, object, writeMeshes }); + mainBlocks.Add({ nodes, object, writeObjects }); + + if(!compressed) + for(b : mainBlocks) + writeE3DBlock(ctx, f, b.type, b.data, b.fn); + else + writeLZMA(ctx, f, mainBlocks); + + delete mainBlocks; + + ctx.materials.RemoveAll(); + ctx.allMeshes.RemoveAll(); + ctx.meshFaceMaterials.Free(); + ctx.meshToID.Free(); + + ctx.firstTexture = ctx.textures.count; + ctx.texUsed.Free(); +} + +static void writeE3DBlock(E3DWriteContext ctx, File f, E3DBlockType type, void * data, void (* fn)(E3DWriteContext ctx, File f, void * data)) +{ + uint64 pos = f.Tell(); + E3DBlockHeader header { type = type }; + f.Write(&header, sizeof(header), 1); + fn(ctx, f, data); + header.size = (uint)(f.Tell() - pos); + f.Seek(pos, start); + f.Write(&header, sizeof(header), 1); + f.Seek(pos + header.size, start); +} + +struct BlockInfo +{ + E3DBlockType type; + void * data; + void (* fn)(E3DWriteContext ctx, File f, void * data); +}; + +static void writeLZMA(E3DWriteContext ctx, File f, Container infos) +{ + TempFile tmp { }; + E3DBlockHeader header { type = lzma }; + byte * cData; + uint size = 0, cSize = 0; + for(i : infos) + writeE3DBlock(ctx, tmp, i.type, i.data, i.fn); + size = tmp.size; + cData = encodeLZMA(tmp.buffer, size, &cSize, null); + delete tmp; + header.size = sizeof(E3DBlockHeader) + sizeof(uint) + cSize; + f.Write(&header, sizeof(header), 1); + f.Write(&size, sizeof(uint), 1); + f.Write(cData, cSize, 1); + delete cData; +} + +static void writeAttributes(E3DWriteContext ctx, File f, Mesh mesh) +{ + int nVertices = mesh.nVertices; + f.Write(&nVertices, sizeof(nVertices), 1); + writeE3DBlock(ctx, f, attrInterleaved, mesh, writeInterleaved); +} + +static void writeColor(E3DWriteContext ctx, File f, ColorRGBAf c) +{ + struct { byte r, g, b, a; } color; + color.r = (byte)Max(0, Min(255, (int)(c.r * 255))); + color.g = (byte)Max(0, Min(255, (int)(c.g * 255))); + color.b = (byte)Max(0, Min(255, (int)(c.b * 255))); + color.a = (byte)Max(0, Min(255, (int)(c.a * 255))); + f.Write(&color, sizeof(color), 1); +} + +static void writeColorRGB(E3DWriteContext ctx, File f, ColorRGB c) +{ + f.Write(c, sizeof(ColorRGB), 1); +} + +static void writeInterleaved(E3DWriteContext ctx, File f, Mesh mesh) // TODO: Add option to write tangent, colors, or remove before calling E3D writer +{ + MeshFeatures features = mesh.flags & ~MeshFeatures { /*tangents = true, */colors = true }; // TWEAKED: Don't write tangents, colors for FLT's + E3DBlockType type; + int i; + int nVertices = mesh.nVertices; + Vector3Df * vertices = null; + Vector3Df * normals = null; + Vector3Df * tangents = null; + ColorRGBAf * colors = null; + Pointf * texCoords = null; + uint16 vSize = 0; + uint vStride = sizeof(Vector3Df); + uint nStride = sizeof(Vector3Df); + uint cStride = sizeof(ColorRGBAf); + uint tStride = sizeof(Pointf); + uint gStride = 2*sizeof(Vector3Df); + + if(features.vertices) + { + vertices = mesh.vertices; + type = attrVertices; + f.Write(&type, sizeof(E3DBlockType), 1); + f.Write(&vSize, sizeof(uint16), 1); + vSize += 12; + if(mesh.flags.interleaved) + vStride = sizeof(float) * 8; + } + if(features.normals) + { + normals = mesh.flags.interleaved ? (Vector3Df *)((byte *)mesh.vertices + 3*sizeof(float)) : mesh.normals; + type = attrNormals; + f.Write(&type, sizeof(E3DBlockType), 1); + f.Write(&vSize, sizeof(uint16), 1); + vSize += 4; + if(mesh.flags.interleaved) + nStride = sizeof(float) * 8; + } + if(features.texCoords1) + { + texCoords = mesh.flags.interleaved ? (Pointf *)((byte *)mesh.vertices + 6*sizeof(float)) : mesh.texCoords; + type = attrTexCoords; + f.Write(&type, sizeof(E3DBlockType), 1); + f.Write(&vSize, sizeof(uint16), 1); + vSize += 8; + if(mesh.flags.interleaved) + tStride = sizeof(float) * 8; + } + if(features.colors) + { + colors = mesh.colors; + type = attrColors; + f.Write(&type, sizeof(E3DBlockType), 1); + f.Write(&vSize, sizeof(uint16), 1); + vSize += 4; + // if(mesh.flags.interleaved) tStride = sizeof(float) * 8; + } + if(features.tangents) + { + tangents = mesh.tangents; + // type = attrTangentsSign; + type = attrTangentsBT; + f.Write(&type, sizeof(E3DBlockType), 1); + f.Write(&vSize, sizeof(uint16), 1); + // vSize += 4; + vSize += 8; + // if(mesh.flags.interleaved) gStride = sizeof(float) * 8; + } + type = 0; + f.Write(&type, sizeof(E3DBlockType), 1); + f.Write(&vSize, sizeof(uint16), 1); + + for(i = 0; i < nVertices; i++) + { + if(vertices) + f.Write((byte *)vertices + vStride * i, sizeof(Vector3Df), 1); + if(normals) + { + uint32 n = vecfPack10i((Vector3Df *)((byte *)normals + nStride * i), null); + f.Write(&n, sizeof(uint), 1); + } + if(texCoords) + f.Write((byte *)texCoords + tStride * i, sizeof(Pointf), 1); + if(features.colors) + writeColor(ctx, f, (ColorRGBAf *)((byte *)colors + i * cStride)); + if(features.tangents) + { + /* + uint32 n = vecfPack10i(tangent0, tangent0 + 1; + f.Write(&n, sizeof(uint), 1); + */ + Vector3Df * tangent0 = (Vector3Df *)((byte *)tangents + gStride * i); + uint32 n = vecfPack10i(tangent0, null); + uint32 b = vecfPack10i(tangent0 + 1, null); + f.Write(&n, sizeof(uint), 1); + f.Write(&b, sizeof(uint), 1); + } + } +} + +static int getFacesCount(PrimitiveGroup g) +{ + int nFaces = 0; + int nIndices = g.nIndices; + switch(g.type.primitiveType) + { + case triangles: nFaces += nIndices / 3; break; + case triStrip: nFaces += nIndices - 2; break; + case triFan: nFaces += nIndices - 2; break; + case quads: nFaces += 2 * (nIndices / 4); break; + case quadStrip: nFaces += 2 * (nIndices - 2)/2; break; + } + return nFaces; +} + +static void writeTriFaces16(E3DWriteContext ctx, File f, Mesh mesh) +{ + int nFaces = 0; + PrimitiveGroup g; + for(g = mesh.groups.first; g; g = g.next) + nFaces += getFacesCount(g); + + f.Write(&nFaces, sizeof(nFaces), 1); + for(g = mesh.groups.first; g; g = g.next) + { + // Assuming triangles for now + int i, gn = g.nIndices; + if(g.type.indices32bit) + { + uint32 * indices = g.indices32; + if(g.type.sharedIndices) + indices = mesh.indices + g.baseIndex; + + for(i = 0; i < gn; i++) + { + uint16 ix = (uint16)indices[i]; + /*if(ix != indices[i]) + PrintLn("Warning 32 bit indices!");*/ + f.Write(&ix, sizeof(uint16), 1); + } + } + else + { + uint16 * indices = g.indices; + if(g.type.sharedIndices) + indices = ((uint16 *)mesh.indices) + g.baseIndex; + + if(g.type == quads) + { + for(i = 0; i < gn; i+=4) + { + f.Write(&indices[i], sizeof(uint16), 1); + f.Write(&indices[i+1], sizeof(uint16), 1); + f.Write(&indices[i+2], sizeof(uint16), 1); + f.Write(&indices[i], sizeof(uint16), 1); + f.Write(&indices[i+2], sizeof(uint16), 1); + f.Write(&indices[i+3], sizeof(uint16), 1); + } + } + else + { + for(i = 0; i < gn; i++) + { + f.Write(&indices[i], sizeof(uint16), 1); + /* + uint16 ix = indices[i]; + if(i > 0) + ix -= indices[i-1]; + f.Write(&ix, sizeof(uint16), 1); + */ + } + } + } + } +} + +static void writeTriFaces32(E3DWriteContext ctx, File f, Mesh mesh) +{ + int nFaces = 0; + PrimitiveGroup g; + for(g = mesh.groups.first; g; g = g.next) + nFaces += getFacesCount(g); + + f.Write(&nFaces, sizeof(nFaces), 1); + for(g = mesh.groups.first; g; g = g.next) + { + // Assuming triangles for now + int i, gn = g.nIndices; + if(g.type.indices32bit) + { + uint32 * indices = g.type.sharedIndices && mesh.indices ? mesh.indices + g.baseIndex : g.indices32; + for(i = 0; i < gn; i++) + f.Write(&indices[i], sizeof(uint32), 1); + } + else + { + uint16 * indices = g.indices; + for(i = 0; i < gn; i++) + { + uint32 ix = indices[i]; + f.Write(&ix, sizeof(uint32), 1); + /* + uint16 ix = indices[i]; + if(i > 0) + ix -= indices[i-1]; + f.Write(&ix, sizeof(uint16), 1); + */ + } + } + } +} + +static void prepareTexture(E3DWriteContext ctx, Bitmap tex, bool usePNG) +{ + if(tex) + { + int texID = ctx.texturesToID[(uintptr)tex]; + if(!texID) + { + texID = ctx.textures.count+1; + ctx.textures.size++; + ctx.texUsePNG.size++; + ctx.texUsePNG[ctx.textures.count-1] = usePNG; + ctx.textures[ctx.textures.count-1] = tex; + ctx.texturesToID[(uintptr)tex] = texID; + } + ctx.texUsed.Add(texID); + } +} + +static void computeFacesMaterials(E3DWriteContext ctx, Mesh mesh) +{ + int nFaces = 0; + int start = 0; + PrimitiveGroup g; + Array faceMaterials { }; + ctx.meshFaceMaterials[(uintptr)mesh] = faceMaterials; + + mesh.UnapplyTranslucency(null); + + for(g = mesh.groups.first; g; g = g.next) + nFaces += getFacesCount(g); + for(g = mesh.groups.first; g; g = g.next) + { + // Assuming triangles for now + int i; + int count = getFacesCount(g); + int materialID = 0; + Material mat = g.material; + if(mat) + { + for(i = 0; i < ctx.materials.count; i++) + if(ctx.materials[i] == mat) + break; + if(i < ctx.materials.count) + materialID = i + 1; + else + { + ctx.materials.size++; + materialID = ctx.materials.count; + ctx.materials[materialID-1] = mat; + + prepareTexture(ctx, mat.baseMap, mat.flags.translucent | mat.flags.partlyTransparent); + prepareTexture(ctx, mat.bumpMap, false); + prepareTexture(ctx, mat.specularMap, false); + prepareTexture(ctx, mat.reflectMap, false); + } + } + if(faceMaterials.count && materialID == faceMaterials[faceMaterials.count-1].material) + faceMaterials[faceMaterials.count-1].count += count; + else + { + faceMaterials.size++; + faceMaterials[faceMaterials.count-1] = { start, count, materialID }; + } + start += count; + } +} + +struct FacesMaterial +{ + int start, count, material; +}; + +static void writeFaceMaterials(E3DWriteContext ctx, File f, Mesh mesh) +{ + Array faceMaterials = ctx.meshFaceMaterials[(uintptr)mesh]; + //PrimitiveGroup g; + //for(g = mesh.groups.first; g; g = g.next) + f.Write(faceMaterials.array, sizeof(FacesMaterial), faceMaterials.count); +} + +static void writeParts(E3DWriteContext ctx, File f, Mesh mesh) +{ + Array parts = mesh.parts; + f.Write(parts.array, sizeof(MeshPart), parts.count); +} + +static void writeMesh(E3DWriteContext ctx, File f, Mesh mesh) +{ + if(mesh) + { + int id = ctx.meshToID[(uintptr)mesh]; + writeE3DBlock(ctx, f, meshID, &id, writeInt); + writeE3DBlock(ctx, f, attributes, mesh, writeAttributes); + if(mesh.nVertices > 65536) + writeE3DBlock(ctx, f, triFaces32, mesh, writeTriFaces32); + else + writeE3DBlock(ctx, f, triFaces16, mesh, writeTriFaces16); + writeE3DBlock(ctx, f, facesMaterials, mesh, writeFaceMaterials); + if(mesh.parts) + writeE3DBlock(ctx, f, parts, mesh, writeParts); + } +} + +void calculateMeshes(E3DWriteContext ctx, Object object) +{ + Object c; + if(object.flags.mesh && object.mesh) + { + Mesh mesh = object.mesh; + if(!ctx.meshToID[(uintptr)mesh]) + { + ctx.meshToID[(uintptr)mesh] = ctx.allMeshes.count+1; + ctx.allMeshes.size++; + ctx.allMeshes[ctx.allMeshes.count-1] = mesh; + computeFacesMaterials(ctx, mesh); + } + } + for(c = object.firstChild; c; c = c.next) + calculateMeshes(ctx, c); +} + +static void writeMeshes(E3DWriteContext ctx, File f, Object object) +{ + for(m : ctx.allMeshes) + writeE3DBlock(ctx, f, mesh, m, writeMesh); +} + +static void writeVector3D(E3DWriteContext ctx, File f, Vector3D v) +{ + f.Write(v, sizeof(Vector3D), 1); +} + +static void writeVector3Df(E3DWriteContext ctx, File f, Vector3Df v) +{ + f.Write(v, sizeof(Vector3Df), 1); +} + +static void writeQuaternion(E3DWriteContext ctx, File f, Quaternion v) +{ + f.Write(v, sizeof(Quaternion), 1); +} + +static void writeObject(E3DWriteContext ctx, File f, Object object) +{ + if(object.mesh) + { + int id = ctx.meshToID[(uintptr)object.mesh]; + writeE3DBlock(ctx, f, meshID, &id, writeInt); + } + if(object.transform.scaling.x != 1 || object.transform.scaling.y != 1 || object.transform.scaling.z != 1) + writeE3DBlock(ctx, f, scaling, object.transform.scaling, writeVector3Df); + if(object.transform.orientation.w != 1 || object.transform.orientation.x || object.transform.orientation.y || object.transform.orientation.z) + writeE3DBlock(ctx, f, orientation, object.transform.orientation, writeQuaternion); + if(object.transform.position.x || object.transform.position.y || object.transform.position.z) + writeE3DBlock(ctx, f, position, object.transform.position, writeVector3D); + if(object.firstChild) + { + Object o; + for(o = object.firstChild; o; o = o.next) + writeE3DBlock(ctx, f, meshNode, o, writeObject); + } +} + +static void writeObjects(E3DWriteContext ctx, File f, Object object) +{ + writeE3DBlock(ctx, f, meshNode, object, writeObject); +} + +struct MaterialInfo +{ + Material material; + int id; +}; + +static void writeMaterials(E3DWriteContext ctx, File f, Object object) +{ + // Write Groups (number of layers, map sizes) + + // Write Material Descriptions + int id = 1; + for(m : ctx.materials) + { + MaterialInfo info { m, id++ }; + writeE3DBlock(ctx, f, material, info, writeMaterial); + } +} + +static void writeInt(E3DWriteContext ctx, File f, int * data) +{ + f.Write(data, sizeof(int), 1); +} + +static void writeTextureID(E3DWriteContext ctx, File f, int * data) +{ + writeE3DBlock(ctx, f, textureID, data, writeInt); +} + +static void writeMaterial(E3DWriteContext ctx, File f, MaterialInfo info) +{ + Material material = info.material; + int group = 0; + MaterialFlags flags = material.flags; + E3DMaterialFlags mFlags { flags.doubleSided, flags.partlyTransparent, flags.translucent, flags.tile, flags.tile }; + writeE3DBlock(ctx, f, materialID, &info.id, writeInt); + if(group) + writeE3DBlock(ctx, f, materialGroup, &group, writeInt); + writeE3DBlock(ctx, f, materialFlags, &mFlags, writeInt); + if(material.opacity < 1) + writeE3DBlock(ctx, f, opacity, &material.opacity, writeInt); + if(material.emissive.r != 1 || material.emissive.g != 1 || material.emissive.b != 1) + writeE3DBlock(ctx, f, emissive, &material.emissive, writeColorRGB); + if(material.bumpMap) + { + int bumpMapID = ctx.texturesToID[(uintptr)material.bumpMap]; + writeE3DBlock(ctx, f, normalMap, &bumpMapID, writeTextureID); + } + + if(material.power) writeE3DBlock(ctx, f, phongShininess, &material.power, writeInt); + if(material.diffuse.r != 1 || material.diffuse.g != 1 || material.diffuse.b != 1) + writeE3DBlock(ctx, f, diffuse, &material.diffuse, writeColorRGB); + if(material.specular.r != 0 || material.specular.g != 0 || material.specular.b != 0) + writeE3DBlock(ctx, f, specular, &material.specular, writeColorRGB); + if(material.ambient.r != material.diffuse.r || + material.ambient.g != material.diffuse.g || + material.ambient.b != material.diffuse.b) + writeE3DBlock(ctx, f, ambient, &material.ambient, writeColorRGB); + if(material.baseMap) + { + int diffuseID = ctx.texturesToID[(uintptr)material.baseMap]; + writeE3DBlock(ctx, f, phongDiffuseMap, &diffuseID, writeTextureID); + } + if(material.specularMap) + { + int specularID = ctx.texturesToID[(uintptr)material.specularMap]; + writeE3DBlock(ctx, f, phongSpecularMap, &specularID, writeTextureID); + } +} + +static struct TextureInfo +{ + Bitmap texture; + int id; + bool usePNG; +}; + +static void writeJPG(E3DWriteContext ctx, File f, Bitmap texture) +{ + TempFile tmp { }; + char fn[100]; + int quality = JPEG_QUALITY; + sprintf(fn, "File://%p", tmp); + if(texture.pixelFormat != pixelFormat888) + { + void * back = texture.driver; + texture.driver = class(LFBDisplayDriver); + texture.Convert(null, pixelFormat888, null); + texture.driver = back; + } + texture.Save(fn, "jpg", &quality); + tmp.Seek(0, start); + f.Write(tmp.buffer, 1, tmp.size); + delete tmp; +} + +static void writePNG(E3DWriteContext ctx, File f, Bitmap texture) +{ + TempFile tmp { }; + char fn[100]; + sprintf(fn, "File://%p", tmp); + if(texture.pixelFormat != pixelFormatRGBA) + { + void * back = texture.driver; + texture.driver = class(LFBDisplayDriver); + texture.Convert(null, pixelFormatRGBA, null); + texture.driver = back; + } + texture.Save(fn, "png", null); + tmp.Seek(0, start); + f.Write(tmp.buffer, 1, tmp.size); + delete tmp; +} + +static void writeString(E3DWriteContext ctx, File f, const String s) +{ + uint16 len = s ? (uint16)strlen(s) : 0; + f.Write(&len, sizeof(uint16), 1); + f.Write(s, 1, len); +} + +static int maxTexSize; + +static void writeTexture(E3DWriteContext ctx, File f, TextureInfo info) +{ + writeE3DBlock(ctx, f, textureID, &info.id, writeInt); + if(externalTextures) + { + char name[256]; + const char * ext = info.usePNG ? "png" : "jpg"; + + if(maxTexSize && info.texture.width > maxTexSize && info.texture.height > maxTexSize) + sprintf(name, "textures/%d-%d.%s", info.id, maxTexSize, ext); + else + sprintf(name, "textures/%d.%s", info.id, ext); + writeE3DBlock(ctx,f, textureName, name, writeString); + + if(info.id - 1 >= ctx.firstTexture) + { + for(maxTexSize : [0, 512, 256]) + { + int quality = JPEG_QUALITY; + PixelFormat wantedFormat = info.usePNG ? pixelFormatRGBA : pixelFormat888; + char path[MAX_LOCATION]; + + strcpy(path, ctx.path); + PathCat(path, "textures"); + if(maxTexSize && info.texture.width > maxTexSize && info.texture.height > maxTexSize) + sprintf(name, "%d-%d.%s", info.id, maxTexSize, ext); + else + sprintf(name, "%d.%s", info.id, ext); + MakeDir(path); + PathCat(path, name); + + if(maxTexSize && info.texture.width > maxTexSize && info.texture.height > maxTexSize) + { + info.texture = resizeTexture(info.texture, maxTexSize); + } + + if(info.texture.pixelFormat != wantedFormat) + { + void * back = info.texture.driver; + info.texture.driver = class(LFBDisplayDriver); + info.texture.Convert(null, wantedFormat, null); + info.texture.driver = back; + } + info.texture.Save(path, ext, info.usePNG ? null : &quality); + } + } + } + else + writeE3DBlock(ctx, f, info.usePNG ? texturePNG : textureJPG, info.texture, info.usePNG ? writePNG : writeJPG); +} + +static void writeTextures(E3DWriteContext ctx, File f, Object object) +{ + int id = 1; + int i; + for(i = 0; i < ctx.textures.count; i++) + { + if(ctx.texUsed.Find(id)) + { + bool usePNG = ctx.texUsePNG[id-1]; + TextureInfo info { ctx.textures[i], id, usePNG }; + writeE3DBlock(ctx, f, texture, info, writeTexture); + } + id++; + } +} + +static void writeVersion(E3DWriteContext ctx, File f, Object object) +{ + uint16 version = 0x0100; + f.Write("E3DF", 1, 4); + f.Write(&version, sizeof(uint16), 1); +} + +static Bitmap resizeTexture(Bitmap bitmap, int size) +{ + // Pre process the bitmap... First make it 32 bit + if((bitmap.width > size && bitmap.height > size) && bitmap.Convert(null, pixelFormat888, null)) + { + uint w = bitmap.width, h = bitmap.height; + w = Min(w, size); + h = Min(h, size); + + if(bitmap.width != w || bitmap.height != h) + { + Bitmap mipMap { }; + if(mipMap.Allocate(null, w, h, w, bitmap.pixelFormat, false)) + { + Surface mipSurface = mipMap.GetSurface(0,0,null); + mipSurface.blend = false; + mipSurface.Filter(bitmap, 0,0,0,0, w, h, bitmap.width, bitmap.height); + delete mipSurface; + delete bitmap.picture; + bitmap.picture = mipMap.picture; + bitmap.size = mipMap.size; + bitmap.stride = mipMap.stride; + bitmap.width = mipMap.width; + bitmap.height = mipMap.height; + bitmap.sizeBytes = mipMap.sizeBytes; + mipMap.picture = null; + //delete mipMap; + //delete bitmap; + //bitmap = mipMap; + } + //else + delete mipMap; + } + } + return bitmap; +} diff --git a/ecere/src/gfx/Bitmap.ec b/ecere/src/gfx/Bitmap.ec index 08fcb5fd9a..b2d4d2383f 100644 --- a/ecere/src/gfx/Bitmap.ec +++ b/ecere/src/gfx/Bitmap.ec @@ -1,7 +1,15 @@ namespace gfx; +define MODELS_TEXTUREARRAY_SIZE = 2048; // 512; + import "Display" +#ifdef ETC2_COMPRESS +default extern void * etc2Compress(float * pixelData, unsigned int width, unsigned int height, + unsigned int * size, unsigned int * dw, unsigned int * dh); +default extern void etc2Free(void * data); +#endif + public class BitmapFormat { class_data const char ** extensions; @@ -265,6 +273,9 @@ public: void * driverData; bool keepData; bool mipMaps; + bool sRGB2Linear; + int numMipMaps; + Bitmap * bitmaps; public: @@ -397,10 +408,17 @@ public: } bool Copy(Bitmap source) + { + return Copy2(source, false); + } + + bool Copy2(Bitmap source, bool moveStuff) { bool result = false; if(source) { + Free(); + // TODO: Watch out for inst stuff width = source.width; height = source.height; @@ -415,21 +433,30 @@ public: displaySystem = source.displaySystem; driver = source.driver; driverData = source.driverData; - - picture = new byte[sizeBytes]; + mipMaps = source.mipMaps; + numMipMaps = source.numMipMaps; + bitmaps = source.bitmaps; palette = source.palette; - allocatePalette = false; - if(picture) + if(!moveStuff) { - memcpy(picture, source.picture, sizeBytes); - result = true; + picture = new byte[sizeBytes]; + allocatePalette = false; + if(source.picture) + memcpy(picture, source.picture, sizeBytes); + else + memset(picture, 0, sizeBytes); } else - memset(picture, 0, sizeBytes); - - if(!result) - Free(); + { + source.picture = null; + source.palette = null; + source.bitmaps = null; + source.numMipMaps = 0; + picture = source.picture; + allocatePalette = source.allocatePalette; + } + result = true; } return result; } @@ -765,7 +792,7 @@ public: bool LoadMipMaps(const char * fileName, const char * type, DisplaySystem displaySystem) { bool result = Load(fileName, type, null); - if(result) + if(result && displaySystem) if(!MakeMipMaps(displaySystem)) { Free(); @@ -777,7 +804,7 @@ public: bool LoadTMipMaps(const char * fileName, const char * type, DisplaySystem displaySystem) { bool result = Load(fileName, type, null); - if(result) + if(result && displaySystem) { transparent = true; if(displaySystem) @@ -852,13 +879,43 @@ public: void Free() { + if(this && bitmaps) + { + uint i; + + for(i = 0; i < numMipMaps; i++) + { + if(bitmaps[i] && pixelFormat == pixelFormatETC2RGBA8) + { + #ifdef ETC2_COMPRESS + etc2Free(bitmaps[i].picture); + bitmaps[i].picture = null; + #else + delete bitmaps[i].picture; + #endif + } + delete bitmaps[i]; + } + delete bitmaps; + } + if(this && pixelFormat == pixelFormatETC2RGBA8) + { +#ifdef ETC2_COMPRESS + if(picture) etc2Free(picture); + picture = null; +#else + delete picture; +#endif + } + if(this && driver) { driver.FreeBitmap(displaySystem, this); + picture = null; driverData = null; driver = class(LFBDisplayDriver); } - if(this && keepData) + if(this && (keepData || !driver)) delete picture; } @@ -944,6 +1001,213 @@ public: } return palette; } + + Bitmap ProcessDD(bool mipMaps, uint cubeMapFace, bool compress, int maxTextureSize, bool makePow2) + { + Bitmap bitmap = this; + Bitmap retValue = null; + Bitmap convBitmap { mipMaps = false }; + bool freeConvBitmap = true; + + convBitmap.width = bitmap.width; + convBitmap.height = bitmap.height; + convBitmap.pixelFormat = bitmap.pixelFormat; + convBitmap.picture = bitmap.picture; + convBitmap.stride = bitmap.stride; + convBitmap.size = bitmap.stride; + convBitmap.transparent = bitmap.transparent; + convBitmap.shadeShift = bitmap.shadeShift; + convBitmap.paletteShades = bitmap.paletteShades; + convBitmap.sizeBytes = bitmap.sizeBytes; + convBitmap.displaySystem = bitmap.displaySystem; + convBitmap.driver = bitmap.driver; + convBitmap.driverData = bitmap.driverData; + + if(bitmap.keepData) + { + convBitmap.picture = new byte[convBitmap.sizeBytes]; + if(bitmap.picture) + memcpy(convBitmap.picture, bitmap.picture, bitmap.sizeBytes); + else + memset(convBitmap.picture, 0, convBitmap.sizeBytes); + } + else + { + convBitmap.picture = bitmap.picture; + bitmap.picture = null; + if(!convBitmap.picture) + // TOCHECK: Why is this happening? + convBitmap.picture = new0 byte[convBitmap.sizeBytes]; + } + + convBitmap.palette = bitmap.palette; + convBitmap.allocatePalette = false; + + // Pre process the bitmap... First make it 32 bit + if(convBitmap.Convert(null, pixelFormat888, null)) + { + int c, level; + uint w = bitmap.width, h = bitmap.height; + bool oldStyleCubeMap = (cubeMapFace >> 3) != 0; + bool result = true; + int numMipMaps = 0; + + if(makePow2) + { + w = pow2i(w); + h = pow2i(h); + } + if(compress) + { + w = (w + 3) & (~3); + h = (h + 3) & (~3); + } + w = Min(w, maxTextureSize); + h = Min(h, maxTextureSize); + + if(mipMaps) + { + if((int)mipMaps == 2) + { + w = MODELS_TEXTUREARRAY_SIZE; //512; + h = MODELS_TEXTUREARRAY_SIZE; //512; + } + else + { + while(w * 2 < h) w *= 2; + while(h * 2 < w) h *= 2; + } + numMipMaps = 1+Max(log2i(w), log2i(h)); + } + + // Switch ARGB to RGBA + { + int size = convBitmap.stride * convBitmap.height; + uint * pic = (uint *)convBitmap.picture; + for(c = 0; c < size; c++) + { + ColorAlpha color = pic[c]; + Color clr = color.color; + pic[c] = ColorRGBA { clr.r, clr.g, clr.b, color.a }; + } + } + + if(cubeMapFace && oldStyleCubeMap) + { + int face = (cubeMapFace & 7) - 1; + if(face == 0 || face == 1 || face == 4 || face == 5) + { + uint w = convBitmap.width; + uint32 * tmp = new uint [convBitmap.width]; + int x, y; + for(y = 0; y < convBitmap.height; y++) + { + uint32 * pic = (uint32 *)((byte *)convBitmap.picture + y * w * 4); + for(x = 0; x < w; x++) + tmp[x] = pic[w-1-x]; + memcpy(pic, tmp, w*4); + } + delete tmp; + } + else if(face == 2 || face == 3) + { + int y; + Bitmap tmp { }; + tmp.Allocate(null, convBitmap.width, convBitmap.height, 0, convBitmap.pixelFormat, false); + for(y = 0; y < convBitmap.height; y++) + { + memcpy(tmp.picture + convBitmap.width * 4 * y, + convBitmap.picture + (convBitmap.height-1-y) * convBitmap.width * 4, + convBitmap.width * 4); + } + memcpy(convBitmap.picture, tmp.picture, convBitmap.sizeBytes); + delete tmp; + } + } + + for(level = 0; result && (w >= 1 || h >= 1); level++, w >>= 1, h >>= 1) + { + Bitmap mipMap; + if(!w) w = 1; + if(!h) h = 1; + if(bitmap.width != w || bitmap.height != h) + { + mipMap = Bitmap { }; + if(mipMap.Allocate(null, w, h, w, pixelFormat888, false)) + { + Surface mipSurface = mipMap.GetSurface(0,0,null); + mipSurface.blend = false; + mipSurface.Filter(convBitmap, 0,0,0,0, w, h, convBitmap.width, convBitmap.height); + delete mipSurface; + } + else + { + result = false; + delete mipMap; + } + } + else + mipMap = convBitmap; + + if(result) + { + if(mipMaps) + { + if(!retValue) + { + retValue = { mipMaps = true }; + retValue.pixelFormat = compress ? pixelFormatETC2RGBA8 : pixelFormatRGBAGL; + retValue.width = w; + retValue.height = h; + retValue.bitmaps = new0 Bitmap[numMipMaps]; + } + if(mipMap == convBitmap) + freeConvBitmap = false; + retValue.bitmaps[retValue.numMipMaps++] = mipMap; + } + else + { + retValue = mipMap; + if(mipMap == convBitmap) + freeConvBitmap = false; + retValue.pixelFormat = compress ? pixelFormatETC2RGBA8 : pixelFormatRGBAGL; + } + } + if(!mipMaps) break; + } +#ifdef ETC2_COMPRESS + if(retValue && compress) + { + for(level = 0; level < (mipMaps ? retValue.numMipMaps : 1); level++) + { + uint size = 0; + int i; + Bitmap mipMap = mipMaps ? retValue.bitmaps[level] : retValue; + void * pixelData = mipMap.picture; + uint width = mipMap.width, height = mipMap.height; + float * fData = new float[width * height * 4]; + + for(i = 0; i < width * height; i++) + { + ColorRGBA * pix = &((ColorRGBA *)pixelData)[i]; + fData[4*i+0] = pix->r / 255.0f; + fData[4*i+1] = pix->g / 255.0f; + fData[4*i+2] = pix->b / 255.0f; + fData[4*i+3] = pix->a / 255.0f; + } + + delete pixelData; + mipMap.picture = etc2Compress(fData, width, height, &size, &width, &height); + mipMap.sizeBytes = size; mipMap.pixelFormat = pixelFormatETC2RGBA8; + delete fData; + } + } +#endif + } + if(freeConvBitmap) + delete convBitmap; + return retValue; + } }; public class CubeMap : Bitmap @@ -956,7 +1220,7 @@ public: for(i = 0; result && i < 6; i++) { char location[MAX_LOCATION]; - Bitmap face = i > 0 ? { } : this; + Bitmap face = i > 0 ? { sRGB2Linear = sRGB2Linear } : this; strcpy(location, names[i]); if(extension) ChangeExtension(location, extension, location); diff --git a/ecere/src/gfx/BitmapResource.ec b/ecere/src/gfx/BitmapResource.ec index eb1a05486a..469e7725f5 100644 --- a/ecere/src/gfx/BitmapResource.ec +++ b/ecere/src/gfx/BitmapResource.ec @@ -33,15 +33,8 @@ public class BitmapResource : Resource ~BitmapResource() { - if(bitmap != null) - { - //if(!grayed) - // Logf("Freeing %s (%d)\n", fileName, bitmap.picture); - // bitmap.Free(bitmap); - delete bitmap; - } - if(fileName) - delete fileName; + delete bitmap; + delete fileName; } void Load(BitmapResource copy, DisplaySystem displaySystem) diff --git a/ecere/src/gfx/Color.ec b/ecere/src/gfx/Color.ec index ced5d2a2e8..b088349e89 100644 --- a/ecere/src/gfx/Color.ec +++ b/ecere/src/gfx/Color.ec @@ -1,5 +1,9 @@ namespace gfx; +#if defined(__linux__) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__i386__) +asm(".symver pow,pow@GLIBC_2.2.5"); +#endif + import "ListBox" import "DropBox" import "ColorPicker" @@ -51,9 +55,9 @@ public: { char tempString[MAX_F_STRING]; const char * result; - bool needClass = false; + ObjectNotationType onType = none; tempString[0] = 0; - result = OnGetString(tempString, null, &needClass); + result = OnGetString(tempString, null, &onType); if(result) string = result; } colorDropBox.contents = string; @@ -62,25 +66,25 @@ public: return colorDropBox; } - const char * OnGetString(char * stringOutput, void * fieldData, bool * needClass) + const char * OnGetString(char * stringOutput, void * fieldData, ObjectNotationType * onType) { const char * string; - if((string = ((DefinedColor)this).class::OnGetString(stringOutput, null, needClass)) || - (string = ((SystemColor)this).class::OnGetString(stringOutput, null, needClass))) + if((string = ((DefinedColor)this).class::OnGetString(stringOutput, null, onType)) || + (string = ((SystemColor)this).class::OnGetString(stringOutput, null, onType))) { - if(needClass) *needClass = false; + if(onType) *onType = none; // TODO: Better document how OnGetString can modify this... return string; } else { char tempString[256]; const char * colorValue; - bool subNeedClass; + ObjectNotationType subONType = none; int value; tempString[0] = '\0'; value = r; - colorValue = value.OnGetString(tempString, null, &subNeedClass); + colorValue = value.OnGetString(tempString, null, &subONType); strcpy(stringOutput, "r = "); strcat(stringOutput, colorValue); // Weird bug in VS @@ -88,7 +92,7 @@ public: tempString[0] = '\0'; value = g; - colorValue = value.OnGetString(tempString, null, &subNeedClass); + colorValue = value.OnGetString(tempString, null, &subONType); strcat(stringOutput, ", g = "); strcat(stringOutput, colorValue); // Weird bug in VS @@ -96,7 +100,7 @@ public: tempString[0] = '\0'; value = b; - colorValue = value.OnGetString(tempString, null, &subNeedClass); + colorValue = value.OnGetString(tempString, null, &subONType); strcat(stringOutput, ", b = "); strcat(stringOutput, colorValue); return stringOutput; } @@ -107,6 +111,16 @@ public: if(!atoi(string) && (((DefinedColor)this).class::OnGetDataFromString(string) || ((SystemColor)this).class::OnGetDataFromString(string))) return true; + else if(string[0] == '#') + { + this = (Color)strtoul(string+1, null, 16); + return true; + } + else if((strlen(string) == 6 || strlen(string) == 8) && ishexdigit(string[0])) + { + this = (Color)strtoul(string, null, 16); + return true; + } else return class::OnGetDataFromString(string); } @@ -114,14 +128,14 @@ public: void OnDisplay(Surface surface, int x, int y, int width, void * fieldData, Alignment alignment, DataDisplayFlags displayFlags) { char tempString[1024] = ""; - bool needClass = false; + ObjectNotationType onType = none; // TODO: This isn't an ideal way of obtaining the clipped height, will fail on hidden areas // This doesn't seem to help anymore? // - Makes SavingDataBox draw at 2 different spots depending if active or not. // - Color property in IDE is fine as well // - How is it on Linux? int yOffset = 0;//(1+surface.box.bottom - surface.box.top - 17)/2; - const char * string = OnGetString(tempString, null, &needClass); + const char * string = OnGetString(tempString, null, &onType); surface.WriteTextDots(alignment, x + 24, y + 1, width - 24, string, strlen(string)); // Erase background? @@ -654,8 +668,8 @@ private class ColorValue : Color { char tempString[1024] = ""; - bool needClass = false; - const char * string = color.OnGetString(tempString, null, &needClass); + ObjectNotationType onType = none; + const char * string = color.OnGetString(tempString, null, &onType); contents = string; } OnKeyDown(escape, 0); @@ -714,14 +728,15 @@ private class ColorValue : Color //master.OnKeyDown(Escape, 0); incref this; + dialog.master = master; if(dialog.Modal() == ok) { color = dialog.color; ((DataBox)master).SetData(&color, false); { char tempString[1024] = ""; - bool needClass = false; - const char * string = color.OnGetString(tempString, null, &needClass); + ObjectNotationType onType = none; + const char * string = color.OnGetString(tempString, null, &onType); contents = string; } } @@ -860,8 +875,8 @@ private class ColorValue : Color else { char tempString[1024] = ""; - bool needClass = false; - const char * string = colorDropBox.color.OnGetString(tempString, null, &needClass); + ObjectNotationType onType = none; + const char * string = colorDropBox.color.OnGetString(tempString, null, &onType); colorDropBox.contents = string; } return true; diff --git a/ecere/src/gfx/Display.ec b/ecere/src/gfx/Display.ec index 9d74303514..53e89b22be 100644 --- a/ecere/src/gfx/Display.ec +++ b/ecere/src/gfx/Display.ec @@ -17,7 +17,7 @@ import "BitmapResource" import "LFBDisplayDriver" // TOFIX: Temporary until we pass Display instead of DisplaySystem to FontExtent -#if defined(__WIN32__) && !defined(ECERE_NOTRUETYPE) +#if defined(__WIN32__) && !defined(ECERE_NOTRUETYPE) && !defined(__UWP__) import "GDIDisplayDriver" #endif @@ -31,14 +31,18 @@ import "Quaternion" import "Vector3D" #endif -#if (!defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER)) +#if (!defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL)) import "OpenGLDisplayDriver" -#define near _near -#define far _far +#define Size Size_ +#define near near_ +#define far far_ +#define String String_ #include "gl123es.h" #undef near #undef far +#undef Size +#undef String #endif public class GLCapabilities : uint @@ -65,8 +69,14 @@ public: // bool mapBuffer :1; bool debug :1; + bool gpuCommands :1; + bool mdei :1; + + GLSamplesCount samples:3; }; +public enum GLSamplesCount { none, ms2, ms4, ms8, ms16 }; + public enum RenderState { fillMode = 1, depthTest, depthWrite, fogDensity, fogColor, blend, ambient, alphaWrite, antiAlias, vSync }; public union RenderStateFloat { float f; uint ui; }; @@ -201,17 +211,17 @@ public: virtual bool ::LockMesh(DisplaySystem, Mesh, MeshFeatures flags); virtual void ::UnlockMesh(DisplaySystem, Mesh, MeshFeatures flags); virtual void * ::AllocateIndices(DisplaySystem, int nIndices, bool indices32bit); - virtual void ::FreeIndices(DisplaySystem, void * indices); - virtual uint16 * ::LockIndices(DisplaySystem, void * indices); - virtual void ::UnlockIndices(DisplaySystem, void * indices, bool indices32bit, int nIndices); + virtual void ::FreeIndices(DisplaySystem, PrimitiveSingle group); + virtual uint16 * ::LockIndices(DisplaySystem, PrimitiveSingle group); + virtual void ::UnlockIndices(DisplaySystem, PrimitiveSingle group, bool indices32bit, int nIndices, void * maeb); virtual void ::SelectMesh(Display, Mesh); virtual void ::ApplyMaterial(Display, Material, Mesh); - virtual void ::DrawPrimitives(Display, PrimitiveSingle *, Mesh mesh); + virtual void ::DrawPrimitives(Display, PrimitiveSingle, Mesh mesh); virtual void ::PushMatrix(Display); virtual void ::PopMatrix(Display, bool); virtual void ::SetTransform(Display, Matrix, bool, bool); #endif - virtual void ::SetBlitTint(Display, Surface, ColorAlpha); + virtual void ::SetBlitTint(Display, Surface, ColorAlpha); // REVIEW: Is it really necessary to have a method here, we have a Surface member }; public enum Alignment { left, right, center }; @@ -222,7 +232,8 @@ define textCellH = 16; public enum PixelFormat // : byte MESSES UP GuiApplication { - pixelFormat4, pixelFormat8, pixelFormat444, pixelFormat555, pixelFormat565, pixelFormat888, pixelFormatAlpha, pixelFormatText, pixelFormatRGBA + pixelFormat4, pixelFormat8, pixelFormat444, pixelFormat555, pixelFormat565, pixelFormat888, pixelFormatAlpha, pixelFormatText, pixelFormatRGBA, + pixelFormatA16, pixelFormatRGBAGL /* TODO: clarify pixelFormatRGBA vs. GL-ready */, pixelFormatETC2RGBA8 }; public enum Resolution : int { @@ -293,6 +304,7 @@ struct SortPrimitive Object object; Vector3Df middle; Vector3Df min, max; + Material material; Plane plane; bool marked; @@ -308,25 +320,29 @@ struct SortPrimitive return 1; else if(value<-EPSILON) return -1; + else if((uintptr)material < (uintptr)primitive2.material) + return -1; + else if((uintptr)material > (uintptr)primitive2.material) + return 1; else return 0; } - bool ZOverlap(SortPrimitive poly2) + private static inline bool ZOverlap(SortPrimitive poly2) { - if(min.z > poly2.max.z - EPSILON || poly2.min.z > max.z - EPSILON) + if(min.z > poly2.max.z - EPSILON || poly2.min.z > max.z - EPSILON) return false; - return true; + return true; } /* bool XYOverlap(SortPrimitive poly2) { - if(min.x > poly2.max.x - EPSILON || poly2.min.x > max.x - EPSILON ) + if(min.x > poly2.max.x - EPSILON || poly2.min.x > max.x - EPSILON ) return false; - if(min.y > poly2.max.y - EPSILON || poly2.min.y > max.y - EPSILON ) + if(min.y > poly2.max.y - EPSILON || poly2.min.y > max.y - EPSILON ) return false; - return true; + return true; } bool SurfaceOutside(SortPrimitive poly2) @@ -358,10 +374,10 @@ struct SortPrimitive if(surface < EPSILON) { - result = false; + result = false; break; } - } + } if(result == true) return true; @@ -395,25 +411,25 @@ struct SortPrimitive vertex.MultMatrix(local, matrix); surface = a * vertex.x + b * vertex.y + c * vertex.z + d; - if(surface > -EPSILON) + if(surface > -EPSILON) { result = false; break; } - } + } if(result == true) return true; else - return result; + return result; } bool ShouldBeSwapped(SortPrimitive poly2) { - if (!XYOverlap(poly2)) return false; - if (SurfaceOutside(poly2)) return false; - if (SurfaceInside(poly2)) return false; - return true; + if (!XYOverlap(poly2)) return false; + if (SurfaceOutside(poly2)) return false; + if (SurfaceInside(poly2)) return false; + return true; } */ }; @@ -422,6 +438,17 @@ struct SortPrimitive #define MAX_CLIP_POINTS 50 +#if !defined(_GLES) && !defined(_GLES2) +#define USE_32_BIT_INDICES true +#define uintindex uint32 +#else +#define USE_32_BIT_INDICES false +#define uintindex uint16 +#endif + +static uintindex * transIndices = null; +static int transSize = 0; + public class Display { public: @@ -430,6 +457,11 @@ public: if(displaySystem) { displaySystem.numDisplays--; + if(!displaySystem.numDisplays) + { + transSize = 0; + delete transIndices; + } displaySystem.driver.DestroyDisplay(this); } #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D) @@ -531,7 +563,7 @@ public: void FontExtent2(Font font, const char * text, int len, int * width, int * height, int prevGlyph, int * rPrevGlyph, int * overHang) { // Fix for OnLoadGraphics time alpha blended window text extent on GDI -#if defined(__WIN32__) && !defined(ECERE_NOTRUETYPE) +#if defined(__WIN32__) && !defined(ECERE_NOTRUETYPE) && !defined(__UWP__) if(this && alphaBlend && pixelFormat == pixelFormat888 && displaySystem.driver == class(GDIDisplayDriver)) { @@ -611,8 +643,9 @@ public: #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D) // *** 3D GRAPHICS *** - void SetCamera(Surface surface, Camera camera) + void SetCamera(Surface surface, const Camera camera) { + Camera cam = (void *)camera; if(!display3D) { display3D = Display3D { }; @@ -620,7 +653,7 @@ public: if(!display3D.selection) DrawTranslucency(); - if(!camera) + if(!cam) { if(!display3D.selection) displaySystem.driver.SelectMesh(this, null); @@ -630,19 +663,19 @@ public: } if(!display3D.selection) { - displaySystem.driver.SetCamera(this, surface, camera); + displaySystem.driver.SetCamera(this, surface, cam); } - this.display3D.camera = camera; + this.display3D.camera = cam; - if(camera) + if(cam) { - if(!camera.focalX) - camera.Setup(width, height, null); + if(!cam.focalX) + cam.Setup(width, height, null); // Always calling Update() here had broken interpolation in OrbitWithMouse! - if(!camera.cAngle.w && surface) - camera.Update(); + if(!cam.cAngle.w && surface) + cam.Update(); if(display3D.selection) { @@ -653,15 +686,15 @@ public: Angle fovLeft, fovRight, fovTop, fovBottom; ClippingPlane c; - double l = camera.origin.x - (display3D.pickX - display3D.pickWidth/2.0f); - double r = camera.origin.x - (display3D.pickX + display3D.pickWidth/2.0f); - double t = (display3D.pickY - display3D.pickHeight/2.0f) - camera.origin.y; - double b = (display3D.pickY + display3D.pickHeight/2.0f) - camera.origin.y; + double l = cam.origin.x - (display3D.pickX - display3D.pickWidth/2.0f); + double r = cam.origin.x - (display3D.pickX + display3D.pickWidth/2.0f); + double t = (display3D.pickY - display3D.pickHeight/2.0f) - cam.origin.y; + double b = (display3D.pickY + display3D.pickHeight/2.0f) - cam.origin.y; - fovLeft = atan(l / camera.focalX); - fovRight = atan(r / camera.focalX); - fovTop = atan(t / camera.focalY); - fovBottom = atan(b / camera.focalY); + fovLeft = atan(l / cam.focalX); + fovRight = atan(r / cam.focalX); + fovTop = atan(t / cam.focalY); + fovBottom = atan(b / cam.focalY); // --- Left --- quat.Yaw(fovLeft - Pi/2); @@ -685,16 +718,16 @@ public: // --- Near --- normal.x = 0; normal.y = 0; normal.z = 1; - point.z = camera.zMin; + point.z = cam.zMin; display3D.viewPickingPlanes[near].FromPointNormal(normal, point); // --- Far --- normal.x = 0; normal.y = 0; normal.z = -1; - point.z = camera.zMax; + point.z = cam.zMax; display3D.viewPickingPlanes[far].FromPointNormal(normal, point); for(c = 0; cfirst; + subclass(DisplayDriver) driver = displaySystem.driver; + ObjectFlags flags = *&object.flags; + int nPrimitives = mesh.nPrimitives; + + if(!objectMaterial) objectMaterial = defaultMaterial; - if(mesh.groups.first) + if(group) { - PrimitiveGroup group; - displaySystem.driver.SelectMesh(this, mesh); - display3D.mesh = mesh; + Material curMaterial = display3D.material; - for(group = mesh.groups.first; group; group = group.next) + if(display3D.mesh != mesh) { - Material material = group.material ? group.material : objectMaterial; - if(!material) material = defaultMaterial; +#if !defined(_GLES) && !defined(_GLES2) && !defined(ECERE_NOGL) + if(!mesh.mab || !display3D.mesh || display3D.mesh.mab != mesh.mab) +#endif + driver.SelectMesh(this, mesh); + } + display3D.mesh = mesh; - if(material != display3D.material) + for(; group; group = group.next) + { + Material material = group.material; + if(!material) material = objectMaterial; + partlyTransparent |= material.flags & MaterialFlags { partlyTransparent = true }; + if(material != curMaterial) { - display3D.material = material; - displaySystem.driver.ApplyMaterial(this, material, mesh); + curMaterial = material; + driver.ApplyMaterial(this, material, mesh); } // *** Render Vertex Arrays *** - displaySystem.driver.DrawPrimitives(this, (PrimitiveSingle *)&group.type, mesh); + driver.DrawPrimitives(this, (PrimitiveSingle *)&group.type, mesh); } + display3D.material = curMaterial; } - if(object.flags.translucent) + if(partlyTransparent) + display3D.partlyTransparentObjects.Add(object); + if(flags.translucent) { Matrix matrix; Matrix inverse, inverseTranspose; int c; - if(object.flags.viewSpace) + if(flags.viewSpace) matrix = object.matrix; else { @@ -854,17 +914,19 @@ public: inverse.Inverse(matrix); inverseTranspose.Transpose(inverse); - for(c = 0; c < mesh.nPrimitives; c++) + for(c = 0; c < nPrimitives; c++) { PrimitiveSingle * triangle = &mesh.primitives[c]; SortPrimitive * sort; Plane * plane = &triangle->plane; + if(!triangle->material || !triangle->material.opacity) continue; if(display3D.nTriangles >= display3D.maxTriangles) { display3D.maxTriangles = display3D.maxTriangles ? (display3D.maxTriangles * 3 / 2) : 32768; display3D.triangles = renew display3D.triangles SortPrimitive[display3D.maxTriangles]; } sort = &display3D.triangles[display3D.nTriangles++]; + sort->material = triangle->material; sort->object = object; sort->triangle = triangle; sort->middle.MultMatrix(triangle->middle, matrix); @@ -876,16 +938,22 @@ public: plane->d * inverseTranspose.m[3][3]; } } - else + else if(nPrimitives) { int c; - displaySystem.driver.SelectMesh(this, mesh); + PrimitiveSingle * primitives = mesh.primitives; + + if(!group && display3D.mesh != mesh +#if !defined(ECERE_NOGL) + && (!mesh.mab || !display3D.mesh || display3D.mesh.mab != mesh.mab) +#endif + ) + displaySystem.driver.SelectMesh(this, mesh); display3D.mesh = mesh; - for(c = 0; cmaterial ? primitive->material : objectMaterial; if(!material) material = defaultMaterial; @@ -894,7 +962,6 @@ public: display3D.material = material; displaySystem.driver.ApplyMaterial(this, material, mesh); } - displaySystem.driver.DrawPrimitives(this, primitive, display3D.mesh); } } @@ -906,14 +973,22 @@ public: bool IsObjectVisible(Object object) { Plane * planes; - if(display3D.selection || !display3D.camera) - planes = object.flags.viewSpace ? display3D.viewPickingPlanes : display3D.worldPickingPlanes; + ObjectFlags flags = *&object.flags; + Display3D display3D = this.display3D; + Camera camera = display3D.camera; + if(display3D.selection || !camera) + planes = flags.viewSpace ? display3D.viewPickingPlanes : display3D.worldPickingPlanes; else - planes = object.flags.viewSpace ? display3D.camera.viewClippingPlanes : display3D.camera.worldClippingPlanes; + planes = flags.viewSpace ? camera.viewClippingPlanes : camera.worldClippingPlanes; return object.InsideFrustum(planes) != outside; } bool DrawObject(Object object) + { + return DrawObjectEx(object, null); + } + + bool DrawObjectEx(Object object, uint64 * id) { bool result = false; if(object && object.volume) @@ -921,12 +996,15 @@ public: Object child; FrustumPlacement visible; Plane * planes; + Display3D display3D = this.display3D; Camera camera = display3D.camera; + ObjectFlags flags = *&object.flags; + bool viewSpace = flags.viewSpace; if(display3D.selection || !camera) - planes = object.flags.viewSpace ? display3D.viewPickingPlanes : display3D.worldPickingPlanes; + planes = viewSpace ? display3D.viewPickingPlanes : display3D.worldPickingPlanes; else - planes = object.flags.viewSpace ? camera.viewClippingPlanes : camera.worldClippingPlanes; + planes = viewSpace ? camera.viewClippingPlanes : camera.worldClippingPlanes; visible = object.InsideFrustum(planes); @@ -934,7 +1012,7 @@ public: { if(display3D.collectingHits && object.tag) { - /*if(object.flags.root) + /*if(flags.root) this.tags[display3D.tagIndex] = object.tag; else if(object.tag) this.tags[++display3D.tagIndex] = object.tag; @@ -942,13 +1020,15 @@ public: display3D.tags[display3D.tagIndex++] = object.tag; } - if(object.flags.mesh && object.mesh) + if(flags.mesh && object.mesh) { + Vector3D wresult, vresult; + if(!display3D.selection && displaySystem.driver.PushMatrix) displaySystem.driver.PushMatrix(this); #if ENABLE_GL_FFP - if(object.mesh.tangents && object.mesh.normals && object.flags.computeLightVectors) + if(object.mesh.tangents && object.mesh.normals && flags.computeLightVectors) { Mesh mesh = object.mesh; if(!glCaps_shaders) @@ -1077,25 +1157,24 @@ public: } #endif - SetTransform(object.matrix, object.flags.viewSpace); + SetTransform(&object.matrix, viewSpace); if(display3D.selection) { if(visible == intersecting || display3D.intersecting) { Vector3D rayIntersect; - if(display3D.PickMesh(object, rayIntersect)) + if(display3D.PickMeshEx(object, rayIntersect, id)) { if(display3D.intersecting) { - Vector3D wresult, vresult; wresult.MultMatrix(rayIntersect, object.matrix); - if(!object.flags.viewSpace) + if(!viewSpace) camera.TransformPoint(vresult, wresult); else vresult = wresult; if(vresult.z < display3D.rayIntersect.z) - display3D.rayIntersect = vresult; + display3D.rayIntersect = vresult; // Returned ray intersect is in viewspace display3D.intersected = true; } result = true; @@ -1122,7 +1201,9 @@ public: hit.tags[c] = display3D.tags[c]; } - if(!object.flags.viewSpace) + if(display3D.intersecting) + hit.center = vresult; + else if(!viewSpace) camera.TransformPoint(hit.center, object.wcenter); else hit.center = object.wcenter; @@ -1130,24 +1211,75 @@ public: } for(child = object.children.first; child; child = child.next) - result |= DrawObject(child); + result |= DrawObjectEx(child, id); - if(display3D.collectingHits && /*!object.flags.root && */object.tag) + if(display3D.collectingHits && /*!flags.root && */object.tag) display3D.tagIndex--; } } return result; } + private void DrawPartlyTransparentMesh(Object object) + { + if(!display3D.selection) + { + Mesh mesh = object.mesh; + Material objectMaterial = object.material; + PrimitiveGroup group = (&mesh.groups)->first; + if(group) + { + subclass(DisplayDriver) driver = displaySystem.driver; + if(display3D.mesh != mesh +#if !defined(ECERE_NOGL) + && (!mesh.mab || !display3D.mesh || display3D.mesh.mab != mesh.mab) +#endif + ) + driver.SelectMesh(this, mesh); + display3D.mesh = mesh; + + for(; group; group = group.next) + { + Material material = group.material ? group.material : objectMaterial; + if(!material) material = defaultMaterial; + if(material.flags.partlyTransparent) + { + if(material != display3D.material) + { + display3D.material = material; + material.flags.partlyTransparent = false; + driver.ApplyMaterial(this, material, mesh); + material.flags.partlyTransparent = true; + } + + // *** Render Vertex Arrays *** + driver.DrawPrimitives(this, (PrimitiveSingle *)&group.type, mesh); + } + } + } + } + } + void DrawTranslucency(void) { if(display3D && display3D.camera) { // *** Render translucent primitives *** - if(display3D.nTriangles) + if(display3D.nTriangles || display3D.partlyTransparentObjects.count) { Matrix * matrix = null; int c; + int toFlush = 0; + #define NUM_ROTATE_BUFS 40 +#if !defined(ECERE_NOGL) + static GLEAB transBuffer[NUM_ROTATE_BUFS]; +#endif + static int bufSizes[NUM_ROTATE_BUFS]; + static int bufID = 0; + Object * partlyTransparentObjects = display3D.partlyTransparentObjects.array; + int count = display3D.partlyTransparentObjects.count; + subclass(DisplayDriver) driver = displaySystem.driver; + int i; blend = true; @@ -1155,59 +1287,154 @@ public: depthWrite = false; - displaySystem.driver.PushMatrix(this); - for(c=0; c transSize) + { + transSize = Max(transSize, display3D.nTriangles * 6 /*3*/); + transIndices = renew transIndices uintindex[display3D.nTriangles * 6 /*3*/]; + } + + driver.PushMatrix(this); + display3D.material = null; + display3D.mesh = null; + + for(i = 0; i < count; i++) + { + Object o = partlyTransparentObjects[i]; + if(o.mvMatrix[0]) + { +#if !defined(ECERE_NOGL) + glmsLoadMatrixf(o.mvMatrix); // TODO: Handle this properly... +#endif + DrawPartlyTransparentMesh(o); + } + else + { + driver.PushMatrix(this); + SetTransform(&o.matrix, (*&o.flags).viewSpace); + DrawPartlyTransparentMesh(o); + driver.PopMatrix(this, true); + } + } + display3D.partlyTransparentObjects.minAllocSize = display3D.partlyTransparentObjects.size; + display3D.partlyTransparentObjects.size = 0; + + for(c=0; c<=display3D.nTriangles; c++) { - SortPrimitive * sort = &display3D.triangles[c]; - Mesh mesh = sort->object.mesh; - PrimitiveSingle * primitive = sort->triangle; - Material material; + bool past = c == display3D.nTriangles; + SortPrimitive * sort = past ? null : &display3D.triangles[c]; + Mesh mesh = past ? null : *&sort->object.mesh; + PrimitiveSingle * primitive = past ? null : sort->triangle; + Material material = past ? null : primitive->material ? primitive->material : sort->object.material; + bool newMatrix, newMesh, newMaterial; + if(!material) material = defaultMaterial; + newMatrix = past ? false : &sort->object.matrix != matrix; + newMesh = past ? false : (display3D.mesh != mesh +#if !defined(ECERE_NOGL) + && (!mesh.mab || !display3D.mesh || display3D.mesh.mab != mesh.mab) +#endif + ); + newMaterial = past ? false : material != display3D.material; - if(&sort->object.matrix != matrix) + if(past || newMatrix || newMesh || newMaterial) + { + // TODO: Was translucency support for Direct3D broken? +#if !defined(ECERE_NOGL) + if(!transBuffer[bufID].buffer || toFlush > bufSizes[bufID]) + { + transBuffer[bufID].allocate(toFlush * sizeof(uint32), null, streamDraw); + bufSizes[bufID] = toFlush; + } + if(toFlush) + { + transBuffer[bufID].upload(0, toFlush * sizeof(uint32), transIndices); + transBuffer[bufID].draw(GL_TRIANGLES, toFlush, + USE_32_BIT_INDICES ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, glCaps_vertexBuffer ? 0 : transIndices); + toFlush = 0; + bufID++; + if(bufID >= NUM_ROTATE_BUFS) + bufID = 0; + } +#endif + if(past) + break; + } + + + if(newMatrix) { matrix = &sort->object.matrix; - displaySystem.driver.PopMatrix(this, false); - displaySystem.driver.PushMatrix(this); + driver.PopMatrix(this, false); + driver.PushMatrix(this); SetTransform(matrix, sort->object.flags.viewSpace); } - if(mesh != display3D.mesh) + if(newMesh) { - displaySystem.driver.SelectMesh(this, mesh); + driver.SelectMesh(this, mesh); display3D.mesh = mesh; } - material = primitive->material ? primitive->material : sort->object.material; - if(!material) material = defaultMaterial; - - if(material != display3D.material) + if(newMaterial) { - displaySystem.driver.ApplyMaterial(this, material, display3D.mesh); + driver.ApplyMaterial(this, material, display3D.mesh); display3D.material = material; } /* + if(primitive->type.vertexRange) { - Material testMaterial { }; - float amount; - - amount = (display3D.triangles[0].middle.z - display3D.triangles[c].middle.z) / - (display3D.triangles[0].middle.z - display3D.triangles[display3D.nTriangles-1].middle.z); - - testMaterial.flags.doubleSided = { doubleSided = true, translucent = true }; - testMaterial.diffuse.a = 1; - testMaterial.emissive.r = testMaterial.emissive.g = testMaterial.emissive.b = amount; - testMaterial.baseMap = material->baseMap; - - displaySystem.driver.ApplyMaterial(this, testMaterial, display3D.mesh); + transIndices[toFlush+0] = primitive->first; + transIndices[toFlush+1] = primitive->first+1; + transIndices[toFlush+2] = primitive->first+2; + toFlush += 3; + if(primitive->type == quads) + { + transIndices[toFlush+0] = primitive->first; + transIndices[toFlush+1] = primitive->first+2; + transIndices[toFlush+2] = primitive->first+3; + toFlush += 3; + } + } + else*/ + { + if(primitive->type.indices32bit) + { + transIndices[toFlush+0] = (uintindex)primitive->indices32[0]; + transIndices[toFlush+1] = (uintindex)primitive->indices32[1]; + transIndices[toFlush+2] = (uintindex)primitive->indices32[2]; + } + else + { + transIndices[toFlush+0] = primitive->indices[0]; + transIndices[toFlush+1] = primitive->indices[1]; + transIndices[toFlush+2] = primitive->indices[2]; + } + toFlush += 3; + if(primitive->type == quads) + { + if(primitive->type.indices32bit) + { + transIndices[toFlush+0] = (uintindex)primitive->indices32[0]; + transIndices[toFlush+1] = (uintindex)primitive->indices32[2]; + transIndices[toFlush+2] = (uintindex)primitive->indices32[3]; + } + else + { + transIndices[toFlush+0] = primitive->indices[0]; + transIndices[toFlush+1] = primitive->indices[2]; + transIndices[toFlush+2] = primitive->indices[3]; + } + toFlush += 3; + } } - */ - - // *** Render primitive *** - // if(sort->plane.d > 0) - displaySystem.driver.DrawPrimitives(this, primitive, display3D.mesh); } - displaySystem.driver.PopMatrix(this, true); + +#if !defined(ECERE_NOGL) + GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +#endif + + driver.PopMatrix(this, true); display3D.nTriangles = 0; blend = false; @@ -1250,6 +1477,7 @@ public: display3D.intersecting = true; } + // Document that returned ray intersect is in viewspace bool GetIntersect(Vector3D intersect) { intersect = display3D.rayIntersect; @@ -1282,13 +1510,21 @@ public: property bool useSharedMemory { set { useSharedMemory = value; } get { return useSharedMemory; } }; property void * systemWindow { get { return window; } }; property DisplaySystem displaySystem { get { return displaySystem; } }; -#if !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) +#if !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && !defined(ECERE_NO3D) property GLCapabilities glCapabilities { - get { return ((OGLDisplay)driverData).capabilities; } + get + { +#if !defined(ECERE_NOGL) + return ((OGLDisplay)driverData).capabilities; +#else + return 0; +#endif + } set { glCapabilities = value; +#if !defined(ECERE_NOGL) if(driverData && displaySystem.driver == class(OpenGLDisplayDriver)) { OGLDisplay oglDisplay = driverData; @@ -1309,8 +1545,20 @@ public: OpenGLDisplayDriver::initialDisplaySetup(this, true, false); Unlock(); } +#endif } }; + property int glVersion + { + get + { +#if !defined(ECERE_NOGL) + return ((OGLDisplay)driverData).version; +#else + return 0; +#endif + } + } #endif int width, height; @@ -1333,10 +1581,41 @@ private: void * windowDriverData; bool useSharedMemory; GLCapabilities glCapabilities; - glCapabilities = { true, true, true, true, true, true, true, true }; + glCapabilities = { true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, ms16 }; }; #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D) + +// Möller and Trumbore +static inline bool intersectTriangle(const Vector3Df a, const Vector3Df b, const Vector3Df c, const Line r, double * kPtr) +{ + bool result = false; + Vector3D e1 { b.x - a.x, b.y - a.y, b.z - a.z }; + Vector3D e2 { c.x - a.x, c.y - a.y, c.z - a.z }; + Vector3D n; + double det; + const Vector3D * dir = &r.delta; + + n.CrossProduct(e1, e2); + det = -dir->DotProduct(n); + if(Abs(det) >= 1e-6) + { + double invDet = 1.0 / det; + Vector3D ao { r.p0.x - a.x, r.p0.y - a.y, r.p0.z - a.z }; + Vector3D dao; + double u, v, k; + + dao.CrossProduct(ao, dir); + u = dao.DotProduct(e2) * invDet; + v = -dao.DotProduct(e1) * invDet; + k = ao.DotProduct(n) * invDet; + result = k >= 0.0 && u >= 0.0 && v >= 0.0 && (u+v) <= 1.0; + if(kPtr) + *kPtr = k; + } + return result; +} + private class Display3D : struct { // 3D Display @@ -1346,6 +1625,7 @@ private class Display3D : struct Vector3D points[MAX_CLIP_POINTS]; Vector3D newPoints[MAX_CLIP_POINTS]; byte goodPoints[MAX_CLIP_POINTS]; + Array partlyTransparentObjects { }; Material material; Mesh mesh; @@ -1362,6 +1642,7 @@ private class Display3D : struct Line rayView, rayWorld, rayLocal; Vector3D rayIntersect; float light0Pos[4]; + Vector3D rlInvDelta; ~Display3D() { @@ -1385,239 +1666,316 @@ private class Display3D : struct return id; } - //#define TRESHOLD -1 - //#define TRESHOLD -0.25 - #define TRESHOLD -0.0025 + //#define THRESHOLD -1 + //#define THRESHOLD -0.25 + #define THRESHOLD -0.0025 bool PickPrimitives(Mesh mesh, PrimitiveSingle primitive, Vector3D rayDiff, Vector3D rayIntersect) + { + return PickPrimitivesEx(mesh, primitive, rayDiff, rayIntersect, 0, null); + } + + bool PickPrimitivesEx(Mesh mesh, PrimitiveSingle primitive, Vector3D rayDiff, Vector3D rayIntersect, + int groupIx, uint64 * id) { Plane * planes = localPickingPlanes; + bool useMollerTrumbore = !planes || (!pickWidth && !pickHeight); // We need rayLocal to be set for MollerTrumbore int c = 0; int nIndex = 1, nPoints = 1; - int offset = 0; bool result = false; Vector3D * points = this.points; Vector3D * newPoints = this.newPoints; byte * goodPoints = this.goodPoints; int nVertices = primitive.type.vertexRange ? primitive.nVertices : primitive.nIndices; int strip = 1; - Vector3Df tmp; bool i32bit = primitive.type.indices32bit; - uint32 * indices32 = primitive.indices32; - uint16 * indices16 = primitive.indices; - - switch(primitive.type.primitiveType) + uint32 * indices32 = !i32bit ? null : + primitive.indices32 != null ? primitive.indices32 : mesh.indices ? mesh.indices + primitive.baseIndex : null; + uint16 * indices16 = !i32bit && primitive.indices != null ? primitive.indices : null; + Array parts = mesh.parts; + int pi; + int firstPart = 0, lastPart = 0; + float * vertices = (float *)mesh.vertices; + int vStride = mesh.flags.interleaved ? 8 : 3; + + if(!vertices) return false; // Need vertices here... + + // Parts are currently in the Mesh rather than the PrimitiveGroup, assuming the group order (restarting at ix 0) + // However picking currently does not seem to support groups with shared indices (baseIndex) + if(parts && id) { - case triangles: nIndex = 3; nPoints = 3; break; - case quads: nIndex = 4; nPoints = 4; break; - case triStrip: - case triFan: - nIndex = 1; nPoints = 3; - offset = 2; - tmp = primitive.type.vertexRange ? mesh.vertices[primitive.first] : mesh.vertices[(i32bit ? indices32[0] : indices16[0])]; - points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z }; - tmp = primitive.type.vertexRange ? mesh.vertices[primitive.first+1] : mesh.vertices[(i32bit ? indices32[1] : indices16[1])]; - points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z }; - break; + int gix = -1; + for(pi = 0; pi < parts.count; pi++) + { + MeshPart * part = parts && id ? &parts[pi] : null; + if(part->start == 0) + { + if(++gix == groupIx) + lastPart = firstPart = pi; + if(gix > groupIx) + break; + } + else if(gix == groupIx) + lastPart = pi; + } } - for(c = offset; cstart : 0; + int end = start + (part ? part->count : nVertices); // TOCHECK: Fix part to be indices, rather than tris? + bool done = false; + const Vector3Df * vx[4]; + + switch(primitive.type.primitiveType) + { + case triangles: nIndex = 3; nPoints = 3; break; + case quads: nIndex = 4; nPoints = 4; break; + case triStrip: + case triFan: + nIndex = 1; nPoints = 3; + offset = 2; + vx[0] = (Vector3Df *)(vertices + vStride * (primitive.type.vertexRange ? primitive.first : (i32bit ? indices32[start + 0] : indices16[start + 0]))); + vx[1] = (Vector3Df *)(vertices + vStride * (primitive.type.vertexRange ? primitive.first+1 : (i32bit ? indices32[start + 1] : indices16[start + 1]))); + break; + } + + for(c = start + offset; c < end; c += nIndex) { - int p; - int n = nPoints; + bool outside = false; int i; if(primitive.type.vertexRange) { if(primitive.type.primitiveType == triStrip) { - tmp = mesh.vertices[primitive.first + (c & 1) ? (c - 1) : (c - 2)]; - points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z }; - tmp = mesh.vertices[primitive.first + (c & 1) ? (c - 2) : (c - 1)]; - points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z }; + vx[0] = (Vector3Df *)(vertices + vStride * (primitive.first + (c & 1) ? (c - 1) : (c - 2))); + vx[1] = (Vector3Df *)(vertices + vStride * (primitive.first + (c & 1) ? (c - 2) : (c - 1))); } else if(primitive.type.primitiveType == triFan) { - tmp = mesh.vertices[primitive.first + 0]; - points[0] = { (double)tmp.x, (double)tmp.y, (double)tmp.z }; - tmp = mesh.vertices[primitive.first + c - 1]; - points[1] = { (double)tmp.x, (double)tmp.y, (double)tmp.z }; + vx[0] = (Vector3Df *)(vertices + vStride * (primitive.first + 0)); + vx[1] = (Vector3Df *)(vertices + vStride * (primitive.first + c - 1)); } for(i = 0; inormal.DotProduct(points[i]); - double distance = dot + plane->d; - if(distance > TRESHOLD) + // Classic method clipping consecutively against 6 picking planes akin to a smaller view frustum + ClippingPlane p; + int n = nPoints; + + points[0] = { (double)vx[0]->x, (double)vx[0]->y, (double)vx[0]->z }; + points[1] = { (double)vx[1]->x, (double)vx[1]->y, (double)vx[1]->z }; + points[2] = { (double)vx[2]->x, (double)vx[2]->y, (double)vx[2]->z }; + if(nPoints > 3) + points[3] = { (double)vx[3]->x, (double)vx[3]->y, (double)vx[3]->z }; + for(p = 0; p < 6; p++) { - numGoodPoints++; - goodPoints[i] = 1; - } - } - if(!numGoodPoints) - { - outside = true; - break; - } - if(numGoodPoints < n) - { - // Clip the polygon - int newN = 0; - int lastGood = -1; - int j; + const Plane * plane = &planes[p]; + int i; + int numGoodPoints = 0; - for(j = 0; jnormal.DotProduct(points[i]); + double distance = dot + plane->d; + if(distance > THRESHOLD) + { + numGoodPoints++; + goodPoints[i] = 1; + } } - else + if(!numGoodPoints) { - Line edge; - int next; - - if(lastGood == -1) - for(lastGood = n-1; !goodPoints[lastGood]; lastGood--); - - edge.p0 = points[lastGood]; - edge.delta.Subtract(points[j], edge.p0); - plane->IntersectLine(edge, newPoints[newN++]); + outside = true; + break; + } + if(numGoodPoints < n) + { + // Clip the polygon + int newN = 0; + int lastGood = -1; + int j; - for(next = j+1; next != j; next++) + for(j = 0; jIntersectLine(edge, newPoints[newN++]); - break; + + for(next = j+1; next != j; next++) + { + if(next == n) next = 0; + if(goodPoints[next]) + { + int prev = next - 1; + if(prev < 0) prev = n-1; + + edge.p0 = points[prev]; + edge.delta.Subtract(points[next], edge.p0); + plane->IntersectLine(edge, newPoints[newN++]); + break; + } + } + if(next <= j) + break; + else + j = next; } } - if(next <= j) - break; - else - j = next; + // Use the new points for the next planes... + memcpy(points, newPoints, newN * sizeof(Vector3D)); + n = newN; } } - // Use the new points - memcpy(points, newPoints, newN * sizeof(Vector3D)); - n = newN; } - } - } - if(!outside) - { - result = true; + else + { + // Möller and Trumbore supporting arbitrary ray used for a null pick aperture + double k; + bool doesIntersect = intersectTriangle(vx[0], vx[1], vx[2], rayLocal, &k); + if(!doesIntersect && nPoints > 3) + doesIntersect = intersectTriangle(vx[0], vx[2], vx[3], rayLocal, &k); + if(doesIntersect) + { + result = true; + if(intersecting) + { + // TOCHECK: GetIntersect() currently returns view space intersect, but here we return local intersect + Vector3D intersect = rayLocal.p0; + intersect.x += k * rayLocal.delta.x; + intersect.y += k * rayLocal.delta.y; + intersect.z += k * rayLocal.delta.z; - // TODO: Implement intersection with TriStrip, TriFan... - if(intersecting) + if(k < rayDiff.z) + { + if(part && id) + *id = part->id; + rayDiff.z = k; + rayIntersect = intersect; + } + } + else + { + done = true; + break; + } + } + else + outside = true; + } + } + + if(!outside && !useMollerTrumbore) { - // Intersect primitives - Plane plane; - Vector3D intersect, diff; - int i0 = c, i1 = c+1, i2 = c+2; + result = true; - if(primitive.type.primitiveType == triStrip) + if(intersecting) { - i0 = (c & 1) ? (c - 1) : (c - 2); - i1 = (c & 1) ? (c - 2) : (c - 1); - i2 = c; - } - else if(primitive.type.primitiveType == triFan) - { - i0 = 0; - i1 = c - 1; - i2 = c; - } + // Intersect primitives + Plane plane; + Vector3D intersect; - if(primitive.type.vertexRange) - plane.FromPointsf( - mesh.vertices[primitive.first + i0], - mesh.vertices[primitive.first + i1], - mesh.vertices[primitive.first + i2]); + // not using 'points' as they are clipped and may all be the same here + plane.FromPointsf(vx[0], vx[1], vx[2]); + + if(plane.a || plane.b || plane.c) // Avoid bad colinear triangles... + { + Vector3D diff; + double k; + + plane.IntersectLine(rayLocal, intersect); + + // In local space + diff.Subtract(intersect, rayLocal.p0); + diff.x = (intersect.x - rayLocal.p0.x) * rlInvDelta.x; // These should be the k of p = p0 + k * delta + diff.y = (intersect.y - rayLocal.p0.y) * rlInvDelta.y; // but delta components may be 0 in local space + diff.z = (intersect.z - rayLocal.p0.z) * rlInvDelta.z; + k = Min(Min(diff.x, diff.y), diff.z); + if(k < rayDiff.z) + { + if(part && id) + *id = part->id; + + rayDiff.z = k; + rayIntersect = intersect; + } + } + else + { +#ifdef _DEBUG + // PrintLn("WARNING: Colinear triangle"); +#endif + } + } else - plane.FromPointsf( - mesh.vertices[(i32bit ? indices32[i0] : indices16[i0])], - mesh.vertices[(i32bit ? indices32[i1] : indices16[i1])], - mesh.vertices[(i32bit ? indices32[i2] : indices16[i2])]); - - plane.IntersectLine(rayLocal, intersect); - diff.Subtract(intersect, rayLocal.p0); - diff.x /= rayLocal.delta.x; - diff.y /= rayLocal.delta.y; - diff.z /= rayLocal.delta.z; - if(diff.x < rayDiff.x || diff.y < rayDiff.y || diff.z < rayDiff.z) { - rayDiff = diff; - rayIntersect = intersect; + done = true; + break; } } - else - break; - } - switch(primitive.type) - { - case triStrip: - points[strip] = points[2]; - strip ^= 1; - break; - case triFan: - points[1] = points[2]; - break; + switch(primitive.type) + { + case triStrip: + vx[strip] = vx[2]; + strip ^= 1; + break; + case triFan: + vx[1] = vx[2]; + break; + } } + if(done) + break; } return result; } bool PickMesh(Object object, Vector3D rayIntersect) + { + return PickMeshEx(object, rayIntersect, null); + } + + bool PickMeshEx(Object object, Vector3D rayIntersect, uint64 * id) { Mesh mesh = object.mesh; bool result = false; @@ -1628,18 +1986,21 @@ private class Display3D : struct if(mesh.groups.first) { PrimitiveGroup group; + int groupIX = 0; for(group = mesh.groups.first; group; group = group.next) { - if(PickPrimitives(mesh, (PrimitiveSingle *)&group.type, rayDiff, rayIntersect)) + if(PickPrimitivesEx(mesh, (PrimitiveSingle *)&group.type, rayDiff, rayIntersect, groupIX, id)) { result = true; if(!intersecting) break; } + groupIX++; } } - else + + if(mesh.nPrimitives) { int c; for(c = 0; c < mesh.nPrimitives; c++) @@ -1665,10 +2026,12 @@ private class Display3D : struct SortPrimitive * sort = &triangles[c]; Mesh mesh = sort->object.mesh; PrimitiveSingle * primitive = sort->triangle; - Vector3Df min { MAXFLOAT, MAXFLOAT, MAXFLOAT }; + Vector3Df min { MAXFLOAT, MAXFLOAT, MAXFLOAT }; Vector3Df max { -MAXFLOAT, -MAXFLOAT, -MAXFLOAT }; int v; bool ix32 = primitive->type.indices32bit; + float * vertices = (float *)mesh.vertices; + uint vStride = mesh.flags.interleaved ? 8 : 3; if(object != sort->object) { object = sort->object; @@ -1687,24 +2050,24 @@ private class Display3D : struct for(v = 0; vnIndices; v++) { - Vector3Df * local = &mesh.vertices[ix32 ? primitive->indices32[v] : primitive->indices[v]]; + Vector3Df * local = (Vector3Df *)(vertices + vStride * (ix32 ? primitive->indices32[v] : primitive->indices[v])); Vector3Df vertex; vertex.MultMatrix(local, &matrix); - if(vertex.x > max.x) max.x = vertex.x; + if(vertex.x > max.x) max.x = vertex.x; if(vertex.y > max.y) max.y = vertex.y; if(vertex.z > max.z) max.z = vertex.z; - if(vertex.x < min.x) min.x = vertex.x; + if(vertex.x < min.x) min.x = vertex.x; if(vertex.y < min.y) min.y = vertex.y; if(vertex.z < min.z) min.z = vertex.z; - } + } sort->min = min; sort->max = max; sort->marked = false; - } + } /* Logf("========= Before Sort ==========\n"); @@ -1730,7 +2093,7 @@ private class Display3D : struct // Logf(" Local %f, %f, %f:\n", local->x, local->y, local->z); Logf(" View %f, %f, %f:\n", vertex.x, vertex.y, vertex.z); - } + } Logf("Min %f, %f, %f:\n", sort->min.x, sort->min.y, sort->min.z); Logf("Max %f, %f, %f:\n", sort->max.x, sort->max.y, sort->max.z); @@ -1763,7 +2126,7 @@ private class Display3D : struct // Logf(" Local %f, %f, %f:\n", local->x, local->y, local->z); Logf(" View %f, %f, %f:\n", vertex.x, vertex.y, vertex.z); - } + } Logf("Min %f, %f, %f:\n", sort->min.x, sort->min.y, sort->min.z); Logf("Max %f, %f, %f:\n", sort->max.x, sort->max.y, sort->max.z); @@ -1905,7 +2268,7 @@ private class Display3D : struct *poly2 = temp; } } - } + } } */ @@ -2030,11 +2393,11 @@ static Material defaultMaterial opacity = 1.0f, diffuse = { 1.0f, 1.0f, 1.0f }, ambient = { 1.0f, 1.0f, 1.0f }, - flags = { doubleSided = true, noFog = true }; + flags = { doubleSided = true, noFog = true, setupTextures = true, update = true }; }; #endif -static byte colorDepthShifts[PixelFormat] = { 0,0,1,1,1,2,0,1,2 }; +static byte colorDepthShifts[PixelFormat] = { 0,0,1,1,1,2,0,1,2,1 }; static Size resolutions[Resolution] = { {80,25}, @@ -2045,7 +2408,7 @@ static Size resolutions[Resolution] = {1024,768},{1152,864},{1280,1024},{1600,1200}, {768,480} }; -static int colorDepths[PixelFormat] = {4,8,12,15,16,32,8,16,32}; +static int colorDepths[PixelFormat] = {4,8,12,15,16,32,8,16,32,16}; // --- Query utilities --- @@ -2129,3 +2492,125 @@ DisplaySystem GetDisplaySystem(const char * driverName) subclass(DisplayDriver) displayDriver = GetDisplayDriver(driverName); return displayDriver ? displayDriver.displaySystem : null; } + +#if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D) +public struct DrawSlot +{ + Object object; + Bitmap baseMap; + + int OnCompare(DrawSlot b) + { + /* + uintptr ma = (uintptr)*&(object.mesh), mb = (uintptr)*&(b.object.mesh); + Mesh mesh1 = (Mesh)ma, mesh2 = (Mesh)mb; + PrimitiveGroup g1 = (&mesh1.groups)->first; + PrimitiveGroup g2 = (&mesh2.groups)->first; + Material mat1 = g1 ? *&g1.material : null; + Material mat2 = g2 ? *&g2.material : null; + + if(mat1.baseMap < mat2.baseMap) return -1; + if(mat1.baseMap > mat2.baseMap) return 1; + if(mat1 < mat2) return -1; + if(mat1 > mat2) return 1; + if(ma < mb) return -1; + if(ma > mb) return 1; + */ + + uintptr ma = (uintptr)*&(object.mesh), mb = (uintptr)*&(b.object.mesh); + if(baseMap < b.baseMap) return -1; + if(baseMap > b.baseMap) return 1; + if(ma < mb) return -1; + if(ma > mb) return 1; + return 0; + } +}; + +public class DrawList +{ + Matrix sm, svm; + //Vector3Df sms; + Vector3D cp; + Plane * vp, * wp; + Array slots { }; + +public: + void init(Matrix sm, Matrix vm, Vector3D cp, Plane * vp, Plane * wp, int guessCount) + { + this.sm = sm; + svm.Multiply(vm, sm); + this.cp = cp; + this.vp = vp; + this.wp = wp; + //sms = { Sgn(sm.m[0][0]), Sgn(sm.m[1][1]), Sgn(sm.m[2][2]) }; + + slots.minAllocSize = Max(slots.minAllocSize, guessCount); + slots.size = 0; + } + + void addObject(Object object) + { + if(object) + { + ObjectFlags flags = *&object.flags; + bool viewSpace = flags.viewSpace; + FrustumPlacement visible = object.InsideFrustum(viewSpace ? vp : wp); + if(visible) + { + Object child; + Mesh mesh = flags.mesh ? *&object.mesh : null; + /*Object parent = viewSpace ? null : object.parent; + Vector3Df scaling = object.transform.scaling; + object.mvs = parent ? parent.mvs : sms; + object.mvs.x *= scaling.x; + object.mvs.y *= scaling.y; + object.mvs.z *= scaling.z;*/ + + if(mesh) + { + DrawSlot * slot = &slots[slots.count++]; + PrimitiveGroup g = (&mesh.groups)->first; + Material mat = g ? *&g.material : null; + slot->object = object; + slot->baseMap = mat ? mat.baseMap : null; + object.setTransform(sm, svm, cp); + } + for(child = (*&object.children).first; child; child = child.next) + addObject(child); + } + } + } + + void prepare() + { + slots.Sort(true); + } + + void render(Display display) + { + int i, count = this.slots.count; + DrawSlot * slots = this.slots.array; +#if !defined(ECERE_NOGL) + Shader shader = DefaultShader::shader(); + glmsFlushMatrices(); + glEnable(GL_CULL_FACE); +#endif + for(i = 0; i < count; i++) + { + Object object = slots[i].object; +#if !defined(ECERE_NOGL) + if(glCaps_shaders) + shader.updateMatrix(modelView, object.mvMatrix, /*object.mvs,*/ false); +#if ENABLE_GL_FFP + else + glLoadMatrixf(object.mvMatrix); // GLLoadMatrixf +#endif +#endif + display.DrawMesh(object); + } +#if !defined(ECERE_NOGL) + GLLoadMatrixd((double *)&svm); +#endif + } +} +#endif diff --git a/ecere/src/gfx/DisplaySystem.ec b/ecere/src/gfx/DisplaySystem.ec index 114e3dceb2..15e55be4d1 100644 --- a/ecere/src/gfx/DisplaySystem.ec +++ b/ecere/src/gfx/DisplaySystem.ec @@ -39,7 +39,7 @@ public: class_no_expansion; ~DisplaySystem() { -#if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D) +#if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) OldLink mesh; materials.Free(Material::Free); @@ -142,7 +142,7 @@ public: } } - void * LoadResource(Resource resource) + void * _LoadResource(Resource resource, void * fm) { DisplaySystemResPtr res; for(res = resources.first; res; res = res.next) @@ -162,8 +162,32 @@ public: resources.Add(res); // This will load e.g. the Bitmap * - res.resource.Load(resource, this); +#if !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) + if(fm) + { + ((FontResource)res.resource).LoadFM((FontResource)resource, this, fm); + } + else +#endif + res.resource.Load(resource, this); } +#if !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) + else if(fm) + { + FontResource fr = (FontResource)res.resource; + if(!fr.fmFont) + { + FontManager fMgr = (FontManager)fm; + fr.fmFont = fMgr.getFont(fr); + fr.fm = fm; + } + if(!fr.font) + { + fr.font = LoadFont(fr.faceName, fr.size, fr.flags); + fr.displaySystem = this; + } + } +#endif // This would copy e.g. the Bitmap * incref res.resource; resource.Reference(res.resource); @@ -171,6 +195,18 @@ public: return res; } + void * LoadResource(Resource resource) + { + return _LoadResource(resource, null); + } + +#if !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) + void * LoadResourceFM(Resource resource, FontManager fm) + { + return _LoadResource(resource, fm); + } +#endif + void UnloadResource(Resource resource, DisplaySystemResPtr res) { // This would clear e.g. the Bitmap * @@ -270,14 +306,14 @@ public: incref bitmap; item.name = new char[strlen(name) + 1]; strcpy(item.name, name); - textures.AddName(item); + if(this) textures.AddName(item); } return item; } Bitmap GetTexture(const char * name) { - return textures.FindNamedLink(name, false); + return this ? textures.FindNamedLink(name, false) : null; } bool RemoveTexture(const char * name) diff --git a/ecere/src/gfx/FontResource.ec b/ecere/src/gfx/FontResource.ec index 12e3eaafdb..d37161ee30 100644 --- a/ecere/src/gfx/FontResource.ec +++ b/ecere/src/gfx/FontResource.ec @@ -1,6 +1,9 @@ namespace gfx; import "Window" +#if !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) +import "fmFontManager" +#endif public class FontResource : Resource { @@ -14,6 +17,9 @@ public: property Window window { set { if(value) { value.RemoveResource(this); value.AddResource(this); } } }; property float outlineSize { set { outlineSize = value; } get { return this ? outlineSize : 0; } }; property float outlineFade { set { outlineFade = value; } get { return this ? outlineFade : 0; } }; +#if !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) + property FMFont fmFont { get { return this ? fmFont : null; } }; +#endif private: char * faceName; @@ -22,6 +28,10 @@ private: FontFlags flags; DisplaySystem displaySystem; float outlineSize, outlineFade; +#if !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) + FontManager fm; + FMFont fmFont; +#endif void Load(FontResource copy, DisplaySystem displaySystem) { @@ -38,6 +48,18 @@ private: } } +#if !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) + void LoadFM(FontResource copy, DisplaySystem displaySystem, FontManager fm) + { + Load(copy, displaySystem); + if(fm) + { + this.fm = fm; + fmFont = fm.getFont(this); + } + } +#endif + void Reference(FontResource reference) { delete faceName; @@ -47,27 +69,48 @@ private: *&outlineSize = *&reference.outlineSize; *&outlineFade = *&reference.outlineFade; font = reference.font; +#if !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) + fmFont = reference.fmFont; +#endif } void Dereference() { font = null; +#if !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) + fmFont = null; +#endif } ~FontResource() { if(font && displaySystem) displaySystem.UnloadFont(font); +#if !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) + if(fmFont && fm) + fm.removeFont(fmFont); +#endif delete faceName; } void OnCopy(FontResource newData) { - property::size = newData.size; - property::faceName = newData.faceName; - property::bold = newData.bold; - property::outlineSize = newData.outlineSize; - property::outlineFade = newData.outlineFade; + if(newData) + { + size = newData.size; + delete faceName; faceName = CopyString(newData.faceName); + flags = newData.flags; + outlineSize = newData.outlineSize; + outlineFade = newData.outlineFade; + } + else + { + size = 0; + flags = 0; + outlineSize = 0; + outlineFade = 0; + delete faceName; + } } /* @@ -109,7 +152,7 @@ private: return result; } - const char * OnGetString(char * string, void * fieldDat, bool * needClass) + const char * OnGetString(char * string, void * fieldDat, ObjectNotationType * onType) { if(this) { diff --git a/ecere/src/gfx/Surface.ec b/ecere/src/gfx/Surface.ec index fff75e287e..905c644328 100644 --- a/ecere/src/gfx/Surface.ec +++ b/ecere/src/gfx/Surface.ec @@ -136,8 +136,10 @@ private: bool writeColor; ColorAlpha blitTint; ColorAlpha outlineColor; + Color blackTint; blitTint = white; + blackTint = black; blend = true; writeColor = true; @@ -559,6 +561,12 @@ public: get { return blitTint; } } + property Color blackTint + { + set { blackTint = value; } + get { return blitTint; } + } + property uint lineStipple { set diff --git a/ecere/src/gfx/bitmaps/ETC2Format.ec b/ecere/src/gfx/bitmaps/ETC2Format.ec new file mode 100644 index 0000000000..b388dfde95 --- /dev/null +++ b/ecere/src/gfx/bitmaps/ETC2Format.ec @@ -0,0 +1,94 @@ +namespace gfx::bitmaps; + +import "Bitmap" + +static const char * extensions[] = { "etc2", null }; + +#ifdef ETC2_COMPRESS +default extern void * etc2Alloc(size_t size); +#endif + +class ETCFormat : BitmapFormat +{ + class_property(extensions) = extensions; + + bool Load(Bitmap bitmap, File f) + { + bool result = false; + int count = 0; + int i; + + if(!f.Read(&count, sizeof(count), 1) || count > 16) + { +#ifdef _DEBUG + PrintLn("WARNING: Invalid ETC2 file"); +#endif + return false; + } + bitmap.pixelFormat = pixelFormatETC2RGBA8; + bitmap.numMipMaps = count > 1 ? count : 0; + if(bitmap.numMipMaps) + { + bitmap.bitmaps = new0 Bitmap[bitmap.numMipMaps]; + bitmap.mipMaps = true; + } + for(i = 0; i < count; i++) + { + Bitmap mipMap = bitmap; + if(count > 1) + bitmap.bitmaps[i] = mipMap = { pixelFormat = pixelFormatETC2RGBA8 }; + f.Read(&mipMap.width, sizeof(mipMap.width), 1); + f.Read(&mipMap.height, sizeof(mipMap.height), 1); + f.Read(&mipMap.sizeBytes, sizeof(mipMap.sizeBytes), 1); + +#ifdef _DEBUG + if(!mipMap.sizeBytes) + PrintLn("WARNING: Invalid ETC2 file"); +#endif + +#ifdef ETC2_COMPRESS + mipMap.picture = etc2Alloc(mipMap.sizeBytes); +#else + mipMap.picture = new byte[mipMap.sizeBytes]; +#endif + f.Read(mipMap.picture, 1, mipMap.sizeBytes); + } + result = true; + return result; + } + + bool Save(Bitmap bitmap, const char * fileName, void * options) + { + bool result = false; + Bitmap bmp = bitmap; + File f = FileOpen(fileName, write); + + if(bitmap.pixelFormat != pixelFormatETC2RGBA8) + bmp = bitmap.ProcessDD(true, 0, true, 16384, true); + + if(f) + { + int count = bitmap.numMipMaps; + int i; + + if(!count) count = 1; + + f.Write(&count, sizeof(count), 1); + for(i = 0; i < count; i++) + { + Bitmap mipMap = bmp.mipMaps && bmp.bitmaps ? bmp.bitmaps[i] : bmp; + + f.Write(&mipMap.width, sizeof(mipMap.width), 1); + f.Write(&mipMap.height, sizeof(mipMap.height), 1); + f.Write(&mipMap.sizeBytes, sizeof(mipMap.sizeBytes), 1); + f.Write(mipMap.picture, 1, mipMap.sizeBytes); + } + delete f; + result = true; + } + + if(bmp != bitmap) + delete bmp; + return result; + } +} diff --git a/ecere/src/gfx/bitmaps/GIFFormat.ec b/ecere/src/gfx/bitmaps/GIFFormat.ec index 6efc721c89..767756bd9c 100644 --- a/ecere/src/gfx/bitmaps/GIFFormat.ec +++ b/ecere/src/gfx/bitmaps/GIFFormat.ec @@ -2,7 +2,7 @@ namespace gfx::bitmaps; import "Display" -#if !defined(__EMSCRIPTEN__) +#if !defined(__EMSCRIPTEN__) && !defined(__LUMIN__) #include "gif_lib.h" diff --git a/ecere/src/gfx/bitmaps/JPEGFormat.ec b/ecere/src/gfx/bitmaps/JPEGFormat.ec index 741778b902..dd58d19cb7 100644 --- a/ecere/src/gfx/bitmaps/JPEGFormat.ec +++ b/ecere/src/gfx/bitmaps/JPEGFormat.ec @@ -246,7 +246,7 @@ class JPGFormat : BitmapFormat bool Save(Bitmap bitmap, const char *filename, void * options) { bool result = false; - if(bitmap.pixelFormat == pixelFormat888) + if(bitmap.pixelFormat == pixelFormat888 && bitmap.picture) { File f = FileOpen(filename, write); if(f) @@ -269,7 +269,7 @@ class JPGFormat : BitmapFormat jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, 100, TRUE); + jpeg_set_quality(&cinfo, options ? *(int *)options : 100, TRUE); jpeg_start_compress(&cinfo, TRUE); diff --git a/ecere/src/gfx/bitmaps/PNGFormat.ec b/ecere/src/gfx/bitmaps/PNGFormat.ec index ce39a50886..9ef94cb03c 100644 --- a/ecere/src/gfx/bitmaps/PNGFormat.ec +++ b/ecere/src/gfx/bitmaps/PNGFormat.ec @@ -54,6 +54,11 @@ static void WriteData(png_structp png, png_bytep bytes, png_size_t size) static const char * extensions[] = { "png", null }; +public struct PNGOptions +{ + int zlibCompressionLevel; +}; + class PNGFormat : BitmapFormat { class_property(extensions) = extensions; @@ -85,6 +90,8 @@ class PNGFormat : BitmapFormat channels = png_get_channels(png_ptr, info_ptr); if(channels == 3 || channels == 4 || channels == 1 || channels == 2) { + PixelFormat pixelFormat = pixelFormatRGBA; + int stride = 0; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, null, null); numPasses = png_set_interlace_handling(png_ptr); @@ -102,7 +109,13 @@ class PNGFormat : BitmapFormat else if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); - if((result = bitmap.Allocate(null, (uint)width, (uint)height, 0, pixelFormatRGBA, false))) + if(channels == 1 && bit_depth == 16 && color_type == PNG_COLOR_TYPE_GRAY) + { + pixelFormat = pixelFormatA16; + stride = width; // no padding for now... + } + + if((result = bitmap.Allocate(null, (uint)width, (uint)height, stride, pixelFormat, false))) { int pass; @@ -117,20 +130,22 @@ class PNGFormat : BitmapFormat for (y = 0; y < height; y++) { uint x; - ColorRGBA * destPtr = ((ColorRGBA *)bitmap.picture) + y * bitmap.stride; png_read_rows(png_ptr, &rowPtr, null, 1); if(bit_depth == 16) { + uint16 * destPtr = ((uint16 *)bitmap.picture) + y * bitmap.stride; for(x = 0; x> 3; @@ -247,11 +262,12 @@ class PNGFormat : BitmapFormat return result; } - bool Save(Bitmap bitmap, const char *filename, void * options) + bool Save(Bitmap bitmap, const char *filename, PNGOptions options) { bool result = false; Bitmap tempBitmap = null; - if(bitmap && bitmap.pixelFormat != pixelFormatRGBA) + if(bitmap && bitmap.pixelFormat != pixelFormatRGBA && + bitmap.pixelFormat != pixelFormatA16 && bitmap.pixelFormat != pixelFormatAlpha) { tempBitmap = Bitmap { }; if(tempBitmap.Copy(bitmap) && tempBitmap.Convert(null, pixelFormatRGBA, null)) @@ -274,20 +290,52 @@ class PNGFormat : BitmapFormat if(!setjmp(png_jmpbuf(png_ptr))) { uint y; + uint bytesPerRow = bitmap.stride * + (bitmap.pixelFormat == pixelFormatAlpha ? 1 : bitmap.pixelFormat == pixelFormatA16 ? 2 : 4); + int colorType = bitmap.pixelFormat == pixelFormatA16 || bitmap.pixelFormat == pixelFormatAlpha ? + PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_RGBA; + int bitsPerPixel = bitmap.pixelFormat == pixelFormatA16 ? 16 : 8; + byte * rowPtr = null; + uint width = bitmap.width; + + // pixelFormatA16 is represented in native 16-bit uint16 + if(bitmap.pixelFormat == pixelFormatA16) + rowPtr = new byte[width * 2]; png_set_write_fn(png_ptr, f, WriteData, null); - png_set_IHDR(png_ptr, info_ptr, bitmap.width, bitmap.height, 8, PNG_COLOR_TYPE_RGBA, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + png_set_IHDR(png_ptr, info_ptr, bitmap.width, bitmap.height, bitsPerPixel, colorType, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + if(options != null) + png_set_compression_level(png_ptr, options.zlibCompressionLevel); + + png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, PNG_ALL_FILTERS); png_write_info(png_ptr, info_ptr); - for (y = 0; y < bitmap.height; y++) + for(y = 0; y < bitmap.height; y++) { - byte * rowPtr = (byte *)(((uint *)bitmap.picture) + y * bitmap.stride); - png_write_rows(png_ptr, &rowPtr, 1); + byte * ptr; + + if(rowPtr) + { + uint16 * src = ((uint16 *)bitmap.picture) + y * width; + int x; + for(x = 0; x < width; x++) + { + uint16 v = src[x]; + rowPtr[2 * x + 0] = (v & 0xFF00) >> 8; + rowPtr[2 * x + 1] = v & 0xFF; + } + ptr = rowPtr; + } + else + ptr = ((byte *)bitmap.picture) + y * bytesPerRow; + png_write_rows(png_ptr, &ptr, 1); } + delete rowPtr; + png_write_end(png_ptr, info_ptr); result = true; diff --git a/ecere/src/gfx/drivers/Direct3D8DisplayDriver.ec b/ecere/src/gfx/drivers/Direct3D8DisplayDriver.ec index 567e81eb48..06ab27cc8f 100644 --- a/ecere/src/gfx/drivers/Direct3D8DisplayDriver.ec +++ b/ecere/src/gfx/drivers/Direct3D8DisplayDriver.ec @@ -98,7 +98,6 @@ static struct D3D8Vertex static class D3D8Indices : struct { - uint16 * indices; IDirect3DIndexBuffer8 * buffer; int nIndices; }; @@ -1358,6 +1357,8 @@ class Direct3D8DisplayDriver : DisplayDriver D3D8System d3dSystem = displaySystem.driverData; bool result = false; IDirect3DDevice8 * d3dDevice = d3dSystem.d3dDevice; + //bool memAllocOnly = flags.memAllocOnly; + flags.memAllocOnly = false; if(!mesh.data) mesh.data = D3D8Mesh { }; @@ -1480,13 +1481,13 @@ class Direct3D8DisplayDriver : DisplayDriver return true; } - void FreeIndices(DisplaySystem displaySystem, D3D8Indices d3dIndices) + void FreeIndices(DisplaySystem displaySystem, PrimitiveSingle group) { + D3D8Indices d3dIndices = group.data; if(d3dIndices) { if(d3dIndices.buffer) IDirect3DIndexBuffer8_Release(d3dIndices.buffer); - delete d3dIndices.indices; delete d3dIndices; } } @@ -1498,7 +1499,6 @@ class Direct3D8DisplayDriver : DisplayDriver D3D8Indices d3dIndices { }; if(d3dIndices && nIndices) { - d3dIndices.indices = (void *)(indices32bit ? new uint32[nIndices] : new uint16[nIndices]); IDirect3DDevice8_CreateIndexBuffer(d3dDevice, (indices32bit ? sizeof(uint32) : sizeof(uint16)) * nIndices, 0, indices32bit ? D3DFMT_INDEX32 : D3DFMT_INDEX16, D3DPOOL_MANAGED, &d3dIndices.buffer); d3dIndices.nIndices = nIndices; @@ -1506,19 +1506,20 @@ class Direct3D8DisplayDriver : DisplayDriver return d3dIndices; } - void UnlockIndices(DisplaySystem displaySystem, D3D8Indices d3dIndices, bool indices32bit, int nIndices) + void UnlockIndices(DisplaySystem displaySystem, PrimitiveSingle group, bool indices32bit, int nIndices, void * meab) { + D3D8Indices d3dIndices = group.data; uint16 * indexBuffer = null; if(!IDirect3DIndexBuffer8_Lock(d3dIndices.buffer, 0, 0, (byte **)&indexBuffer, 0)) { - memcpy(indexBuffer, d3dIndices.indices, indices32bit ? sizeof(uint32) : sizeof(uint16) * d3dIndices.nIndices); + memcpy(indexBuffer, group.indices, indices32bit ? sizeof(uint32) : sizeof(uint16) * d3dIndices.nIndices); IDirect3DIndexBuffer8_Unlock(d3dIndices.buffer); } } - uint16 * LockIndices(DisplaySystem displaySystem, D3D8Indices d3dIndices) + void * LockIndices(DisplaySystem displaySystem, PrimitiveSingle group) { - return d3dIndices.indices; + return (void *)1; } void SelectMesh(Display display, Mesh mesh) @@ -1552,18 +1553,18 @@ class Direct3D8DisplayDriver : DisplayDriver } } - void DrawPrimitives(Display display, PrimitiveSingle * primitive, Mesh mesh) + void DrawPrimitives(Display display, PrimitiveSingle primitive, Mesh mesh) { DisplaySystem displaySystem = display.displaySystem; D3D8System d3dSystem = displaySystem.driverData; D3D8Display d3dDisplay = display.driverData; IDirect3DDevice8 * d3dDevice = d3dSystem.d3dDevice; - if(primitiveTypes[primitive->type.primitiveType]) + if(primitiveTypes[primitive.type.primitiveType]) { - int numPrimitives = (primitive->type.vertexRange) ? primitive->nVertices : primitive->nIndices; + int numPrimitives = (primitive.type.vertexRange) ? primitive.nVertices : primitive.nIndices; int c; - switch(primitive->type.primitiveType) + switch(primitive.type.primitiveType) { case lines: numPrimitives /= 2; break; case triangles: numPrimitives /= 3; break; @@ -1578,27 +1579,27 @@ class Direct3D8DisplayDriver : DisplayDriver numPrimitives /= 4; break; } - if(primitive->type.vertexRange) + if(primitive.type.vertexRange) { - if(primitive->type.primitiveType == quads) + if(primitive.type.primitiveType == quads) { for(c = 0; ctype.primitiveType], primitive->first+c*4, 2); + IDirect3DDevice8_DrawPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], primitive.first+c*4, 2); } else - IDirect3DDevice8_DrawPrimitive(d3dDevice, primitiveTypes[primitive->type.primitiveType], primitive->first, numPrimitives); + IDirect3DDevice8_DrawPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], primitive.first, numPrimitives); } else { - D3D8Indices indices = primitive->data; + D3D8Indices indices = primitive.data; IDirect3DDevice8_SetIndices(d3dDevice, indices.buffer, 0); - if(primitive->type.primitiveType == quads) + if(primitive.type.primitiveType == quads) { for(c = 0; ctype.primitiveType], 0, mesh.nVertices, c*4, 2); + IDirect3DDevice8_DrawIndexedPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], 0, mesh.nVertices, c*4, 2); } else - IDirect3DDevice8_DrawIndexedPrimitive(d3dDevice, primitiveTypes[primitive->type.primitiveType], 0, mesh.nVertices, 0, numPrimitives); + IDirect3DDevice8_DrawIndexedPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], 0, mesh.nVertices, 0, numPrimitives); } if(display.display3D.material.flags.doubleSided) @@ -1610,27 +1611,27 @@ class Direct3D8DisplayDriver : DisplayDriver if(d3dDisplay.lights[c].Type) IDirect3DDevice8_SetLight(d3dDevice, c, &d3dDisplay.lightsPI[c]); } - if(primitive->type.vertexRange) + if(primitive.type.vertexRange) { - if(primitive->type.primitiveType == quads) + if(primitive.type.primitiveType == quads) { for(c = 0; ctype.primitiveType], primitive->first+c*4, 2); + IDirect3DDevice8_DrawPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], primitive.first+c*4, 2); } else - IDirect3DDevice8_DrawPrimitive(d3dDevice, primitiveTypes[primitive->type.primitiveType], primitive->first, numPrimitives); + IDirect3DDevice8_DrawPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], primitive.first, numPrimitives); } else { - D3D8Indices indices = primitive->data; + D3D8Indices indices = primitive.data; IDirect3DDevice8_SetIndices(d3dDevice, indices.buffer, 0); - if(primitive->type.primitiveType == quads) + if(primitive.type.primitiveType == quads) { for(c = 0; ctype.primitiveType], 0, mesh.nVertices, c*4, 2); + IDirect3DDevice8_DrawIndexedPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], 0, mesh.nVertices, c*4, 2); } else - IDirect3DDevice8_DrawIndexedPrimitive(d3dDevice, primitiveTypes[primitive->type.primitiveType], 0, mesh.nVertices, 0, numPrimitives); + IDirect3DDevice8_DrawIndexedPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], 0, mesh.nVertices, 0, numPrimitives); } if(!display.display3D.material.flags.singleSideLight) diff --git a/ecere/src/gfx/drivers/Direct3D9DisplayDriver.ec b/ecere/src/gfx/drivers/Direct3D9DisplayDriver.ec index b21d4c94f1..4089bcb1f8 100644 --- a/ecere/src/gfx/drivers/Direct3D9DisplayDriver.ec +++ b/ecere/src/gfx/drivers/Direct3D9DisplayDriver.ec @@ -104,7 +104,6 @@ static struct Vertex static class D3DIndices : struct { - uint16 * indices; IDirect3DIndexBuffer9 * buffer; int nIndices; }; @@ -1384,6 +1383,8 @@ class Direct3D9DisplayDriver : DisplayDriver D3DSystem d3dSystem = displaySystem.driverData; bool result = false; IDirect3DDevice9 * d3dDevice = d3dSystem.d3dDevice; + //bool memAllocOnly = flags.memAllocOnly; + flags.memAllocOnly = false; if(!mesh.data) mesh.data = D3DMesh { }; @@ -1506,13 +1507,13 @@ class Direct3D9DisplayDriver : DisplayDriver return true; } - void FreeIndices(DisplaySystem displaySystem, D3DIndices d3dIndices) + void FreeIndices(DisplaySystem displaySystem, PrimitiveSingle group) { + D3DIndices d3dIndices = group.data; if(d3dIndices) { if(d3dIndices.buffer) IDirect3DIndexBuffer9_Release(d3dIndices.buffer); - delete d3dIndices.indices; delete d3dIndices; } } @@ -1524,7 +1525,6 @@ class Direct3D9DisplayDriver : DisplayDriver D3DIndices d3dIndices { }; if(d3dIndices && nIndices) { - d3dIndices.indices = (void *)(indices32bit ? new uint32[nIndices] : new uint16[nIndices]); IDirect3DDevice9_CreateIndexBuffer(d3dDevice, (indices32bit ? sizeof(uint32) : sizeof(uint16)) * nIndices, 0, indices32bit ? D3DFMT_INDEX32 : D3DFMT_INDEX16, D3DPOOL_MANAGED, &d3dIndices.buffer, null); d3dIndices.nIndices = nIndices; @@ -1532,19 +1532,20 @@ class Direct3D9DisplayDriver : DisplayDriver return d3dIndices; } - void UnlockIndices(DisplaySystem displaySystem, D3DIndices d3dIndices, bool indices32bit, int nIndices) + void UnlockIndices(DisplaySystem displaySystem, PrimitiveSingle group, bool indices32bit, int nIndices, void * meab) { + D3DIndices d3dIndices = group.data; uint16 * indexBuffer = null; if(!IDirect3DIndexBuffer9_Lock(d3dIndices.buffer, 0, 0, (void **)&indexBuffer, 0)) { - memcpy(indexBuffer, d3dIndices.indices, (indices32bit ? sizeof(uint32) : sizeof(uint16)) * d3dIndices.nIndices); + memcpy(indexBuffer, group.indices, (indices32bit ? sizeof(uint32) : sizeof(uint16)) * d3dIndices.nIndices); IDirect3DIndexBuffer9_Unlock(d3dIndices.buffer); } } - uint16 * LockIndices(DisplaySystem displaySystem, D3DIndices d3dIndices) + void * LockIndices(DisplaySystem displaySystem, PrimitiveSingle group) { - return d3dIndices.indices; + return (void *)1; } void SelectMesh(Display display, Mesh mesh) @@ -1578,19 +1579,19 @@ class Direct3D9DisplayDriver : DisplayDriver } } - void DrawPrimitives(Display display, PrimitiveSingle * primitive, Mesh mesh) + void DrawPrimitives(Display display, PrimitiveSingle primitive, Mesh mesh) { DisplaySystem displaySystem = display.displaySystem; D3DSystem d3dSystem = displaySystem.driverData; D3DDisplay d3dDisplay = display.driverData; IDirect3DDevice9 * d3dDevice = d3dSystem.d3dDevice; - if(primitiveTypes[primitive->type.primitiveType]) + if(primitiveTypes[primitive.type.primitiveType]) { - int numPrimitives = (primitive->type.vertexRange) ? primitive->nVertices : primitive->nIndices; + int numPrimitives = (primitive.type.vertexRange) ? primitive.nVertices : primitive.nIndices; int c; - switch(primitive->type.primitiveType) + switch(primitive.type.primitiveType) { case lines: numPrimitives /= 2; break; case triangles: numPrimitives /= 3; break; @@ -1605,27 +1606,27 @@ class Direct3D9DisplayDriver : DisplayDriver numPrimitives /= 4; break; } - if(primitive->type.vertexRange) + if(primitive.type.vertexRange) { - if(primitive->type.primitiveType == quads) + if(primitive.type.primitiveType == quads) { for(c = 0; ctype.primitiveType], primitive->first+c*4, 2); + IDirect3DDevice9_DrawPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], primitive.first+c*4, 2); } else - IDirect3DDevice9_DrawPrimitive(d3dDevice, primitiveTypes[primitive->type.primitiveType], primitive->first, numPrimitives); + IDirect3DDevice9_DrawPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], primitive.first, numPrimitives); } else { - D3DIndices indices = primitive->data; + D3DIndices indices = primitive.data; IDirect3DDevice9_SetIndices(d3dDevice, indices.buffer); - if(primitive->type.primitiveType == quads) + if(primitive.type.primitiveType == quads) { for(c = 0; ctype.primitiveType], 0, 0, mesh.nVertices, c*4, 2); + IDirect3DDevice9_DrawIndexedPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], 0, 0, mesh.nVertices, c*4, 2); } else - IDirect3DDevice9_DrawIndexedPrimitive(d3dDevice, primitiveTypes[primitive->type.primitiveType], 0, 0, mesh.nVertices, 0, numPrimitives); + IDirect3DDevice9_DrawIndexedPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], 0, 0, mesh.nVertices, 0, numPrimitives); } if(display.display3D.material.flags.doubleSided) @@ -1638,27 +1639,27 @@ class Direct3D9DisplayDriver : DisplayDriver IDirect3DDevice9_SetLight(d3dDevice, c, &d3dDisplay.lightsPI[c]); } - if(primitive->type.vertexRange) + if(primitive.type.vertexRange) { - if(primitive->type.primitiveType == quads) + if(primitive.type.primitiveType == quads) { for(c = 0; ctype.primitiveType], primitive->first+c*4, 2); + IDirect3DDevice9_DrawPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], primitive.first+c*4, 2); } else - IDirect3DDevice9_DrawPrimitive(d3dDevice, primitiveTypes[primitive->type.primitiveType], primitive->first, numPrimitives); + IDirect3DDevice9_DrawPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], primitive.first, numPrimitives); } else { - D3DIndices indices = primitive->data; + D3DIndices indices = primitive.data; IDirect3DDevice9_SetIndices(d3dDevice, indices.buffer); - if(primitive->type.primitiveType == quads) + if(primitive.type.primitiveType == quads) { for(c = 0; ctype.primitiveType], 0, 0, mesh.nVertices, c*4, 2); + IDirect3DDevice9_DrawIndexedPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], 0, 0, mesh.nVertices, c*4, 2); } else - IDirect3DDevice9_DrawIndexedPrimitive(d3dDevice, primitiveTypes[primitive->type.primitiveType], 0, 0, mesh.nVertices, 0, numPrimitives); + IDirect3DDevice9_DrawIndexedPrimitive(d3dDevice, primitiveTypes[primitive.type.primitiveType], 0, 0, mesh.nVertices, 0, numPrimitives); } if(!display.display3D.material.flags.singleSideLight) { diff --git a/ecere/src/gfx/drivers/GDIDisplayDriver.ec b/ecere/src/gfx/drivers/GDIDisplayDriver.ec index 728cadc228..2f76040af7 100644 --- a/ecere/src/gfx/drivers/GDIDisplayDriver.ec +++ b/ecere/src/gfx/drivers/GDIDisplayDriver.ec @@ -34,6 +34,8 @@ class GDIDisplay : LFBDisplay ~GDIDisplay() { + bitmap.picture = null; + if(memDC) DeleteDC(memDC); if(memBitmap) DeleteObject(memBitmap); if(palette) DeleteObject(palette); diff --git a/ecere/src/gfx/drivers/LFBDisplayDriver.ec b/ecere/src/gfx/drivers/LFBDisplayDriver.ec index 6b22f7f583..8114e336e9 100644 --- a/ecere/src/gfx/drivers/LFBDisplayDriver.ec +++ b/ecere/src/gfx/drivers/LFBDisplayDriver.ec @@ -35,10 +35,10 @@ public class Font : struct { } import "lfbBlit" import "lfbConvert" -#if !defined(ECERE_NO3D) && !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && defined(__WIN32__) +#if !defined(ECERE_NO3D) && !defined(ECERE_VANILLA) && !defined(ECERE_ONEDRIVER) && defined(__WIN32__) && !defined(ECERE_NOGL) import "OpenGLDisplayDriver" -#if !defined(_GLES) && !defined(ECERE_STATIC) +#if !defined(_GLES) && !defined(ECERE_STATIC) && !defined(_GLES2) && !defined(_GLES3) import "Direct3D8DisplayDriver" import "Direct3D9DisplayDriver" #endif @@ -1193,7 +1193,7 @@ public class LFBDisplayDriver : DisplayDriver theOffset = (byte *) (((uint32 *)lfbSurface.bitmap.picture) + y1 * lfbSurface.bitmap.stride + x1); for(y = y1; y<= y2; y++) { - #if defined(__GNUC__) + #if 1 //defined(__GNUC__) FillBytesBy4((uint32 *) theOffset,color,w); #else memset_32((uint32 *) theOffset,color,w); @@ -2089,18 +2089,21 @@ public class LFBDisplayDriver : DisplayDriver #if !defined(ECERE_NOTRUETYPE) x <<= 6; - if(surface.font.outlineSize) + if(surface.font) { - ColorAlpha backForeground = surface.foreground; - int fx = x; - lfbSurface.writingOutline = true; - surface.foreground = surface.outlineColor; - lfbSurface.font.ProcessString(surface.displaySystem, (const byte *)text, len, true, surface, display, &fx, y, prevGlyph, rPrevGlyph, null); - lfbSurface.writingOutline = false; - surface.foreground = backForeground; - } + if(surface.font.outlineSize) + { + ColorAlpha backForeground = surface.foreground; + int fx = x; + lfbSurface.writingOutline = true; + surface.foreground = surface.outlineColor; + lfbSurface.font.ProcessString(surface.displaySystem, (const byte *)text, len, true, surface, display, &fx, y, prevGlyph, rPrevGlyph, null); + lfbSurface.writingOutline = false; + surface.foreground = backForeground; + } - lfbSurface.font.ProcessString(surface.displaySystem, (const byte *)text, len, true, surface, display, &x, y, prevGlyph, rPrevGlyph, null); + lfbSurface.font.ProcessString(surface.displaySystem, (const byte *)text, len, true, surface, display, &x, y, prevGlyph, rPrevGlyph, null); + } #endif lfbSurface.writingText = false; } @@ -2155,34 +2158,46 @@ public class LFBDisplayDriver : DisplayDriver bool AllocateMesh(DisplaySystem displaySystem, Mesh mesh, MeshFeatures flags, int nVertices) { bool result = false; + //bool memAllocOnly = flags.memAllocOnly; + flags.memAllocOnly = false; + if(mesh.nVertices == nVertices) { result = true; // Same number of vertices, adding features (Leaves the other features pointers alone) if(mesh.flags != flags) { - if(!mesh.flags.vertices && flags.vertices) + if(flags.interleaved && !flags.doubleVertices && flags.vertices && flags.normals && flags.texCoords1) { - if(flags.doubleVertices) - { - mesh.vertices = (Vector3Df *)new Vector3D[nVertices]; - } - else - mesh.vertices = new Vector3Df[nVertices]; + mesh.vertices = (Vector3Df *)renew mesh.vertices float[8*nVertices]; } - if(!mesh.flags.normals && flags.normals) + else { - if(flags.doubleNormals) + if(!mesh.flags.vertices && flags.vertices) { - mesh.normals = (Vector3Df *)new Vector3D[nVertices]; + if(flags.doubleVertices) + { + mesh.vertices = (Vector3Df *)new Vector3D[nVertices]; + } + else + mesh.vertices = new Vector3Df[nVertices]; } - else - mesh.normals = new Vector3Df[nVertices]; + if(!mesh.flags.normals && flags.normals) + { + if(flags.doubleNormals) + { + mesh.normals = (Vector3Df *)new Vector3D[nVertices]; + } + else + mesh.normals = new Vector3Df[nVertices]; + } + if(!mesh.flags.texCoords1 && flags.texCoords1) + mesh.texCoords = new Pointf[nVertices]; + if(!mesh.flags.colors && flags.colors) + mesh.colors = new ColorRGBAf[nVertices]; + if(!mesh.flags.tangents && flags.tangents) + mesh.tangents = new Vector3Df[2*nVertices]; } - if(!mesh.flags.texCoords1 && flags.texCoords1) - mesh.texCoords = new Pointf[nVertices]; - if(!mesh.flags.colors && flags.colors) - mesh.colors = new ColorRGBAf[nVertices]; } } else @@ -2190,28 +2205,37 @@ public class LFBDisplayDriver : DisplayDriver result = true; // New number of vertices, reallocate all current and new features flags |= mesh.flags; - if(flags.vertices) + if(flags.interleaved && !flags.doubleVertices && flags.vertices && flags.normals && flags.texCoords1) { - if(flags.doubleVertices) - { - mesh.vertices = (Vector3Df *)renew mesh.vertices Vector3D[nVertices]; - } - else - mesh.vertices = renew mesh.vertices Vector3Df[nVertices]; + mesh.vertices = (Vector3Df *)renew mesh.vertices float[8*nVertices]; } - if(flags.normals) + else { - if(flags.doubleNormals) + if(flags.vertices) + { + if(flags.doubleVertices) + { + mesh.vertices = (Vector3Df *)renew mesh.vertices Vector3D[nVertices]; + } + else + mesh.vertices = renew mesh.vertices Vector3Df[nVertices]; + } + if(flags.normals) { - mesh.normals = (Vector3Df *)renew mesh.normals Vector3D[nVertices]; + if(flags.doubleNormals) + { + mesh.normals = (Vector3Df *)renew mesh.normals Vector3D[nVertices]; + } + else + mesh.normals = renew mesh.normals Vector3Df[nVertices]; } - else - mesh.normals = renew mesh.normals Vector3Df[nVertices]; + if(flags.texCoords1) + mesh.texCoords = renew mesh.texCoords Pointf[nVertices]; + if(flags.colors) + mesh.colors = renew mesh.colors ColorRGBAf[nVertices]; + if(flags.tangents) + mesh.tangents = renew mesh.tangents Vector3Df[2 * nVertices]; } - if(flags.texCoords1) - mesh.texCoords = renew mesh.texCoords Pointf[nVertices]; - if(flags.colors) - mesh.colors = renew mesh.colors ColorRGBAf[nVertices]; } return result; } @@ -2221,18 +2245,18 @@ public class LFBDisplayDriver : DisplayDriver return true; } - void FreeIndices(DisplaySystem displaySystem, uint16 * indices) + void FreeIndices(DisplaySystem displaySystem, PrimitiveSingle group) { - delete indices; + } - uint16 * AllocateIndices(DisplaySystem displaySystem, int nIndices, bool indices32bit) + void * AllocateIndices(DisplaySystem displaySystem, int nIndices, bool indices32bit) { - return (void *)(indices32bit ? new uint32[nIndices] : new uint16[nIndices]); + return (void *)1; } - uint16 * LockIndices(DisplaySystem displaySystem, void * indices) + void * LockIndices(DisplaySystem displaySystem, void * indices) { - return indices; + return (void *)1; } #endif } diff --git a/ecere/src/gfx/drivers/OpenGLDisplayDriver.ec b/ecere/src/gfx/drivers/OpenGLDisplayDriver.ec index 6c0e8d7737..70514e29b8 100644 --- a/ecere/src/gfx/drivers/OpenGLDisplayDriver.ec +++ b/ecere/src/gfx/drivers/OpenGLDisplayDriver.ec @@ -1,7 +1,15 @@ +#if defined(__linux__) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__i386__) +asm(".symver log,log@GLIBC_2.2.5"); +#endif + #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__) +#ifdef _DEBUG +#define GLSTATS +#endif + // #define DIAGNOSTICS -#if defined(_DEBUG) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__) +#if defined(_DEBUG) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__) && !defined(__UWP__) #define GL_DEBUGGING #endif @@ -16,18 +24,34 @@ namespace gfx::drivers; #include "gl123es.h" +#define GL_CLAMP_TO_EDGE 0x812F + +#if defined _GLES1 +#define glClampFunction(version) (GL_CLAMP) +#elif defined(_GLES2) || defined(__UWP__) +#define glClampFunction(version) (GL_CLAMP_TO_EDGE) +#else +#define glClampFunction(version) (version >= 2 ? GL_CLAMP_TO_EDGE : GL_CLAMP) +#endif + // ********** GL PLATFORMS INCLUDES ********** // UNIX -#if defined(__unix__) || defined(__APPLE__) +#if defined(__unix__) || defined(__APPLE__) || defined(__UWP__) // EGL - #if defined(__ANDROID__) || defined(__ODROID__) + #if defined(__ANDROID__) || defined(__ODROID__) || defined(__UWP__) import "egl" #if defined(__ANDROID__) - #include #include - #define printf(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "ecere-app", __VA_ARGS__)) + #if defined(__LUMIN__) + #define printf(...) ML_LOG(Info, __VA_ARGS__) + #else + #include + #define printf(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "ecere-app", __VA_ARGS__)) + #endif + #elif defined(__UWP__) + #define printf Logf #endif // Emscripten @@ -100,12 +124,14 @@ namespace gfx::drivers; #include // WGL -#elif defined(__WIN32__) +#elif defined(__WIN32__) && !defined(__UWP__) //#define WIN32_LEAN_AND_MEAN #undef _WIN32_WINNT #define _WIN32_WINNT 0x0502 - #define String Sting_ + #define String String_ + #define Size Size_ #include + #undef Size #undef String #include "wglDefs.h" @@ -167,19 +193,22 @@ GLCapabilities glCaps; bool glCaps_nonPow2Textures, glCaps_vertexBuffer, glCaps_quads, glCaps_intAndDouble, glCaps_legacyFormats, glCaps_compatible, glCaps_vertexPointer; // Might toggle without Reload: bool glCaps_core, glCaps_shaders, glCaps_fixedFunction, glCaps_immediate, glCaps_legacy, glCaps_pointSize, glCaps_frameBuffer, glCaps_vao, glCaps_select; +bool glCaps_gpuCommands; +bool glCaps_mdei; // bool mapBuffer; private: // ********** Errors and Debugging ********** -/* -void CheckGLErrors() +#ifdef _DEBUG +void CheckGLErrors(const String s, int l) { int e, nCount = 0; while((e = glGetError()) && nCount++ < 10) - printf("GL error %d!\n", e); + printf("GL error %d! (%s:%d)\n", e, s, l); } -*/ +#endif + #ifdef GL_DEBUGGING #ifndef APIENTRY #define APIENTRY @@ -365,8 +394,6 @@ Shader activeShader; static int displayWidth, displayHeight; -#define GL_CLAMP_TO_EDGE 0x812F - static bool useSingleGLContext = false; class OGLDisplay : struct { @@ -381,7 +408,7 @@ class OGLDisplay : struct uint vao; int maxTMU; -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) HDC hdc; HGLRC glrc; @@ -394,7 +421,7 @@ class OGLDisplay : struct int imageBuffers[2]; byte * pboMemory1, * pboMemory2; */ -#elif !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__) +#elif !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__) && !defined(__UWP__) GLXContext glContext; Pixmap pixmap; @@ -416,7 +443,7 @@ class OGLSystem : struct { int maxTextureSize; bool loadingFont; -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) PIXELFORMATDESCRIPTOR pfd; int format; HDC hdc; @@ -424,7 +451,7 @@ class OGLSystem : struct HWND hwnd; #elif defined(__EMSCRIPTEN__) EMSCRIPTEN_WEBGL_CONTEXT_HANDLE glc; -#elif !defined(__ANDROID__) && !defined(__ODROID__) +#elif !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__UWP__) XVisualInfo * visualInfo; GLXContext glContext; GLXDrawable glxDrawable; @@ -458,11 +485,12 @@ class OGLMesh : struct GLAB texCoords; GLAB texCoords2; GLAB colors; + bool needAlloc; + bool interleaved; }; class OGLIndices : struct { - uint16 * indices; GLEAB buffer; uint nIndices; }; @@ -470,14 +498,15 @@ class OGLIndices : struct int current; void * previous; -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) static HGLRC winCreateContext(HDC hdc, int * contextVersion, bool * isCompatible, bool compatible) { HGLRC result = 0; if(wglCreateContextAttribsARB) { - int versions[12][2] = + int versions[13][2] = { + { 4, 6 }, { 4, 5 }, { 4, 4 }, { 4, 3 }, { 4, 2 }, { 4, 1 }, { 4, 0 }, { 3, 3 }, { 3, 2 }, { 3, 1 }, { 3, 0 }, { 2, 1 }, { 2, 0 } @@ -526,6 +555,124 @@ static HGLRC winCreateContext(HDC hdc, int * contextVersion, bool * isCompatible } #endif +#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__) +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +typedef GLXContext (*glXCreateContextAttribsARBProc)(void *, GLXFBConfig, GLXContext, Bool, const int*); + +static bool ctxErrorOccurred = false; +static int ctxErrorHandler( void *dpy, XErrorEvent *ev ) +{ + ctxErrorOccurred = true; + return 0; +} + +GLXContext GLX_CreateContext(OGLSystem oglSystem, void * display, GLXFBConfig config, XVisualInfo * visualInfo, + OGLDisplay oglDisplay, bool compatible) +{ + GLXContext ctx = 0; + const String glxExts = glXQueryExtensionsString( display, DefaultScreen( display ) ); + int (*oldHandler)(void*, XErrorEvent*) = (void *)XSetErrorHandler((void *)&ctxErrorHandler); + glXCreateContextAttribsARBProc glXCreateContextAttribsARB = + (void *)glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" ); + + ctxErrorOccurred = false; + if (!config || !glXCreateContextAttribsARB || !strstr(glxExts, "GLX_ARB_create_context" )) + { + if(config) + ctx = glXCreateNewContext( display, config, GLX_RGBA_TYPE, oglSystem ? oglSystem.glContext : 0, True ); + else + ctx = glXCreateContext(display, visualInfo, oglSystem ? oglSystem.glContext : 0, True); + if(oglDisplay) + oglDisplay.compat = true; + } + else + { + int versions[13][2] = + { + { 4, 6 }, + { 4, 5 }, { 4, 4 }, { 4, 3 }, { 4, 2 }, { 4, 1 }, { 4, 0 }, + { 3, 3 }, { 3, 2 }, { 3, 1 }, { 3, 0 }, + { 2, 1 }, { 2, 0 } + }; +#if 0 + int context_attribs[] = + { + GLX_CONTEXT_MAJOR_VERSION_ARB, 4, + GLX_CONTEXT_MINOR_VERSION_ARB, 5, + GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + None + }; + + ctx = glXCreateContextAttribsARB( display, config, oglSystem ? oglSystem.glContext : 0, True, context_attribs ); + + XSync( display, False ); + if(ctxErrorOccurred || !ctx) + { + // TODO: Use a loop like in winCreateContext() to try different versions... + context_attribs[1] = 1; + context_attribs[3] = 0; + ctxErrorOccurred = false; + ctx = glXCreateContextAttribsARB( display, config, 0, True, context_attribs ); + } +#endif // 0 + bool tryingCompat = compatible; + int v = 0; + while(!ctx) + { + for(v = 0; !ctx && v < sizeof(versions) / sizeof(versions[0]); v++) + { + int v0 = versions[v][0], v1 = versions[v][1]; + if(!tryingCompat || v0 < 3) + { + //bool coreNotion = v0 > 3 || (v0 == 3 && v1 >= 3); + /*int attribs[] = + { + WGL_CONTEXT_MAJOR_VERSION_ARB, v0, WGL_CONTEXT_MINOR_VERSION_ARB, v1, + #ifdef _DEBUG + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, + #endif + coreNotion ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, coreNotion ? (tryingCompat ? WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB : WGL_CONTEXT_CORE_PROFILE_BIT_ARB) : 0, + 0,0 + };*/ + int context_attribs[] = + { + GLX_CONTEXT_MAJOR_VERSION_ARB, v0, + GLX_CONTEXT_MINOR_VERSION_ARB, v1, + //GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + None + }; + ctx = glXCreateContextAttribsARB( display, config, oglSystem ? oglSystem.glContext : 0, True, context_attribs ); + if(ctx) + { + if(oglDisplay) + oglDisplay.compat = false; //tryingCompat; // FIXME: Some things don't work now with compat set to true? + + //if(contextVersion) *contextVersion = v0; + //if(isCompatible) *isCompatible = tryingCompat || !coreNotion; +#ifdef _DEBUG + PrintLn("got context for ", v0, ".", v1); +#endif + } + } + } + if(tryingCompat) + tryingCompat = false; + else + break; + } + + } +#ifdef _DEBUG + if(!ctx) + PrintLn("no context!"); +#endif + XSync( display, False ); + XSetErrorHandler( (void *)oldHandler ); + return ctx; +} +#endif + class OpenGLDisplayDriver : DisplayDriver { class_property(name) = "OpenGL"; @@ -535,7 +682,7 @@ class OpenGLDisplayDriver : DisplayDriver #if defined(__EMSCRIPTEN__) OGLSystem oglSystem = displaySystem.driverData; emscripten_webgl_make_context_current(oglSystem.glc); -#elif !defined(__ANDROID__) && !defined(__ODROID__) +#elif !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__UWP__) OGLSystem oglSystem = displaySystem.driverData; if(useSingleGLContext) return true; #if defined(__WIN32__) @@ -555,7 +702,7 @@ class OpenGLDisplayDriver : DisplayDriver void UnlockSystem(DisplaySystem displaySystem) { if(useSingleGLContext) return; - #if defined(__WIN32__) + #if defined(__WIN32__) && !defined(__UWP__) wglMakeCurrent(null, null); #elif defined(__unix__) || defined(__APPLE__) // printf("Making NULL current\n"); @@ -572,7 +719,7 @@ class OpenGLDisplayDriver : DisplayDriver #if !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__) OGLDisplay oglDisplay = display.driverData; if(useSingleGLContext) return true; - #if defined(__WIN32__) + #if defined(__WIN32__) && !defined(__UWP__) wglMakeCurrent(oglDisplay.hdc, oglDisplay.glrc); #elif defined(__unix__) || defined(__APPLE__) // if(previous) glXMakeCurrent(xGlobalDisplay, None, null); @@ -600,7 +747,7 @@ class OpenGLDisplayDriver : DisplayDriver if(oglDisplay) { - #if defined(__WIN32__) + #if defined(__WIN32__) && !defined(__UWP__) wglMakeCurrent( null, null ); if(oglDisplay.glrc) @@ -673,10 +820,14 @@ class OpenGLDisplayDriver : DisplayDriver glGetIntegerv(GL_MAX_TEXTURE_SIZE, &oglSystem.maxTextureSize); -#if defined(_GLES) - capabilities = { fixedFunction = true, vertexPointer = true, vertexBuffer = true, pointSize = true, legacyFormats = true, frameBuffer = extensions && strstr(extensions, "GL_OES_framebuffer_object") }; +#if defined(__LUMIN__) + capabilities = { shaders = true, vertexBuffer = true, pointSize = true, frameBuffer = true, legacyFormats = true, vao = true, mdei = true, gpuCommands = true }; +#elif defined(_GLES3) + capabilities = { shaders = true, vertexBuffer = true, pointSize = true, frameBuffer = true, legacyFormats = true, intAndDouble = true, mdei = true, gpuCommands = true }; +#elif defined(_GLES) + capabilities = { fixedFunction = true, vertexPointer = true, vertexBuffer = true, pointSize = true, legacyFormats = true, frameBuffer = extensions && strstr(extensions, "GL_OES_framebuffer_object"), mdei = true, gpuCommands = true }; #elif defined(_GLES2) - capabilities = { glCaps_shaders = true, vertexBuffer = true, pointSize = true, frameBuffer = true, legacyFormats = true }; + capabilities = { shaders = true, vertexBuffer = true, pointSize = true, frameBuffer = true, legacyFormats = true, mdei = true, gpuCommands = true }; #else capabilities = { @@ -702,16 +853,22 @@ class OpenGLDisplayDriver : DisplayDriver vertexPointer = oglDisplay.compat; #endif #if ENABLE_GL_VAO - vao = glBindVertexArray != null && !oglDisplay.compat; + vao = glBindVertexArray != null && !oglDisplay.compat; // NOTE: Compat must be turned off to use VAOs! #endif #if ENABLE_GL_FBO frameBuffer = glBindFramebuffer != null; #endif vertexBuffer = glBindBuffer != null; // mapBuffer = glMapBuffer != null; + mdei = true; + gpuCommands = true; }; #endif + #if (defined(__ANDROID__) && !defined(__LUMIN__)) || defined(__UWP__) + capabilities.mdei = false; + #endif + #ifdef DIAGNOSTICS PrintLn("max texture size: ", oglSystem.maxTextureSize); #endif @@ -725,24 +882,34 @@ class OpenGLDisplayDriver : DisplayDriver bool result = false; OGLSystem oglSystem = displaySystem.driverData = OGLSystem { }; -#ifdef _GLES - oglSystem.capabilities = { fixedFunction = true, vertexBuffer = true, frameBuffer = true, pointSize = true }; +#if defined(__LUMIN__) + oglSystem.capabilities = { shaders = true, vertexBuffer = true, frameBuffer = true, pointSize = true, vao = true, mdei = true, gpuCommands = true }; +#elif defined(_GLES3) + oglSystem.capabilities = { shaders = true, vertexBuffer = true, frameBuffer = true, pointSize = true, intAndDouble = true, mdei = true, gpuCommands = true }; +#elif defined(_GLES) + oglSystem.capabilities = { fixedFunction = true, vertexBuffer = true, frameBuffer = true, pointSize = true, mdei = true, gpuCommands = true }; #elif defined(_GLES2) - oglSystem.capabilities = { shaders = true, vertexBuffer = true, frameBuffer = true, pointSize = true }; + oglSystem.capabilities = { shaders = true, vertexBuffer = true, frameBuffer = true, pointSize = true, mdei = true, gpuCommands = true }; #else - oglSystem.capabilities = { compatible = glCaps_compatible, shaders = true, fixedFunction = true, immediate = true, legacy = true, pointSize = true, quads = true, intAndDouble = true, vertexBuffer = true, frameBuffer = true, vao = true, nonPow2Textures = true }; + oglSystem.capabilities = { compatible = glCaps_compatible, shaders = true, fixedFunction = true, immediate = true, legacy = true, pointSize = true, quads = true, intAndDouble = true, vertexBuffer = true, frameBuffer = true, vao = true, nonPow2Textures = true, mdei = true, gpuCommands = true }; #endif + #if (defined(__ANDROID__) && !defined(__LUMIN__)) || defined(__UWP__) + oglSystem.capabilities.mdei = false; + #endif + #ifdef DIAGNOSTICS PrintLn("OpenGL driver's CreateDisplaySystem()"); #endif - #ifdef __WIN32__ + #if defined(__WIN32__) && !defined(__UWP__) oglSystem.hwnd = CreateWindow("static", null, 0,0,0,0,0,null,null,null,null); oglSystem.hdc = GetDC(oglSystem.hwnd); if(oglSystem.hdc) { + GLSamplesCount sc = displaySystem.glCapabilities.samples; + int samplesCount = sc == ms16 ? 16 : sc == ms8 ? 8 : sc == ms4 ? 4 : sc == ms2 ? 2 : 0; oglSystem.pfd.nSize = (short)sizeof(oglSystem.pfd); oglSystem.pfd.nVersion = 1; @@ -794,33 +961,43 @@ class OpenGLDisplayDriver : DisplayDriver WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB, WGL_COLOR_BITS_ARB,24, WGL_ALPHA_BITS_ARB,8, - WGL_DEPTH_BITS_ARB,16, + WGL_DEPTH_BITS_ARB, 24, WGL_STENCIL_BITS_ARB,0, WGL_DOUBLE_BUFFER_ARB,GL_TRUE, + //WGL_DEPTH_FLOAT_EXT,GL_TRUE, WGL_SAMPLE_BUFFERS_ARB,GL_TRUE, - WGL_SAMPLES_ARB, 8, // Check For 4x Multisampling + samplesCount > 1 ? WGL_SAMPLES_ARB : 0, + samplesCount > 1 ? samplesCount : 0, // Check For Multisampling Support 0,0 }; + #define MS_ATTRIB_IX 19 //21 + //Log("Found wglChoosePixelFormatARB\n"); valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); if(!valid || !numFormats) { - //Log("Can't find 8x multi sampling\n"); - iAttributes[19] = 4; - valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); + //Log("Can't find 16x multi sampling\n"); + iAttributes[MS_ATTRIB_IX] = 8; + valid = samplesCount >= 8 && wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); if(!valid || !numFormats) { - //Log("Can't find 4x multi sampling\n"); - iAttributes[19] = 2; - valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); + //Log("Can't find 8x multi sampling\n"); + iAttributes[MS_ATTRIB_IX] = 4; + valid = samplesCount >= 4 && wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); if(!valid || !numFormats) { - // Log("Can't find 2x multi sampling\n"); - iAttributes[16] = 0; - iAttributes[17] = 0; - valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); + //Log("Can't find 4x multi sampling\n"); + iAttributes[MS_ATTRIB_IX] = 2; + valid = samplesCount >= 2 && wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); + if(!valid || !numFormats) + { + // Log("Can't find 2x multi sampling\n"); + iAttributes[MS_ATTRIB_IX-3] = 0; + iAttributes[MS_ATTRIB_IX-2] = 0; + valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); + } } } } @@ -843,7 +1020,16 @@ class OpenGLDisplayDriver : DisplayDriver PrintLn("wglMakeCurrent()"); #endif if(oglSystem.glrc) + { wglMakeCurrent(oglSystem.hdc, oglSystem.glrc); + + ogl_LoadFunctions(); + oglSystem.version = ogl_GetMajorVersion(); + +#ifdef _DEBUG + PrintLn("We've got OpenGL Version: ", (char*)glGetString(GL_VERSION), "\n"); +#endif + } } } /*else @@ -885,21 +1071,28 @@ class OpenGLDisplayDriver : DisplayDriver matrixStack[1][0].Identity(); matrixStack[2][0].Identity(); - GLMatrixMode(GL_MODELVIEW); + GLMatrixMode(MatrixMode::modelView); GLScaled(1.0, 1.0, -1.0); - GLMatrixMode(GL_PROJECTION); + GLMatrixMode(MatrixMode::projection); +#if ENABLE_GL_FFP glShadeModel(GL_FLAT); +#endif #if !defined(_GLES) if(!glCaps_shaders) ;//GLLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); #endif + +#if ENABLE_GL_FFP glFogi(GL_FOG_MODE, GL_EXP); glFogf(GL_FOG_DENSITY, 0); glEnable(GL_NORMALIZE); +#endif glDepthFunc(GL_LESS); glClearDepth(1.0); +#if !defined(_GLES) && !defined(_GLES2) glDisable(GL_MULTISAMPLE); +#endif glViewport(0,0,eglWidth,eglHeight); GLLoadIdentity(); @@ -908,6 +1101,10 @@ class OpenGLDisplayDriver : DisplayDriver glabCurArrayBuffer = 0; glabCurElementBuffer = 0; + + PrintLn("We've got OpenGL Version: ", (char*)glGetString(GL_VERSION), "\n"); + PrintLn("We've got OpenGL Renderer: ", (char*)glGetString(GL_RENDERER), "\n"); + result = true; #elif defined(__EMSCRIPTEN__) { @@ -954,6 +1151,9 @@ class OpenGLDisplayDriver : DisplayDriver GLX_DOUBLEBUFFER, None }; +#ifdef DIAGNOSTICS + PrintLn("glXChooseVisual()"); +#endif oglSystem.visualInfo = glXChooseVisual( xGlobalDisplay, DefaultScreen( xGlobalDisplay ), attrList ); attr.background_pixel = 0; attr.border_pixel = 0; @@ -966,17 +1166,37 @@ class OpenGLDisplayDriver : DisplayDriver } if(oglSystem.visualInfo) { - oglSystem.glContext = glXCreateContext(xGlobalDisplay, oglSystem.visualInfo, null, True); +#ifdef DIAGNOSTICS + PrintLn("got visual info!"); + PrintLn("GLX_CreateContext()"); +#endif + oglSystem.glContext = GLX_CreateContext(null, xGlobalDisplay, null, oglSystem.visualInfo, null, + (*&oglSystem.capabilities).compatible); if(oglSystem.glContext) { +#ifdef DIAGNOSTICS + PrintLn("got context!"); +#endif glXMakeCurrent(xGlobalDisplay, oglSystem.glxDrawable, oglSystem.glContext); glXMakeCurrent(xGlobalDisplay, None, null); +#if 0 + // oglSystem.version = ogl_GetMajorVersion(); +#ifdef _DEBUG + PrintLn("We've got OpenGL Version", (char*)glGetString(GL_VERSION), "\n"); +#endif +#endif // 0 result = true; } } #endif #endif + #if defined(__UWP__) + result = true; + oglSystem.maxTextureSize = 16384; + oglSystem.compat = false; + #endif + displaySystem.flags.alpha = true; displaySystem.flags.flipping = true; displaySystem.pixelFormat = pixelFormat888; @@ -1000,7 +1220,7 @@ class OpenGLDisplayDriver : DisplayDriver delete oglSystem.shortBDBuffer; glimtkTerminate(); - #if defined(__WIN32__) + #if defined(__WIN32__) && !defined(__UWP__) wglMakeCurrent( null, null ); if(oglSystem.glrc) @@ -1043,7 +1263,7 @@ class OpenGLDisplayDriver : DisplayDriver OGLSystem oglSystem = display.displaySystem.driverData; bool result = true; -#if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__) +#if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__) && !defined(__UWP__) if(loadExtensions && ogl_LoadFunctions() == ogl_LOAD_FAILED) PrintLn("ogl_LoadFunctions() failed!"); CheckCapabilities(oglSystem, oglDisplay, canCheckExtensions); @@ -1126,16 +1346,26 @@ class OpenGLDisplayDriver : DisplayDriver #if ENABLE_GL_LEGACY else { - glDisableVertexAttribArray(GLBufferContents::color); - glDisableVertexAttribArray(GLBufferContents::normal); - glDisableVertexAttribArray(GLBufferContents::texCoord); - glDisableVertexAttribArray(GLBufferContents::vertex); - glDisableVertexAttribArray(GLBufferContents::tangent1); - glDisableVertexAttribArray(GLBufferContents::tangent2); + if(glDisableVertexAttribArray) + { + glDisableVertexAttribArray(GLBufferContents::color); + glDisableVertexAttribArray(GLBufferContents::normal); + glDisableVertexAttribArray(GLBufferContents::texCoord); + glDisableVertexAttribArray(GLBufferContents::vertex); + glDisableVertexAttribArray(GLBufferContents::tangent1); + glDisableVertexAttribArray(GLBufferContents::tangent2); + } + GLDisableClientState(COLORS); + GLDisableClientState(NORMALS); + GLDisableClientState(TEXCOORDS); + GLDisableClientState(TANGENTS1); + GLDisableClientState(TANGENTS2); #if ENABLE_GL_VAO - glBindVertexArray(0); + if(glBindVertexArray) + glBindVertexArray(0); #endif - glUseProgram(0); + if(glUseProgram) + glUseProgram(0); } #endif @@ -1191,8 +1421,11 @@ class OpenGLDisplayDriver : DisplayDriver } #endif glDepthFunc(GL_LESS); +#if !defined(__UWP__) glClearDepth(1.0); -#if !defined(__EMSCRIPTEN__) +#endif + +#if !defined(_GLES) && !defined(_GLES2) && !defined(__UWP__) glDisable(GL_MULTISAMPLE); #endif @@ -1212,26 +1445,41 @@ class OpenGLDisplayDriver : DisplayDriver oglDisplay = display.driverData = OGLDisplay { }; oglDisplay.capabilities = oglSystem.capabilities; -#if defined(__WIN32__) || defined(USEPBUFFER) +#if (defined(__WIN32__) && !defined(__UWP__)) || defined(USEPBUFFER) if(!display.alphaBlend) #endif { -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) oglDisplay.hdc = GetDC(display.window); SetPixelFormat(oglDisplay.hdc, oglSystem.format, &oglSystem.pfd); if((oglDisplay.glrc = winCreateContext(oglDisplay.hdc, &oglDisplay.version, &oglDisplay.compat, (*&display.glCapabilities).compatible))) { wglShareLists(oglSystem.glrc, oglDisplay.glrc); wglMakeCurrent(oglDisplay.hdc, oglDisplay.glrc); + + ogl_LoadFunctions(); + oglDisplay.version = ogl_GetMajorVersion(); + result = true; } else ReleaseDC(display.window, oglDisplay.hdc); +#elif defined(__UWP__) + oglDisplay.version = 3; + result = true; #elif defined(__unix__) || defined(__APPLE__) # if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__) + #if defined(__LUMIN__) + oglDisplay.version = 4; + #elif defined(__ODROID__) + oglDisplay.version = 1; + #else + oglDisplay.version = 2; + #endif result = true; # else XVisualInfo * visualInfo = ((XWindowData)display.windowDriverData).visual; + GLXFBConfig fbConfig = ((XWindowData)display.windowDriverData).config; /* #if defined(__APPLE__) XVisualInfo template = { 0 }; @@ -1258,7 +1506,9 @@ class OpenGLDisplayDriver : DisplayDriver { //printf("visualInfo is not null\n"); // printf("Creating Display Context, sharing with %x!\n", oglSystem.glContext); - oglDisplay.glContext = glXCreateContext(xGlobalDisplay, visualInfo, oglSystem.glContext, True); + GLCapabilities caps = *&display.glCapabilities; + oglDisplay.glContext = GLX_CreateContext(oglSystem, xGlobalDisplay, fbConfig, visualInfo, oglDisplay, + caps.compatible); //XFree(visualInfo); } @@ -1272,7 +1522,7 @@ class OpenGLDisplayDriver : DisplayDriver # endif #endif } -#if defined(__WIN32__) || defined(USEPBUFFER) +#if (defined(__WIN32__) && !defined(__UWP__))|| defined(USEPBUFFER) else { oglDisplay.compat = (*&display.glCapabilities).compatible; @@ -1286,7 +1536,7 @@ class OpenGLDisplayDriver : DisplayDriver emscripten_webgl_make_context_current(oglSystem.glc); #endif -#if defined(__WIN32__) || defined(USEPBUFFER) +#if (defined(__WIN32__) && !defined(__UWP__)) || defined(USEPBUFFER) initialDisplaySetup(display, !display.alphaBlend, true); #else initialDisplaySetup(display, true, true); @@ -1295,7 +1545,7 @@ class OpenGLDisplayDriver : DisplayDriver if(!useSingleGLContext) { - #if defined(__WIN32__) + #if defined(__WIN32__) && !defined(__UWP__) wglMakeCurrent(null, null); #elif defined(__unix__) || defined(__APPLE__) #if defined(__ANDROID__) || defined(__ODROID__) || defined(__EMSCRIPTEN__) @@ -1319,7 +1569,7 @@ class OpenGLDisplayDriver : DisplayDriver OGLDisplay oglDisplay = display.driverData; bool result = false; -#if defined(__WIN32__) || defined(USEPBUFFER) +#if (defined(__WIN32__) && !defined(__UWP__)) || defined(USEPBUFFER) OGLSystem oglSystem = display.displaySystem.driverData; if(display.alphaBlend) { @@ -1335,6 +1585,8 @@ class OpenGLDisplayDriver : DisplayDriver int valid; int numFormats; float fAttributes[] = {0,0}; + GLSamplesCount sc = display.glCapabilities.samples; + int samplesCount = sc == ms16 ? 16 : sc == ms8 ? 8 : sc == ms4 ? 4 : sc == ms2 ? 2 : 0; int iAttributes[] = { //WGL_DRAW_TO_BITMAP_ARB, GL_TRUE, @@ -1347,7 +1599,8 @@ class OpenGLDisplayDriver : DisplayDriver WGL_STENCIL_BITS_ARB,0, WGL_DOUBLE_BUFFER_ARB,GL_FALSE, WGL_SAMPLE_BUFFERS_ARB,GL_TRUE, - WGL_SAMPLES_ARB, 8, // Check For 4x Multisampling + samplesCount > 1 ? WGL_SAMPLES_ARB : 0, + samplesCount > 1 ? samplesCount : 0, // Check For Multisampling Support 0,0 }; @@ -1356,33 +1609,39 @@ class OpenGLDisplayDriver : DisplayDriver valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); if(!valid || !numFormats) { - //Log("Can't find 4x multi sampling\n"); - iAttributes[19] = 4; - valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); + //Log("Can't find 16x multi sampling\n"); + iAttributes[19] = 8; + valid = samplesCount >= 8 && wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); if(!valid || !numFormats) { - //Log("Can't find 4x multi sampling\n"); - iAttributes[19] = 2; - valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); + //Log("Can't find 8x multi sampling\n"); + iAttributes[19] = 4; + valid = samplesCount >= 4 && wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); if(!valid || !numFormats) { - // Log("Can't find 2x multi sampling\n"); - iAttributes[16] = 0; - iAttributes[17] = 0; - valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); + //Log("Can't find 4x multi sampling\n"); + iAttributes[19] = 2; + valid = samplesCount >= 2 && wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); if(!valid || !numFormats) { - int iAttributes[] = - { - WGL_DRAW_TO_PBUFFER_ARB,GL_TRUE, - //WGL_DRAW_TO_BITMAP_ARB,GL_TRUE, - WGL_SUPPORT_OPENGL_ARB,GL_TRUE, - WGL_COLOR_BITS_ARB,24, - WGL_ALPHA_BITS_ARB,8, - WGL_DEPTH_BITS_ARB,16, - 0,0 - }; + // Log("Can't find 2x multi sampling\n"); + iAttributes[16] = 0; + iAttributes[17] = 0; valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); + if(!valid || !numFormats) + { + int iAttributes[] = + { + WGL_DRAW_TO_PBUFFER_ARB,GL_TRUE, + //WGL_DRAW_TO_BITMAP_ARB,GL_TRUE, + WGL_SUPPORT_OPENGL_ARB,GL_TRUE, + WGL_COLOR_BITS_ARB,24, + WGL_ALPHA_BITS_ARB,8, + WGL_DEPTH_BITS_ARB,16, + 0,0 + }; + valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats); + } } } } @@ -1569,7 +1828,8 @@ class OpenGLDisplayDriver : DisplayDriver oglDisplay.pBuffer = glXCreatePbuffer(xGlobalDisplay, config[0], PBattrib); if(oglDisplay.pBuffer) { - oglDisplay.glContext = glXCreateNewContext(xGlobalDisplay, config[0], GLX_RGBA_TYPE, oglSystem.glContext, True); + oglDisplay.glContext = GLX_CreateContext(oglSystem, xGlobalDisplay, config[0], visualInfo); + oglDisplay.compat = false; // TODO: Have GLX_CreateContext set that up if(oglDisplay.glContext) { glXMakeCurrent(xGlobalDisplay, None, null); @@ -1750,7 +2010,7 @@ class OpenGLDisplayDriver : DisplayDriver glFinish();*/ #endif -#if defined(__WIN32__) || defined(USEPBUFFER) +#if (defined(__WIN32__) && !defined(__UWP__)) || defined(USEPBUFFER) if(display.alphaBlend) { glPixelStorei(GL_PACK_ALIGNMENT, 4); @@ -1760,7 +2020,7 @@ class OpenGLDisplayDriver : DisplayDriver glReadPixels(0,0,display.width,display.height,GL_BGRA_EXT,GL_UNSIGNED_BYTE, oglDisplay.picture); { -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) HDC hdc = GetDC(0); POINT point = { oglDisplay.x, oglDisplay.y}; POINT srcPoint = { 0, 0 }; @@ -1835,7 +2095,7 @@ class OpenGLDisplayDriver : DisplayDriver else #endif { -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) //wglSwapLayerBuffers(oglDisplay.hdc,WGL_SWAP_MAIN_PLANE); SwapBuffers(oglDisplay.hdc); //ecere::sys::Sleep(0.1); @@ -1848,6 +2108,10 @@ class OpenGLDisplayDriver : DisplayDriver #endif #endif } +#ifdef _DEBUG + CheckGLErrors(__FILE__, __LINE__); +#endif + // PrintLn("End of frame."); } void FreeBitmap(DisplaySystem displaySystem, Bitmap bitmap) @@ -1855,6 +2119,9 @@ class OpenGLDisplayDriver : DisplayDriver if(bitmap.driverData) { GLuint tex = (GLuint)(uintptr)bitmap.driverData; +#ifdef GLSTATS + GLStats::freeTextures(1, &tex); +#endif glDeleteTextures(1, &tex); bitmap.driverData = 0; } @@ -1899,6 +2166,10 @@ class OpenGLDisplayDriver : DisplayDriver // glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, mipMap.picture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, mipMap.picture); +#ifdef GLSTATS + GLStats::allocTexture(glBitmap, w, h, false); +#endif + delete mipMap; bitmap.driverData = (void *)(uintptr)glBitmap; @@ -1916,80 +2187,26 @@ class OpenGLDisplayDriver : DisplayDriver OGLSystem oglSystem = displaySystem.driverData; GLCapabilities capabilities = oglSystem.capabilities; Bitmap convBitmap = bitmap; - bool oldStyleCubeMap = (cubeMapFace >> 3) != 0; - int face = (cubeMapFace & 7) - 1; - if(bitmap.keepData) - { - convBitmap = { }; - convBitmap.Copy(bitmap); - } - // Pre process the bitmap... First make it 32 bit - if(/*bitmap.pixelFormat == pixelFormatRGBA || */convBitmap.Convert(null, pixelFormat888, null)) + if(convBitmap.pixelFormat != pixelFormatRGBAGL && convBitmap.pixelFormat != pixelFormatETC2RGBA8) + convBitmap = bitmap.ProcessDD(mipMaps, cubeMapFace, false, oglSystem.maxTextureSize, !capabilities.nonPow2Textures); + if(convBitmap) { - int c, level; - uint w = bitmap.width, h = bitmap.height; - GLuint glBitmap = cubeMapFace && face > 0 ? (GLuint)(uintptr)bitmap.driverData : 0; + // TODO: Proper _GLES3 setup... +#if !defined(__EMSCRIPTEN__) && ((!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3)) + bool sRGB2Linear = bitmap.sRGB2Linear; + int internalFormat = convBitmap.pixelFormat == pixelFormatETC2RGBA8 ? + (sRGB2Linear ? GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : GL_COMPRESSED_RGBA8_ETC2_EAC) : + (sRGB2Linear ? GL_SRGB8_ALPHA8 : GL_RGBA8); +#else + int internalFormat = convBitmap.pixelFormat == pixelFormatETC2RGBA8 ? 0 : GL_RGBA; +#endif + int minFilter = oglSystem.loadingFont ? GL_NEAREST : mipMaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR; + int maxFilter = oglSystem.loadingFont ? GL_NEAREST : GL_LINEAR; + int face = (cubeMapFace & 7) - 1; int target = cubeMapFace ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; - if(!capabilities.nonPow2Textures) - { - w = pow2i(w); - h = pow2i(h); - } - w = Min(w, oglSystem.maxTextureSize); - h = Min(h, oglSystem.maxTextureSize); - - if(mipMaps) - { - while(w * 2 < h) w *= 2; - while(h * 2 < w) h *= 2; - } - - // Switch ARGB to RGBA - //if(bitmap.format != pixelFormatRGBA) - { - int size = convBitmap.stride * convBitmap.height; - for(c = 0; c < size; c++) - { - // ((ColorRGBA *)bitmap.picture)[c] = ((ColorAlpha *)bitmap.picture)[c]; - // TODO: - ColorAlpha color = ((ColorAlpha *)convBitmap.picture)[c]; - ((ColorRGBA *)convBitmap.picture)[c] = ColorRGBA { color.color.r, color.color.g, color.color.b, color.a }; - } - } - // convBitmap.pixelFormat = pixelFormat888; - - if(cubeMapFace && oldStyleCubeMap) - { - if(face == 0 || face == 1 || face == 4 || face == 5) - { - uint w = convBitmap.width; - uint32 * tmp = new uint [convBitmap.width]; - int x, y; - for(y = 0; y < convBitmap.height; y++) - { - uint32 * pic = (uint32 *)((byte *)convBitmap.picture + y * w * 4); - for(x = 0; x < w; x++) - tmp[x] = pic[w-1-x]; - memcpy(pic, tmp, w*4); - } - delete tmp; - } - else if(face == 2 || face == 3) - { - int y; - Bitmap tmp { }; - tmp.Allocate(null, convBitmap.width, convBitmap.height, 0, convBitmap.pixelFormat, false); - for(y = 0; y < convBitmap.height; y++) - { - memcpy(tmp.picture + convBitmap.width * 4 * y, - convBitmap.picture + (convBitmap.height-1-y) * convBitmap.width * 4, - convBitmap.width * 4); - } - memcpy(convBitmap.picture, tmp.picture, convBitmap.sizeBytes); - delete tmp; - } - } + GLuint glBitmap = cubeMapFace && face > 0 ? (GLuint)(uintptr)bitmap.driverData : 0; + int level; glGetError(); if(!glBitmap) @@ -1997,22 +2214,19 @@ class OpenGLDisplayDriver : DisplayDriver if(glBitmap == 0) { //int error = glGetError(); + if(convBitmap != bitmap) + delete convBitmap; return false; } glBindTexture(target, glBitmap); glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, mipMaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, maxFilter); - //glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - //glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP); - //glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP); - - glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_S, glClampFunction(oglSystem.version)); + glTexParameteri(target, GL_TEXTURE_WRAP_T, glClampFunction(oglSystem.version)); #ifndef GL_TEXTURE_WRAP_R #define GL_TEXTURE_WRAP_R 0x8072 @@ -2020,7 +2234,7 @@ class OpenGLDisplayDriver : DisplayDriver #if !defined(__EMSCRIPTEN__) if(cubeMapFace) - glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_R, glClampFunction(oglSystem.version)); #endif #ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT @@ -2028,43 +2242,33 @@ class OpenGLDisplayDriver : DisplayDriver #endif #if ENABLE_GL_FFP - if(!capabilities.shaders) - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + if(!capabilities.shaders) + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); #endif result = true; - for(level = 0; result && (w >= 1 || h >= 1); level++, w >>= 1, h >>= 1) +#ifdef GLSTATS + GLStats::allocTexture(glBitmap, convBitmap.width, convBitmap.height, mipMaps); +#endif + for(level = 0; level < (convBitmap.mipMaps ? convBitmap.numMipMaps : 1); level++) { - Bitmap mipMap; - if(!w) w = 1; - if(!h) h = 1; - if(bitmap.width != w || bitmap.height != h) + Bitmap mipMap = convBitmap.mipMaps ? convBitmap.bitmaps[level] : convBitmap; + if(mipMap) { - mipMap = Bitmap { }; - if(mipMap.Allocate(null, w, h, w, convBitmap.pixelFormat, false)) - { - Surface mipSurface = mipMap.GetSurface(0,0,null); - mipSurface.blend = false; - mipSurface.Filter(convBitmap, 0,0,0,0, w, h, convBitmap.width, convBitmap.height); - delete mipSurface; - } + int target = cubeMapFace ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + face : GL_TEXTURE_2D; + uint width = mipMap.width, height = mipMap.height; + int format = GL_RGBA; + int type = GL_UNSIGNED_BYTE; + int error; + + if(convBitmap.pixelFormat == pixelFormatETC2RGBA8) + glCompressedTexImage2D(target, level, internalFormat, width, height, 0, mipMap.sizeBytes, mipMap.picture); else - { - result = false; - delete mipMap; - } - } - else - mipMap = convBitmap; + glTexImage2D(target, level, internalFormat, width, height, 0, format, type, mipMap.picture); - if(result) - { - int error; //int width = 0; glGetError(); - // glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, mipMap.picture); - glTexImage2D(cubeMapFace ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + face : GL_TEXTURE_2D, level, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, mipMap.picture); //printf("Calling glTexImage2D\n"); //glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_WIDTH, &width); //printf("width = %d (Should be %d, %d)\n", width, w, h); @@ -2075,24 +2279,16 @@ class OpenGLDisplayDriver : DisplayDriver result = false; } } - if(mipMap != convBitmap) - delete mipMap; - if(!mipMaps) break; } - convBitmap.driver.FreeBitmap(convBitmap.displaySystem, convBitmap); - bitmap.driverData = (void *)(uintptr)glBitmap; - bitmap.driver = displaySystem.driver; - if(bitmap.keepData) + if(convBitmap != bitmap) delete convBitmap; - - if(!result) - FreeBitmap(displaySystem, bitmap); - else if(oglSystem.loadingFont) + else if(!bitmap.keepData) + bitmap.Free(); + if(result) { - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - oglSystem.loadingFont = false; + bitmap.driverData = (void *)(uintptr)glBitmap; + bitmap.driver = displaySystem.driver; } } return result; @@ -2397,6 +2593,8 @@ class OpenGLDisplayDriver : DisplayDriver // glTranslatef(-0.375f, -0.375f, 0.0f); GLSetupTexturing(true); GLColor4fv(oglSurface.bitmapMult); + if(glCaps_shaders && surface.blackTint) + defaultShader.blackTint = *&surface.blackTint; glBindTexture(GL_TEXTURE_2D, tex); GLBegin(GLIMTKMode::quads); } @@ -2411,14 +2609,28 @@ class OpenGLDisplayDriver : DisplayDriver if(h < 0) { - GLTexCoord2f((float)sx/ bitmap.width, (float)(sy-h)/ bitmap.height); - GLVertex2i(dx+surface.offset.x, dy+surface.offset.y); - GLTexCoord2f((float)(sx+w) / bitmap.width, (float)(sy-h)/ bitmap.height); - GLVertex2i(dx+w+surface.offset.x, dy+surface.offset.y); - GLTexCoord2f((float)(sx+w)/ bitmap.width, (float)sy/ bitmap.height); - GLVertex2i(dx+w+surface.offset.x, dy-h+surface.offset.y); - GLTexCoord2f((float)sx / bitmap.width, (float)sy/ bitmap.height); - GLVertex2i(dx+surface.offset.x, dy-h+surface.offset.y); + if(w < 0) + { + GLTexCoord2f((float)(sx-w) / bitmap.width, (float)(sy-h)/ bitmap.height); + GLVertex2i(dx+surface.offset.x, dy+surface.offset.y); + GLTexCoord2f((float)sx/ bitmap.width, (float)(sy-h)/ bitmap.height); + GLVertex2i(dx-w+surface.offset.x, dy+surface.offset.y); + GLTexCoord2f((float)sx / bitmap.width, (float)sy/ bitmap.height); + GLVertex2i(dx-w+surface.offset.x, dy-h+surface.offset.y); + GLTexCoord2f((float)(sx-w)/ bitmap.width, (float)sy/ bitmap.height); + GLVertex2i(dx+surface.offset.x, dy-h+surface.offset.y); + } + else + { + GLTexCoord2f((float)sx/ bitmap.width, (float)(sy-h)/ bitmap.height); + GLVertex2i(dx+surface.offset.x, dy+surface.offset.y); + GLTexCoord2f((float)(sx+w) / bitmap.width, (float)(sy-h)/ bitmap.height); + GLVertex2i(dx+w+surface.offset.x, dy+surface.offset.y); + GLTexCoord2f((float)(sx+w)/ bitmap.width, (float)sy/ bitmap.height); + GLVertex2i(dx+w+surface.offset.x, dy-h+surface.offset.y); + GLTexCoord2f((float)sx / bitmap.width, (float)sy/ bitmap.height); + GLVertex2i(dx+surface.offset.x, dy-h+surface.offset.y); + } } else { @@ -2433,14 +2645,29 @@ class OpenGLDisplayDriver : DisplayDriver GLVertex2i(dx+surface.offset.x, dy+h+surface.offset.y); */ - GLTexCoord2f((float)sx / bitmap.width, (float)sy/ bitmap.height); - GLVertex2f((float)dx+surface.offset.x, (float)dy+surface.offset.y); - GLTexCoord2f((float)(sx+w)/ bitmap.width, (float)sy/ bitmap.height); - GLVertex2f((float)dx+w+surface.offset.x, (float)dy+surface.offset.y); - GLTexCoord2f((float)(sx+w) / bitmap.width, (float)(sy+h)/ bitmap.height); - GLVertex2f((float)dx+w+surface.offset.x, (float)dy+h+surface.offset.y); - GLTexCoord2f((float)sx/ bitmap.width, (float)(sy+h)/ bitmap.height); - GLVertex2f((float)dx+surface.offset.x, (float)dy+h+surface.offset.y); + if(w < 0) + { + GLTexCoord2f((float)(sx-w)/ bitmap.width, (float)sy/ bitmap.height); + GLVertex2f((float)dx+surface.offset.x, (float)dy+surface.offset.y); + GLTexCoord2f((float)sx / bitmap.width, (float)sy/ bitmap.height); + GLVertex2f((float)dx-w+surface.offset.x, (float)dy+surface.offset.y); + + GLTexCoord2f((float)sx/ bitmap.width, (float)(sy+h)/ bitmap.height); + GLVertex2f((float)dx-w+surface.offset.x, (float)dy+h+surface.offset.y); + GLTexCoord2f((float)(sx-w) / bitmap.width, (float)(sy+h)/ bitmap.height); + GLVertex2f((float)dx+surface.offset.x, (float)dy+h+surface.offset.y); + } + else + { + GLTexCoord2f((float)sx / bitmap.width, (float)sy/ bitmap.height); + GLVertex2f((float)dx+surface.offset.x, (float)dy+surface.offset.y); + GLTexCoord2f((float)(sx+w)/ bitmap.width, (float)sy/ bitmap.height); + GLVertex2f((float)dx+w+surface.offset.x, (float)dy+surface.offset.y); + GLTexCoord2f((float)(sx+w) / bitmap.width, (float)(sy+h)/ bitmap.height); + GLVertex2f((float)dx+w+surface.offset.x, (float)dy+h+surface.offset.y); + GLTexCoord2f((float)sx/ bitmap.width, (float)(sy+h)/ bitmap.height); + GLVertex2f((float)dx+surface.offset.x, (float)dy+h+surface.offset.y); + } } if(!oglSurface.writingText) { @@ -2453,6 +2680,17 @@ class OpenGLDisplayDriver : DisplayDriver void Stretch(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh) { OGLSurface oglSurface = surface.driverData; + bool flipX = w < 0, flipY = h < 0; + float invW = 1.0f / bitmap.width, invH = 1.0f / bitmap.height; + float sx0 = (sx + (flipX ? sw : 0)) * invW; + float sx1 = (sx + (flipX ? 0 : sw)) * invW; + float sy0 = (sy + (flipY ? sh : 0)) * invH; + float sy1 = (sy + (flipY ? 0 : sh)) * invH; + + if(flipX) w = -w; + if(flipY) h = -h; + dx += surface.offset.x; + dy += surface.offset.y; //glTranslate(-0.375, -0.375, 0.0); @@ -2460,37 +2698,22 @@ class OpenGLDisplayDriver : DisplayDriver glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)bitmap.driverData); GLColor4fv(oglSurface.bitmapMult); + if(glCaps_shaders) + defaultShader.blackTint = *&surface.blackTint; GLBegin(GLIMTKMode::quads); - if(h < 0) - { - GLTexCoord2f((float)(sx)/ bitmap.width, (float)(sy+sh)/ bitmap.height); - GLVertex2i(dx+surface.offset.x, dy+surface.offset.y); + GLTexCoord2f(sx0, sy0); + GLVertex2i(dx, dy); - GLTexCoord2f((float)(sx+sw) / bitmap.width, (float)(sy+sh)/ bitmap.height); - GLVertex2i(dx+w+surface.offset.x, dy+surface.offset.y); + GLTexCoord2f(sx1, sy0); + GLVertex2i(dx + w, dy); - GLTexCoord2f((float)(sx+sw)/ bitmap.width, (float)(sy)/ bitmap.height); - GLVertex2i(dx+w+surface.offset.x, dy-h+surface.offset.y); + GLTexCoord2f(sx1, sy1); + GLVertex2i(dx + w, dy + h); - GLTexCoord2f((float)(sx) / bitmap.width, (float)(sy)/ bitmap.height); - GLVertex2i(dx+surface.offset.x, dy-h+surface.offset.y); - } - else - { - GLTexCoord2f((float)(sx) / bitmap.width, (float)(sy)/ bitmap.height); - GLVertex2i(dx+surface.offset.x, dy+surface.offset.y); - - GLTexCoord2f((float)(sx+sw)/ bitmap.width, (float)(sy)/ bitmap.height); - GLVertex2i(dx+w+surface.offset.x, dy+surface.offset.y); - - GLTexCoord2f((float)(sx+sw) / bitmap.width, (float)(sy+sh)/ bitmap.height); - GLVertex2i(dx+w+surface.offset.x, dy+h+surface.offset.y); - - GLTexCoord2f((float)(sx)/ bitmap.width, (float)(sy+sh)/ bitmap.height); - GLVertex2i(dx+surface.offset.x, dy+h+surface.offset.y); - } + GLTexCoord2f(sx0, sy1); + GLVertex2i(dx, dy + h); GLEnd(); @@ -2690,6 +2913,7 @@ class OpenGLDisplayDriver : DisplayDriver OGLSystem oglSystem = displaySystem.driverData; oglSystem.loadingFont = true; font = ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags, outlineSize, outlineFade); + oglSystem.loadingFont = false; return font; } @@ -2815,14 +3039,14 @@ class OpenGLDisplayDriver : DisplayDriver int t; for(t = lastTMU; t < oglDisplay.maxTMU; t++) { - glActiveTexture(GL_TEXTURE0 + t); - glClientActiveTexture(GL_TEXTURE0 + t); + if(glActiveTexture) glActiveTexture(GL_TEXTURE0 + t); + if(glClientActiveTexture) glClientActiveTexture(GL_TEXTURE0 + t); glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_CUBE_MAP); GLDisableClientState(TEXCOORDS); } - glActiveTexture(GL_TEXTURE0); - glClientActiveTexture(GL_TEXTURE0); + if(glActiveTexture) glActiveTexture(GL_TEXTURE0); + if(glClientActiveTexture) glClientActiveTexture(GL_TEXTURE0); oglDisplay.maxTMU = lastTMU; } #endif @@ -2834,7 +3058,7 @@ class OpenGLDisplayDriver : DisplayDriver switch(state) { case antiAlias: -#ifndef __EMSCRIPTEN__ +#if !defined(_GLES) && !defined(_GLES2) && !defined(__UWP__) if(value) glEnable(GL_MULTISAMPLE); else @@ -2907,7 +3131,7 @@ class OpenGLDisplayDriver : DisplayDriver } case vSync: { -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) if(wglSwapIntervalEXT) wglSwapIntervalEXT(value ? 1 : 0); #endif @@ -3124,12 +3348,15 @@ class OpenGLDisplayDriver : DisplayDriver } else GLLoadIdentity(); + +#if !defined(__UWP__) GLFrustum( (left - origX) * camera.zMin / camera.focalX, (right - origX) * camera.zMin / camera.focalX, (bottom - origY) * camera.zMin / camera.focalY, (top - origY) * camera.zMin / camera.focalY, camera.zMin, camera.zMax); +#endif glDisable(GL_BLEND); @@ -3140,6 +3367,10 @@ class OpenGLDisplayDriver : DisplayDriver GLLoadIdentity(); +#if defined(__UWP__) + glmsScaled(1, -1, 1); +#endif + GLScaled(1.0/nearPlane, 1.0/nearPlane, -1.0/nearPlane); // *** View Matrix *** @@ -3166,8 +3397,9 @@ class OpenGLDisplayDriver : DisplayDriver glDepthMask((byte)bool::true); oglDisplay.depthWrite = true; -#ifndef __EMSCRIPTEN__ - glEnable(GL_MULTISAMPLE); +#if !defined(_GLES) && !defined(_GLES2) && !defined(__UWP__) + if(oglDisplay.version >= 2) + glEnable(GL_MULTISAMPLE); #endif } else if(surface && display.display3D.camera) @@ -3218,14 +3450,17 @@ class OpenGLDisplayDriver : DisplayDriver disableRemainingTMUs(display, 0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glDisable(GL_TEXTURE_CUBE_MAP); - #if _GLES - glDisable(GL_TEXTURE_GEN_STR); - #else - glDisable(GL_TEXTURE_GEN_R); - glDisable(GL_TEXTURE_GEN_S); - glDisable(GL_TEXTURE_GEN_T); - #endif + if(oglDisplay.version >= 2) + { + glDisable(GL_TEXTURE_CUBE_MAP); + #if _GLES + glDisable(GL_TEXTURE_GEN_STR); + #else + glDisable(GL_TEXTURE_GEN_R); + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + #endif + } } #endif @@ -3240,22 +3475,27 @@ class OpenGLDisplayDriver : DisplayDriver defaultShader.setMaterial(null, 0); } #endif - #if ENABLE_GL_FFP if(!glCaps_shaders) glShadeModel(GL_FLAT); #endif glEnable(GL_BLEND); -#if !defined(__EMSCRIPTEN__) - glDisable(GL_MULTISAMPLE); +#if !defined(_GLES) && !defined(_GLES2) && !defined(__UWP__) + if(oglDisplay.version >= 2) + glDisable(GL_MULTISAMPLE); #endif } } void ApplyMaterial(Display display, Material material, Mesh mesh) { +#if ENABLE_GL_FFP + OGLDisplay oglDisplay = display.driverData; +#endif Shader shader = material.shader ? material.shader : defaultShader; MaterialFlags flags = material.flags; + Bitmap baseMap = material.baseMap; + bool cubeMap = flags.cubeMap; #if ENABLE_GL_FFP static int lastSeparate = 0; int tmu = 0; @@ -3283,7 +3523,8 @@ class OpenGLDisplayDriver : DisplayDriver if(!glCaps_shaders) GLLightModeli(GL_LIGHT_MODEL_TWO_SIDE, bool::false); #endif - glEnable(GL_CULL_FACE); + if(!mesh.mab) // TODO: State check + glEnable(GL_CULL_FACE); } // Fog @@ -3291,7 +3532,7 @@ class OpenGLDisplayDriver : DisplayDriver #if ENABLE_GL_SHADERS if(glCaps_shaders) - activeShader.setMaterial(material, mesh.flags); + shader.setMaterial(material, *&mesh.flags); #endif #if ENABLE_GL_FFP @@ -3300,8 +3541,8 @@ class OpenGLDisplayDriver : DisplayDriver if(material.bumpMap && mesh.lightVectors) { float color[4] = { 1,1,1,1 }; - glActiveTexture(GL_TEXTURE0 + tmu); - glClientActiveTexture(GL_TEXTURE0 + tmu++); + if(glActiveTexture) glActiveTexture(GL_TEXTURE0 + tmu); + if(glClientActiveTexture) glClientActiveTexture(GL_TEXTURE0 + tmu++); glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)material.bumpMap.driverData); glDisable(GL_TEXTURE_CUBE_MAP); glEnable(GL_TEXTURE_2D); @@ -3339,19 +3580,22 @@ class OpenGLDisplayDriver : DisplayDriver GLScalef(material.uScale, material.vScale, 1); GLMatrixMode(MatrixMode::modelView); - if(flags.tile) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - else + if(flags.setupTextures) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if(flags.tile) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glClampFunction(oglDisplay.version)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glClampFunction(oglDisplay.version)); + } } - glActiveTexture(GL_TEXTURE0 + tmu); - glClientActiveTexture(GL_TEXTURE0 + tmu); + if(glActiveTexture) glActiveTexture(GL_TEXTURE0 + tmu); + if(glClientActiveTexture) glClientActiveTexture(GL_TEXTURE0 + tmu); normalMapped = true; @@ -3378,8 +3622,8 @@ class OpenGLDisplayDriver : DisplayDriver color[0] = material.diffuse.r, color[1] = material.diffuse.g, color[2] = material.diffuse.b, color[3] = 1.0; glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color ); - glActiveTexture(GL_TEXTURE0 + tmu); - glClientActiveTexture(GL_TEXTURE0 + tmu); + if(glActiveTexture) glActiveTexture(GL_TEXTURE0 + tmu); + if(glClientActiveTexture) glClientActiveTexture(GL_TEXTURE0 + tmu); } // Add ambient light @@ -3407,8 +3651,8 @@ class OpenGLDisplayDriver : DisplayDriver color[0] = ambient.r, color[1] = ambient.g, color[2] = ambient.b, color[3] = 1.0; glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color ); - glActiveTexture(GL_TEXTURE0 + tmu); - glClientActiveTexture(GL_TEXTURE0 + tmu); + if(glActiveTexture) glActiveTexture(GL_TEXTURE0 + tmu); + if(glClientActiveTexture) glClientActiveTexture(GL_TEXTURE0 + tmu); } } } @@ -3424,18 +3668,18 @@ class OpenGLDisplayDriver : DisplayDriver } #endif // Maps - if(flags.cubeMap || (material.baseMap && (mesh.texCoords || mesh.flags.texCoords1))) + if(cubeMap || (baseMap && (mesh.texCoords || mesh.flags.texCoords1))) { - Bitmap map = material.baseMap; - int diffuseTarget = flags.cubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + int diffuseTarget = cubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; #if ENABLE_GL_FFP if(!glCaps_shaders) { - glActiveTexture(GL_TEXTURE0 + tmu); - glClientActiveTexture(GL_TEXTURE0 + tmu++); + if(glActiveTexture) glActiveTexture(GL_TEXTURE0 + tmu); + if(glClientActiveTexture) glClientActiveTexture(GL_TEXTURE0 + tmu++); glEnable(diffuseTarget); - glDisable(flags.cubeMap ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP); + if(oglDisplay.version >= 2) + glDisable(flags.cubeMap ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP); } #endif @@ -3444,7 +3688,7 @@ class OpenGLDisplayDriver : DisplayDriver GLSetupTexturing(true); #endif - glBindTexture(diffuseTarget, (GLuint)(uintptr)map.driverData); + glBindTexture(diffuseTarget, (GLuint)(uintptr)baseMap.driverData); #if ENABLE_GL_FFP if(!glCaps_shaders) @@ -3464,7 +3708,7 @@ class OpenGLDisplayDriver : DisplayDriver glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); */ - if(flags.cubeMap) + if(cubeMap) { #if _GLES glEnable(GL_TEXTURE_GEN_STR); @@ -3502,18 +3746,13 @@ class OpenGLDisplayDriver : DisplayDriver oglMesh.texCoords.use(texCoord, 2, GL_FLOAT, 0, oglMesh.texCoords.buffer ? null : mesh.texCoords); GLEnableClientState(TEXCOORDS); } - glClientActiveTexture(GL_TEXTURE0); + if(glClientActiveTexture) glClientActiveTexture(GL_TEXTURE0); } #endif - if(flags.tile) - { - glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - else + if(flags.setupTextures) { - glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_S, flags.tile ? GL_REPEAT : glClampFunction(oglDisplay.version)); + glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_T, flags.tile ? GL_REPEAT : glClampFunction(oglDisplay.version)); } } else @@ -3527,7 +3766,8 @@ class OpenGLDisplayDriver : DisplayDriver int separate = material.flags.separateSpecular ? GL_SEPARATE_SPECULAR_COLOR : GL_SINGLE_COLOR; if(separate != lastSeparate) { - GLLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, separate); + if(oglDisplay.version >= 2) + GLLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, separate); lastSeparate = separate; } } @@ -3539,11 +3779,11 @@ class OpenGLDisplayDriver : DisplayDriver #if ENABLE_GL_FFP if(!glCaps_shaders) { - glActiveTexture(GL_TEXTURE0 + tmu - 1); - glClientActiveTexture(GL_TEXTURE0 + tmu - 1); + if(glActiveTexture) glActiveTexture(GL_TEXTURE0 + tmu - 1); + if(glClientActiveTexture) glClientActiveTexture(GL_TEXTURE0 + tmu - 1); } #endif - GLMatrixMode(GL_TEXTURE); + GLMatrixMode(MatrixMode::texture); GLLoadIdentity(); if(material.uScale && material.vScale) GLScalef(material.uScale, material.vScale, 1); @@ -3551,8 +3791,8 @@ class OpenGLDisplayDriver : DisplayDriver #if ENABLE_GL_FFP if(!glCaps_shaders) { - glActiveTexture(GL_TEXTURE0); - glClientActiveTexture(GL_TEXTURE0); + if(glActiveTexture) glActiveTexture(GL_TEXTURE0); + if(glClientActiveTexture) glClientActiveTexture(GL_TEXTURE0); } #endif } @@ -3563,8 +3803,8 @@ class OpenGLDisplayDriver : DisplayDriver if(material.envMap && material.refractiveIndex) { float color[4] = { material.opacity, material.opacity, material.opacity, 1.0 }; - glActiveTexture(GL_TEXTURE0 + tmu); - glClientActiveTexture(GL_TEXTURE0 + tmu++); + if(glActiveTexture) glActiveTexture(GL_TEXTURE0 + tmu); + if(glClientActiveTexture) glClientActiveTexture(GL_TEXTURE0 + tmu++); glBindTexture(GL_TEXTURE_CUBE_MAP, (GLuint)(uintptr)material.envMap.driverData); glEnable(GL_TEXTURE_CUBE_MAP); #if _GLES @@ -3628,8 +3868,8 @@ class OpenGLDisplayDriver : DisplayDriver if(material.envMap && material.reflectivity) { float color[4] = { 1.0f - material.reflectivity, 1.0f - material.reflectivity, 1.0f - material.reflectivity, 1.0 }; - glActiveTexture(GL_TEXTURE0 + tmu); - glClientActiveTexture(GL_TEXTURE0 + tmu++); + if(glActiveTexture) glActiveTexture(GL_TEXTURE0 + tmu); + if(glClientActiveTexture) glClientActiveTexture(GL_TEXTURE0 + tmu++); glBindTexture(GL_TEXTURE_CUBE_MAP, (GLuint)(uintptr)material.envMap.driverData); glEnable(GL_TEXTURE_CUBE_MAP); #if _GLES @@ -3727,68 +3967,86 @@ class OpenGLDisplayDriver : DisplayDriver } glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &material.power); } +#endif + material.flags.setupTextures = false; + +#ifdef _DEBUG + CheckGLErrors(__FILE__, __LINE__); #endif } void FreeMesh(DisplaySystem displaySystem, Mesh mesh) { OGLMesh oglMesh = mesh.data; - if(oglMesh) - { - OGLSystem oglSystem = displaySystem.driverData; - GLCapabilities caps = glCaps; - SETCAPS(oglSystem.capabilities); + OGLSystem oglSystem = displaySystem.driverData; + GLCapabilities caps = glCaps; + if(oglSystem) SETCAPS(oglSystem.capabilities); - if(!mesh.flags.vertices) - { - oglMesh.vertices.free(); - delete mesh.vertices; - } - if(!mesh.flags.normals) - { - oglMesh.normals.free(); - delete mesh.normals; - } - if(!mesh.flags.tangents) - { - oglMesh.tangents.free(); - delete mesh.tangents; - } - if(!mesh.flags.lightVectors) - { - oglMesh.lightVectors.free(); - delete mesh.lightVectors; - } - if(!mesh.flags.texCoords1) - { - oglMesh.texCoords.free(); - delete mesh.texCoords; - } - if(!mesh.flags.texCoords2) - { - oglMesh.texCoords2.free(); - // delete mesh.texCoords2; - } - if(!mesh.flags.colors) - { - oglMesh.colors.free(); - delete mesh.colors; - } - if(!mesh.flags) + if(mesh.mab && oglMesh) + { + int baseVertex = mesh.baseVertex; + oglMesh.vertices.buffer = 0; + if(baseVertex != -1) { - delete oglMesh; - mesh.data = null; + uint vSize = 8 * sizeof(float); + mesh.mab.freeBlock(BlockEntry { baseVertex * vSize, (baseVertex + mesh.nVertices) * vSize-1 }); + mesh.baseVertex = -1; } - SETCAPS(caps); } + + if(!mesh.flags.vertices) + { + if(oglMesh) oglMesh.vertices.free(); + delete mesh.vertices; + } + if(!mesh.flags.normals) + { + if(oglMesh) oglMesh.normals.free(); + delete mesh.normals; + } + if(!mesh.flags.tangents) + { + if(oglMesh) oglMesh.tangents.free(); + delete mesh.tangents; + } + if(!mesh.flags.lightVectors) + { + if(oglMesh) oglMesh.lightVectors.free(); + delete mesh.lightVectors; + } + if(!mesh.flags.texCoords1) + { + if(oglMesh) oglMesh.texCoords.free(); + delete mesh.texCoords; + } + if(!mesh.flags.texCoords2) + { + if(oglMesh) oglMesh.texCoords2.free(); + // delete mesh.texCoords2; + } + if(!mesh.flags.colors) + { + if(oglMesh) oglMesh.colors.free(); + delete mesh.colors; + } + if(!mesh.flags) + { + delete oglMesh; + mesh.data = null; + } + if(oglSystem) SETCAPS(caps); } bool AllocateMesh(DisplaySystem displaySystem, Mesh mesh, MeshFeatures flags, int nVertices) { bool result = false; + OGLMesh oglMesh = mesh.data; + bool memAllocOnly = flags.memAllocOnly; + bool interleaved = flags.interleaved; + flags &= ~{ memAllocOnly = true, interleaved = true }; - if(!mesh.data) - mesh.data = OGLMesh { }; + if(!oglMesh) + mesh.data = oglMesh = OGLMesh { }; if(mesh.data) { if(mesh.nVertices == nVertices) @@ -3796,81 +4054,128 @@ class OpenGLDisplayDriver : DisplayDriver // Same number of vertices, adding features (Leaves the other features pointers alone) if(mesh.flags != flags) { - if(!mesh.flags.vertices && flags.vertices) + if(interleaved && !flags.doubleVertices && flags.vertices && flags.normals && flags.texCoords1) { - if(flags.doubleVertices) + //if(!mesh.vertices) { - mesh.vertices = (Vector3Df *)new Vector3D[nVertices]; + mesh.vertices = (Vector3Df *)renew mesh.vertices float[8*nVertices]; + if(!memAllocOnly) + oglMesh.vertices.allocate(nVertices * 8*sizeof(float), null, staticDraw); } - else - mesh.vertices = new Vector3Df[nVertices]; } - if(!mesh.flags.normals && flags.normals) + else { - if(flags.doubleNormals) + if(!mesh.flags.vertices && flags.vertices) { - mesh.normals = (Vector3Df *)new Vector3D[nVertices]; + if(flags.doubleVertices) + { + mesh.vertices = (Vector3Df *)new Vector3D[nVertices]; + } + else + mesh.vertices = new Vector3Df[nVertices]; + if(!memAllocOnly) + oglMesh.vertices.allocate(nVertices * (mesh.flags.doubleVertices ? sizeof(Vector3D) : sizeof(Vector3Df)), null, staticDraw); + } + if(!mesh.flags.normals && flags.normals) + { + if(flags.doubleNormals) + { + mesh.normals = (Vector3Df *)new Vector3D[nVertices]; + } + else + mesh.normals = new Vector3Df[nVertices]; + if(!memAllocOnly) + oglMesh.normals.allocate(nVertices * (mesh.flags.doubleNormals ? sizeof(Vector3D) : sizeof(Vector3Df)), null, staticDraw); + } + if(!mesh.flags.tangents && flags.tangents) + { + mesh.tangents = new Vector3Df[2*nVertices]; + if(!memAllocOnly) + oglMesh.tangents.allocate(nVertices * 2*sizeof(Vector3Df), null, staticDraw); + } + if(!mesh.flags.lightVectors && flags.lightVectors) + { + mesh.lightVectors = new ColorRGB[nVertices]; + if(!memAllocOnly) + oglMesh.lightVectors.allocate(nVertices * sizeof(ColorRGB), null, staticDraw); + } + if(!mesh.flags.texCoords1 && flags.texCoords1) + { + mesh.texCoords = new Pointf[nVertices]; + if(!memAllocOnly) + oglMesh.texCoords.allocate(nVertices * sizeof(Pointf), null, staticDraw); + } + if(!mesh.flags.colors && flags.colors) + { + mesh.colors = new ColorRGBAf[nVertices]; + if(!memAllocOnly) + oglMesh.colors.allocate(nVertices * sizeof(ColorRGBAf), null, staticDraw); } - else - mesh.normals = new Vector3Df[nVertices]; } - if(!mesh.flags.tangents && flags.tangents) + } + } + else + { + // New number of vertices, reallocate all current and new features + flags |= mesh.flags; + if(interleaved && !flags.doubleVertices && flags.vertices && flags.normals && flags.texCoords1) + { + mesh.vertices = (Vector3Df *)renew mesh.vertices float[8*nVertices]; + if(!memAllocOnly) + oglMesh.vertices.allocate(nVertices * 8*sizeof(float), null, staticDraw); + } + else + { + if(flags.vertices) { - mesh.tangents = new Vector3Df[2*nVertices]; + if(flags.doubleVertices) + { + mesh.vertices = (Vector3Df *)renew mesh.vertices Vector3D[nVertices]; + } + else + mesh.vertices = renew mesh.vertices Vector3Df[nVertices]; + if(!memAllocOnly) + oglMesh.vertices.allocate(nVertices * (mesh.flags.doubleVertices ? sizeof(Vector3D) : sizeof(Vector3Df)), null, staticDraw); } - if(!mesh.flags.lightVectors && flags.lightVectors) + if(flags.normals) { - mesh.lightVectors = new ColorRGB[nVertices]; + if(flags.doubleNormals) + { + mesh.normals = (Vector3Df *)renew mesh.normals Vector3D[nVertices]; + } + else + mesh.normals = renew mesh.normals Vector3Df[nVertices]; + if(!memAllocOnly) + oglMesh.normals.allocate(nVertices * (mesh.flags.doubleNormals ? sizeof(Vector3D) : sizeof(Vector3Df)), null, staticDraw); } - if(!mesh.flags.texCoords1 && flags.texCoords1) + if(flags.texCoords1) { - mesh.texCoords = new Pointf[nVertices]; + mesh.texCoords = renew mesh.texCoords Pointf[nVertices]; + if(!memAllocOnly) + oglMesh.texCoords.allocate(nVertices * sizeof(Pointf), null, staticDraw); } - if(!mesh.flags.colors && flags.colors) + if(flags.colors) { - mesh.colors = new ColorRGBAf[nVertices]; + mesh.colors = renew mesh.colors ColorRGBAf[nVertices]; + if(!memAllocOnly) + oglMesh.colors.allocate(nVertices * sizeof(ColorRGBAf), null, staticDraw); } - } - } - else - { - // New number of vertices, reallocate all current and new features - flags |= mesh.flags; - if(flags.vertices) - { - if(flags.doubleVertices) + if(flags.tangents) { - mesh.vertices = (Vector3Df *)renew mesh.vertices Vector3D[nVertices]; + mesh.tangents = renew mesh.tangents Vector3Df[2 * nVertices]; + if(!memAllocOnly) + oglMesh.tangents.allocate(nVertices * 2*sizeof(Vector3Df), null, staticDraw); } - else - mesh.vertices = renew mesh.vertices Vector3Df[nVertices]; - } - if(flags.normals) - { - if(flags.doubleNormals) + if(flags.lightVectors) { - mesh.normals = (Vector3Df *)renew mesh.normals Vector3D[nVertices]; + mesh.lightVectors = renew mesh.lightVectors ColorRGB[nVertices]; + if(!memAllocOnly) + oglMesh.lightVectors.allocate(nVertices * sizeof(ColorRGB), null, staticDraw); } - else - mesh.normals = renew mesh.normals Vector3Df[nVertices]; - } - if(flags.texCoords1) - { - mesh.texCoords = renew mesh.texCoords Pointf[nVertices]; - } - if(flags.colors) - { - mesh.colors = renew mesh.colors ColorRGBAf[nVertices]; - } - if(flags.tangents) - { - mesh.tangents = renew mesh.tangents Vector3Df[2 * nVertices]; - } - if(flags.lightVectors) - { - mesh.lightVectors = renew mesh.lightVectors ColorRGB[nVertices]; } } + oglMesh.needAlloc = memAllocOnly; + oglMesh.interleaved = interleaved; result = true; } return result; @@ -3881,32 +4186,125 @@ class OpenGLDisplayDriver : DisplayDriver OGLSystem oglSystem = displaySystem.driverData; GLCapabilities caps = glCaps; SETCAPS(oglSystem.capabilities); - + // TODO: Not yet handling single allocation of all (shared) mesh indices when uploading separately if(glCaps_vertexBuffer) { + GLMB mab = mesh.mab; OGLMesh oglMesh = mesh.data; + if(!oglMesh) + oglMesh = mesh.data = OGLMesh { needAlloc = true }; + if(!flags) flags = mesh.flags; - if(flags.vertices) - oglMesh.vertices.allocate( - mesh.nVertices * (mesh.flags.doubleVertices ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.vertices, staticDraw); - if(flags.normals) - oglMesh.normals.allocate( - mesh.nVertices * (mesh.flags.doubleNormals ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.normals, staticDraw); + if(mab) + { + int nVertices = mesh.nVertices; + uint vSize = 8 * sizeof(float); + if(oglMesh.needAlloc) + { + BlockEntry block = mab.allocate(attributes, nVertices * vSize); + oglMesh.interleaved = true; + mesh.baseVertex = block ? block.start / vSize : -1; + oglMesh.vertices.buffer = mab.ab.buffer; + oglMesh.needAlloc = false; + } + if(flags.interleaved) + oglMesh.vertices.upload(vSize * mesh.baseVertex, nVertices * vSize, mesh.vertices); + else + { + // Interleave here for MAB if the data isn't already interleaved.. + float * buf = new float[nVertices * 8]; + int i; + Vector3Df * vertices = mesh.vertices; + Vector3Df * normals = mesh.normals; + Pointf * texCoords = mesh.texCoords; + for(i = 0; i < nVertices; i++) + { + float * v = buf + (i << 3); + if(vertices) + memcpy(v, vertices + i, 3 * sizeof(float)); + if(normals) + memcpy(v + 3, normals + i, 3 * sizeof(float)); + else + memset(v + 3, 0, 3 * sizeof(float)); + if(texCoords) + memcpy(v + 6, texCoords + i, 2 * sizeof(float)); + else + memset(v + 6, 0, 2 * sizeof(float)); + } + oglMesh.vertices.upload(vSize * mesh.baseVertex, nVertices * vSize, buf); + delete buf; + } + } + else if(oglMesh.interleaved && !flags.doubleVertices && + mesh.flags.vertices && mesh.flags.normals && mesh.flags.texCoords1 && + flags & { vertices = true, normals = true, texCoords1 = true }) + { + // temporary solution for interleaved attributes + if(oglMesh.needAlloc) + { + oglMesh.vertices.allocate(mesh.nVertices * 8 * sizeof(float), mesh.vertices, staticDraw); + oglMesh.needAlloc = false; + } + else + oglMesh.vertices.upload(0, mesh.nVertices * 8 * sizeof(float), mesh.vertices); + + if(!mab && oglMesh.needAlloc) + { + if(flags.colors) + oglMesh.colors.allocate(mesh.nVertices * sizeof(ColorRGBAf), mesh.colors, staticDraw); + if(flags.tangents) + oglMesh.tangents.allocate(mesh.nVertices * 2*sizeof(Vector3Df), mesh.tangents, staticDraw); + if(flags.lightVectors) + oglMesh.lightVectors.allocate(mesh.nVertices * sizeof(ColorRGB), mesh.lightVectors, staticDraw); + oglMesh.needAlloc = false; + } + else + { + if(flags.colors) + oglMesh.colors.upload(0, mesh.nVertices * sizeof(ColorRGBAf), mesh.colors); + + if(flags.tangents) + oglMesh.tangents.upload(0, mesh.nVertices * 2*sizeof(Vector3Df), mesh.tangents); - if(flags.texCoords1) - oglMesh.texCoords.allocate( - mesh.nVertices * sizeof(Pointf), mesh.texCoords, staticDraw); + if(flags.lightVectors) + oglMesh.lightVectors.upload(0, mesh.nVertices * sizeof(ColorRGB), mesh.lightVectors); + } + } + else if(!mab && oglMesh.needAlloc) + { + if(flags.vertices) + oglMesh.vertices.allocate(mesh.nVertices * (mesh.flags.doubleVertices ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.vertices, staticDraw); + if(flags.normals) + oglMesh.normals.allocate(mesh.nVertices * (mesh.flags.doubleNormals ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.normals, staticDraw); + if(flags.texCoords1) + oglMesh.texCoords.allocate(mesh.nVertices * sizeof(Pointf), mesh.texCoords, staticDraw); + if(flags.colors) + oglMesh.colors.allocate(mesh.nVertices * sizeof(ColorRGBAf), mesh.colors, staticDraw); + if(flags.tangents) + oglMesh.tangents.allocate(mesh.nVertices * 2*sizeof(Vector3Df), mesh.tangents, staticDraw); + if(flags.lightVectors) + oglMesh.lightVectors.allocate(mesh.nVertices * sizeof(ColorRGB), mesh.lightVectors, staticDraw); + oglMesh.needAlloc = false; + } + else + { + if(flags.vertices) + oglMesh.vertices.upload(0, mesh.nVertices * (mesh.flags.doubleVertices ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.vertices); + if(flags.normals) + oglMesh.normals.upload(0, mesh.nVertices * (mesh.flags.doubleNormals ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.normals); + if(flags.texCoords1) + oglMesh.texCoords.upload(0, mesh.nVertices * sizeof(Pointf), mesh.texCoords); - if(flags.colors) - oglMesh.colors.allocate( - mesh.nVertices * sizeof(ColorRGBAf), mesh.colors, staticDraw); + if(flags.colors) + oglMesh.colors.upload(0, mesh.nVertices * sizeof(ColorRGBAf), mesh.colors); - if(flags.tangents) - oglMesh.tangents.allocate(mesh.nVertices * 2*sizeof(Vector3Df), mesh.tangents, staticDraw); + if(flags.tangents) + oglMesh.tangents.upload(0, mesh.nVertices * 2*sizeof(Vector3Df), mesh.tangents); - if(flags.lightVectors) - oglMesh.lightVectors.allocate(mesh.nVertices * sizeof(ColorRGB), mesh.lightVectors, staticDraw); + if(flags.lightVectors) + oglMesh.lightVectors.upload(0, mesh.nVertices * sizeof(ColorRGB), mesh.lightVectors); + } } SETCAPS(caps); } @@ -3918,8 +4316,9 @@ class OpenGLDisplayDriver : DisplayDriver return result; } - void FreeIndices(DisplaySystem displaySystem, OGLIndices oglIndices) + void FreeIndices(DisplaySystem displaySystem, PrimitiveSingle group) { + OGLIndices oglIndices = group.data; OGLSystem oglSystem = displaySystem.driverData; GLCapabilities caps = glCaps; SETCAPS(oglSystem.capabilities); @@ -3927,7 +4326,6 @@ class OpenGLDisplayDriver : DisplayDriver if(oglIndices) { oglIndices.buffer.free(); - delete oglIndices.indices; delete oglIndices; } SETCAPS(caps); @@ -3935,99 +4333,113 @@ class OpenGLDisplayDriver : DisplayDriver void * AllocateIndices(DisplaySystem displaySystem, int nIndices, bool indices32bit) { - OGLIndices oglIndices = OGLIndices { }; - if(oglIndices) - { - oglIndices.indices = (void *)(indices32bit ? new uint32[nIndices] : new uint16[nIndices]); - oglIndices.nIndices = nIndices; - } - return oglIndices; + return OGLIndices { nIndices = nIndices }; } - void UnlockIndices(DisplaySystem displaySystem, OGLIndices oglIndices, bool indices32bit, int nIndices) + void UnlockIndices(DisplaySystem displaySystem, PrimitiveSingle group, bool indices32bit, int nIndices, GLMB mb) { OGLSystem oglSystem = displaySystem.driverData; GLCapabilities caps = glCaps; + OGLIndices oglIndices = group.data; SETCAPS(oglSystem.capabilities); + // TODO: Not yet handling single allocation of all (shared) mesh indices when uploading separately if(glCaps_vertexBuffer) { + uint16 * b = group.indices; + uint ixSize = indices32bit ? sizeof(uint32) : sizeof(uint16); + uint size = nIndices * ixSize; if(!glCaps_intAndDouble && indices32bit) { - if(!oglIndices.buffer.buffer) - glGenBuffers(1, &oglIndices.buffer.buffer); - if(glabCurElementBuffer != oglIndices.buffer.buffer) - GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oglIndices.buffer.buffer); - + uint * pointer = (uint *)b; + int i; + if(nIndices > oglSystem.shortBDSize) { - uint * pointer = (uint *)oglIndices.indices; - int i; - uint16 * b; - if(nIndices > oglSystem.shortBDSize) - { - oglSystem.shortBDSize = nIndices; - oglSystem.shortBDBuffer = renew oglSystem.shortBDBuffer uint16[oglSystem.shortBDSize]; - } - b = oglSystem.shortBDBuffer; - for(i = 0; i < nIndices; i++) - b[i] = (uint16)pointer[i]; - - glBufferData(GL_ELEMENT_ARRAY_BUFFER, nIndices * sizeof(uint16), b, GL_STATIC_DRAW); + oglSystem.shortBDSize = nIndices; + oglSystem.shortBDBuffer = renew oglSystem.shortBDBuffer uint16[oglSystem.shortBDSize]; } + b = oglSystem.shortBDBuffer; + for(i = 0; i < nIndices; i++) + b[i] = (uint16)pointer[i]; + size = nIndices * sizeof(uint16); } + if(!oglIndices) { group.data = oglIndices = OGLIndices { nIndices = 0 }; }; + if(mb == null && (!oglIndices.buffer.buffer || oglIndices.nIndices == nIndices)) + oglIndices.buffer.allocate(size, b, staticDraw); else - oglIndices.buffer.allocate( - nIndices * (indices32bit ? sizeof(uint32) : sizeof(uint16)), - oglIndices.indices, staticDraw); + { + if(mb != null) + { + BlockEntry block = mb.allocate(elements, size); + group.baseIndex = block ? block.start / ixSize : -1; + oglIndices.buffer.buffer = mb.ab.buffer; + } + oglIndices.buffer.upload(group.baseIndex * ixSize, size, b); + } } SETCAPS(caps); } - uint16 * LockIndices(DisplaySystem displaySystem, OGLIndices oglIndices) + void * LockIndices(DisplaySystem displaySystem, PrimitiveSingle group) { - - return oglIndices.indices; + return (void *)1; } void SelectMesh(Display display, Mesh mesh) { -#if !defined( __ANDROID__) && !defined(__APPLE__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__) +#if !defined(_GLES) && !defined(_GLES2) && !defined(__APPLE__) && !defined(__UWP__) #if defined(__WIN32__) if(glUnlockArraysEXT) #endif - if(!glCaps_vertexBuffer && display.display3D.mesh) + if(!glCaps_vertexBuffer && display.display3D && display.display3D.mesh) glUnlockArraysEXT(); #endif if(mesh) { OGLMesh oglMesh = mesh.data; bool collectingHits = display.display3D && display.display3D.collectingHits; + int baseVertexOffset = 0; + +#if defined(_GLES) || defined(_GLES2) + baseVertexOffset = mesh.baseVertex; +#endif // *** Vertex Stream *** GLEnableClientState(VERTICES); if(!collectingHits && oglMesh) { - oglMesh.vertices.use(vertex, 3, (mesh.flags.intVertices ? GL_INT : mesh.flags.doubleVertices ? GL_DOUBLE : GL_FLOAT), 0, oglMesh.vertices.buffer ? null : (double *)mesh.vertices); + bool interleaved = oglMesh.interleaved; + if(mesh.mab && !mesh.mab.keepSameBufferID) + { + uint buffer = mesh.mab.ab.buffer; + oglMesh.vertices.buffer = buffer; + } + oglMesh.vertices.use(vertex, 3, (mesh.flags.intVertices ? GL_INT : mesh.flags.doubleVertices ? GL_DOUBLE : GL_FLOAT), + interleaved ? 8*sizeof(float) : 0, + oglMesh.vertices.buffer ? (void *)((baseVertexOffset*8)*sizeof(float)) : (void *)mesh.vertices); // *** Normals Stream *** if(mesh.normals || mesh.flags.normals) { GLEnableClientState(NORMALS); - oglMesh.normals.use(normal, 3, GL_FLOAT, 0, oglMesh.normals.buffer ? null : mesh.normals); + if(interleaved) + oglMesh.vertices.use(normal, 3, GL_FLOAT, 8*sizeof(float), (void *)((baseVertexOffset*8+3)*sizeof(float))); + else + oglMesh.normals.use(normal, 3, GL_FLOAT, 0, oglMesh.normals.buffer ? (void *)((baseVertexOffset*3)*sizeof(float)) : mesh.normals); } else GLDisableClientState(NORMALS); #if ENABLE_GL_SHADERS - if(glCaps_shaders) + if(glCaps_shaders/* && !oglMesh.interleaved*/) // For now tangents are stored separately... { // *** Tangents Stream *** if(mesh.tangents || mesh.flags.tangents) { GLEnableClientState(TANGENTS1); GLEnableClientState(TANGENTS2); - oglMesh.tangents.use(tangent1, 3, GL_FLOAT, sizeof(Vector3Df)*2, oglMesh.tangents.buffer ? null : mesh.tangents); - oglMesh.tangents.use(tangent2, 3, GL_FLOAT, sizeof(Vector3Df)*2, oglMesh.tangents.buffer ? (void *)sizeof(Vector3Df) : mesh.tangents+1); + oglMesh.tangents.use(tangent1, 3, GL_FLOAT, sizeof(Vector3Df)*2, oglMesh.tangents.buffer ? (void *)(baseVertexOffset * 6 * sizeof(float)) : mesh.tangents); + oglMesh.tangents.use(tangent2, 3, GL_FLOAT, sizeof(Vector3Df)*2, oglMesh.tangents.buffer ? (void *)((baseVertexOffset*6 + 3) * sizeof(float)) : mesh.tangents+1); } else { @@ -4041,7 +4453,10 @@ class OpenGLDisplayDriver : DisplayDriver if(mesh.texCoords || mesh.flags.texCoords1) { GLEnableClientState(TEXCOORDS); - oglMesh.texCoords.use(texCoord, 2, GL_FLOAT, 0, oglMesh.texCoords.buffer ? null : mesh.texCoords); + if(interleaved) + oglMesh.vertices.use(texCoord, 2, GL_FLOAT, 8*sizeof(float), (void *)((baseVertexOffset*8 + 6)*sizeof(float))); + else + oglMesh.texCoords.use(texCoord, 2, GL_FLOAT, 0, oglMesh.texCoords.buffer ? (void *)(baseVertexOffset*2 *sizeof(float)) : mesh.texCoords); } else GLDisableClientState(TEXCOORDS); @@ -4064,7 +4479,7 @@ class OpenGLDisplayDriver : DisplayDriver if(mesh.colors || mesh.flags.colors) { GLEnableClientState(COLORS); - oglMesh.colors.use(color, 4, GL_FLOAT, 0, oglMesh.colors.buffer ? null : mesh.colors); + oglMesh.colors.use(color, 4, GL_FLOAT, 0, oglMesh.colors.buffer ? (void *)(baseVertexOffset * 4 * sizeof(float)) : mesh.colors); } else GLDisableClientState(COLORS); @@ -4127,7 +4542,7 @@ class OpenGLDisplayDriver : DisplayDriver GLDisableClientState(COLORS); } -#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__) +#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__) && !defined(__UWP__) #if defined(__WIN32__) if(glLockArraysEXT) @@ -4136,36 +4551,50 @@ class OpenGLDisplayDriver : DisplayDriver glLockArraysEXT(0, mesh.nVertices); #endif } +#ifdef _DEBUG + CheckGLErrors(__FILE__, __LINE__); +#endif } - void DrawPrimitives(Display display, PrimitiveSingle * primitive, Mesh mesh) + void DrawPrimitives(Display display, PrimitiveSingle primitive, Mesh mesh) { - if(primitive->type.vertexRange) + PrimitiveGroupType type = primitive.type; + if(type.vertexRange) { GLFlushMatrices(); - glDrawArrays(getPrimitiveType(primitive->type.primitiveType), primitive->first, primitive->nVertices); + glDrawArrays(getPrimitiveType(type.primitiveType), primitive.first, primitive.nVertices); } else { - OGLIndices oglIndices = primitive->data; - bool collectingHits = display.display3D && display.display3D.collectingHits; + Display3D display3D = display.display3D; + uint primType = getPrimitiveType(type.primitiveType); + bool indices32Bit = type.indices32bit; + OGLIndices oglIndices = mesh.displaySystem ? primitive.data : null; + bool collectingHits = display3D && display3D.collectingHits; GLEAB eab = ((!collectingHits && oglIndices && glCaps_vertexBuffer) ? oglIndices.buffer : noEAB); - if(!glCaps_intAndDouble && !glCaps_vertexBuffer && primitive->type.indices32bit) + uint nIndices = primitive.nIndices; + if(mesh.meab && (!oglIndices || !mesh.meab.keepSameBufferID)) + eab.buffer = mesh.meab.ab.buffer; + + if(!glCaps_intAndDouble && !glCaps_vertexBuffer && indices32Bit) { - uint16 * temp = new uint16[primitive->nIndices]; - uint32 * src = (uint32 *)(oglIndices ? oglIndices.indices : primitive->indices); + uint16 * temp = new uint16[nIndices]; + uint32 * src = (uint32 *)(oglIndices ? primitive.indices : primitive.indices); int i; - for(i = 0; i < primitive->nIndices; i++) + for(i = 0; i < primitive.nIndices; i++) temp[i] = (uint16)src[i]; - eab.draw(getPrimitiveType(primitive->type.primitiveType), primitive->nIndices, GL_UNSIGNED_SHORT, temp); + eab.draw(primType, nIndices, GL_UNSIGNED_SHORT, temp); delete temp; } else - eab.draw(getPrimitiveType(primitive->type.primitiveType), primitive->nIndices, - primitive->type.indices32bit ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, - eab.buffer ? 0 : (oglIndices ? oglIndices.indices : primitive->indices)); - GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + eab.draw2(getPrimitiveType(type.primitiveType), nIndices, + indices32Bit ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, + eab.buffer ? (void *)(uintptr)(primitive.baseIndex * (indices32Bit ? 4 : 2)) : primitive.indices, mesh.baseVertex); + // TODO: Do this somewhere else... GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } +#ifdef _DEBUG + CheckGLErrors(__FILE__, __LINE__); +#endif } void PushMatrix(Display display) @@ -4190,10 +4619,11 @@ class OpenGLDisplayDriver : DisplayDriver } else if(camera) { + Vector3D *cPosition = &camera.cPosition; GLTranslated( - matrix.m[3][0] - camera.cPosition.x, - matrix.m[3][1] - camera.cPosition.y, - matrix.m[3][2] - camera.cPosition.z); + matrix.m[3][0] - cPosition->x, + matrix.m[3][1] - cPosition->y, + matrix.m[3][2] - cPosition->z); } else GLTranslated( @@ -4223,10 +4653,10 @@ IS_GLGetContext(DisplaySystem displaySystem) { if(displaySystem) { -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) OGLSystem system = displaySystem.driverData; return system.glrc; -#elif defined(__ANDROID__) || defined(__ODROID__) +#elif defined(__ANDROID__) || defined(__ODROID__) || defined(__UWP__) return eglContext; #elif defined(__EMSCRIPTEN__) OGLSystem system = displaySystem.driverData; diff --git a/ecere/src/gfx/drivers/Win32BitmapPrinterDisplayDriver.ec b/ecere/src/gfx/drivers/Win32BitmapPrinterDisplayDriver.ec index 0f6709d819..83e4db521e 100644 --- a/ecere/src/gfx/drivers/Win32BitmapPrinterDisplayDriver.ec +++ b/ecere/src/gfx/drivers/Win32BitmapPrinterDisplayDriver.ec @@ -19,7 +19,11 @@ import "instance" #undef String #undef strlen +#if defined(__clang__) +#define wcstok(a, b) wcstok(a, b, (void *)0) +#else default wchar_t *wcstok(wchar_t *strToken,const wchar_t *strDelimit); +#endif import "Display" diff --git a/ecere/src/gfx/drivers/Win32PrinterDisplayDriver.ec b/ecere/src/gfx/drivers/Win32PrinterDisplayDriver.ec index ef290e0233..b303c70633 100644 --- a/ecere/src/gfx/drivers/Win32PrinterDisplayDriver.ec +++ b/ecere/src/gfx/drivers/Win32PrinterDisplayDriver.ec @@ -17,7 +17,11 @@ import "instance" #undef String #undef strlen +#if defined(__clang__) +#define wcstok(a, b) wcstok(a, b, (void *)0) +#else default wchar_t *wcstok(wchar_t *strToken,const wchar_t *strDelimit); +#endif import "Display" diff --git a/ecere/src/gfx/drivers/gl3/GLMultiDraw.ec b/ecere/src/gfx/drivers/gl3/GLMultiDraw.ec new file mode 100644 index 0000000000..5e103aa03a --- /dev/null +++ b/ecere/src/gfx/drivers/gl3/GLMultiDraw.ec @@ -0,0 +1,723 @@ +#if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__) + +import "OpenGLDisplayDriver" + +#include "gl123es.h" + +#ifdef _DEBUG +#define GLSTATS +#endif + +#if defined(_GLES3) +#include +#endif + +#if defined(_GLES2) || defined(_GLES3) +#define GL_R16 GL_LUMINANCE +#define GL_RED GL_LUMINANCE // Should this be GL_ALPHA ? Swizzle mode needed? +#endif + +public define drawIDAttribute = 7; +public define posOffsetAttribute = 8; + +public define transform0Attribute = 9; +public define transform1Attribute = 10; +public define transform2Attribute = 11; +public define transform3Attribute = 12; + +private: + +// Set glCapabilities.gpuCommands = false as a work-around for Intel driver that does not seem to support +// indirect commands buffers, and/or glCapabilities.mdei = false + +#define GL_CLAMP_TO_EDGE 0x812F + +#if defined _GLES1 +#define glClampFunction(version) (GL_CLAMP) +#elif defined(_GLES2) || defined(_GLES3) +#define glClampFunction(version) (GL_CLAMP_TO_EDGE) +#else +#define glClampFunction(version) (version >= 2 ? GL_CLAMP_TO_EDGE : GL_CLAMP) +#endif + +public struct FreeSpots +{ + uint size; + int * spots; + int nextSpot; + int used; + + void init(uint count) + { + if(count) + { + int i; + spots = new int[count]; + for(i = 0; i < (int)count-1; i++) + spots[i] = i+1; + spots[i] = -1; + nextSpot = 0; + size = count; + } + } + + void markFree(int spot) + { + if(spot != -1) + { + spots[spot] = nextSpot; + nextSpot = spot; + used--; + } + } + + int next() + { + int result = -1; + if(nextSpot != -1) + { + result = nextSpot; + nextSpot = spots[nextSpot]; + spots[result] = -1; + used++; + } + return result; + } + + + // NOTE: Only supporting resizing bigger... + void resize(uint count) + { + if(!size) + init(count); + else if(count > size) + { + int i; + + spots = renew spots int[count]; + for(i = size-1; i < count-1; i++) + spots[i] = i+1; + spots[i] = -1; + if(nextSpot == -1) + nextSpot = size; + size = count; + } + } +}; + +default: +#if defined(_GLES3) +int glVersion = 3; +#elif defined(_GLES2) +int glVersion = 2; +#elif defined(_GLES) +int glVersion = 1; +#else +int glVersion = 0; +#endif +private: + +#if (defined(__ANDROID__) && !defined(__LUMIN__)) || defined(__UWP__) +uint tempTexFBO; // TODO: Free this on termination... glDeleteFramebuffers(1, &tempTexFBO); +#endif + +public struct GLArrayTexture +{ + uint texture; + uint width, height, numLayers; + uint numLevels; + bool maxLevel; + int format; + /*private */FreeSpots spots; + + void free() + { +#ifdef GLSTATS + GLStats::freeTextures(1, &texture); +#endif + if(texture) + glDeleteTextures(1, &texture); + delete spots.spots; + texture = 0; + } + + void init(int levels, int w, int h, int count) + { +#if !defined(_GLES) && !defined(_GLES2) && !defined(_GLES3) + int internalFormat = GL_RGBA8; +#else + int internalFormat = GL_RGBA; +#endif + _init(levels, w, h, count, internalFormat, false); + } + + void initMaxLevel(int levels, int w, int h, int count) + { +#if !defined(_GLES) && !defined(_GLES2) && !defined(_GLES3) + int internalFormat = GL_RGBA8; +#else + int internalFormat = GL_RGBA; +#endif + _init(levels, w, h, count, internalFormat, true); + } + + void initUShort(int levels, int w, int h, int count) + { + _init(levels, w, h, count, GL_R16, false); + } + void initRGBUShort(int levels, int w, int h, int count) + { + _init(levels, w, h, count, GL_RGB16 /*UI*/, false); + } + void initRGBAUShort(int levels, int w, int h, int count) + { + _init(levels, w, h, count, GL_RGBA16 /*UI*/, false); + } + + void _init(int levels, int w, int h, int count, int format, bool setMaxLevel) + { + // TODO: How to replace texture arrays in OpenGL ES 2? They are supported in ES 3 / WebGL 2. + // Easy way to alternatively use a list of different textures? +#if (!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3) + int target = GL_TEXTURE_2D_ARRAY; + if(texture) + { + // glTexStorage* immutable-format textures need to be deleted before invoking it again +#ifdef GLSTATS + GLStats::freeTextures(1, &texture); +#endif + glDeleteTextures(1, &texture); + texture = 0; + } + +#if !defined(_GLES) && !defined(_GLES2) && !defined(_GLES3) + if(!glVersion) + glVersion = ogl_GetMajorVersion(); +#endif + + if(!texture) + glGenTextures(1, &texture); + + this.format = format; + maxLevel = setMaxLevel; + numLevels = levels; + width = w; + height = h; + if(count) + numLayers = count; + + glBindTexture(target, texture); + +#if (!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3) + if(setMaxLevel) + glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1); +#endif + +#ifdef _DEBUG + CheckGLErrors(__FILE__,__LINE__); +#endif + if(glVersion) // >= 4) // && glMinorVersion >= 2) + glTexStorage3D(target, levels, format, w, h, numLayers); + else + { + // TODO: Review for GL 3 support? + /*if(numLayers > 1 && glVersion) + glTexImage3D(target, levels, format, w, h, numLayers, 0, GL_RGBA, GL_UNSIGNED_BYTE, null); + else*/ + glTexImage2D(target, levels, format, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, null); + } + +#ifdef GLSTATS + GLStats::allocTexture(texture, w, h * numLayers, levels > 1); +#endif + +#ifdef _DEBUG + CheckGLErrors(__FILE__,__LINE__); +#endif + #ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT + if(glVersion >= 2) + glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0 ); + #endif + + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, levels > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if(levels > 3) // TODO: Wrap options as a property? + { + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + else + { + glTexParameteri(target, GL_TEXTURE_WRAP_S, glClampFunction(glVersion)); //GL_CLAMP_TO_EDGE + glTexParameteri(target, GL_TEXTURE_WRAP_T, glClampFunction(glVersion)); //GL_CLAMP_TO_EDGE + } + glBindTexture(target, 0); +#endif + } + + void resize(uint numLayers, uint targetFBO) + { +#if (!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3) + GLArrayTexture tmp { }; + + tmp._init(numLevels, width, height, numLayers, format, maxLevel); + tmp.copy(this, targetFBO); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); // TOCHECK: +#ifdef GLSTATS + GLStats::freeTextures(1, &texture); +#endif + glDeleteTextures(1, &texture); + + texture = tmp.texture; + this.numLayers = numLayers; + if(spots.spots) + spots.resize(numLayers); +#endif + } + + void copy(GLArrayTexture src, uint targetFBO) + { +#if (!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3) + int target = GL_TEXTURE_2D_ARRAY; + // 4.3+ +#if (!defined(__ANDROID__) || defined(__LUMIN__)) && !defined(__UWP__) + int level = 0; + int w = width, h = height; + + // glCopyImageSubData doesn't use bound texture... glBindTexture(target, src.texture); + for(level = 0; level < numLevels; level++) + { + // NOTE: On nVidia 455 linux drivers, copying lower mipmaps for compressed texture is not working + glCopyImageSubData( + src.texture, target, level, 0, 0, 0, + texture, target, level, 0, 0, 0, + w, h, src.numLayers); + if(w > 1) w >>= 1; + if(w > 1) h >>= 1; + } + // glBindTexture(target, 0); +#else + // FALLBACK for 3.0->4.2: + int i; + + // TODO: Fix for multiple levels... + + if(!tempTexFBO) + glGenFramebuffers(1, &tempTexFBO); + glBindFramebuffer(GL_FRAMEBUFFER, tempTexFBO); + glBindTexture(target, texture); + for(i = 0; i < src.numLayers; i++) + { + /* FIXME: binding a compressed texture as a color attachment won't work + Without ARB_copy_image, probably need to use PBO (glGetCompressedTexImage) */ + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, src.texture, 0, i); + glCopyTexSubImage3D(target, 0, 0, 0, i, 0, 0, width, height); + } + glBindTexture(target, 0); + glBindFramebuffer(GL_FRAMEBUFFER, targetFBO); +#endif +#endif + } + + void setLayer(int level, int x, int y, int layer, byte * c, uint targetFBO) + { + setLayerFormat(level, x, y, layer, c, targetFBO, GL_RGBA, GL_UNSIGNED_BYTE); + } + + void setLayerUShort(int level, int x, int y, int layer, byte * c, uint targetFBO) + { + setLayerFormat(level, x, y, layer, c, targetFBO, GL_RED, GL_UNSIGNED_SHORT); + } + + void setLayerRGBUShort(int level, int x, int y, int layer, byte * c, uint targetFBO) + { + setLayerFormat(level, x, y, layer, c, targetFBO, GL_RGB, GL_UNSIGNED_SHORT); + } + + void setLayerRGBAUShort(int level, int x, int y, int layer, byte * c, uint targetFBO) + { + setLayerFormat(level, x, y, layer, c, targetFBO, GL_RGBA, GL_UNSIGNED_SHORT); + } + + void setLayerFormat(int level, int x, int y, int layer, byte * c, uint targetFBO, int format, int type) + { +#if (!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3) + int target = GL_TEXTURE_2D_ARRAY; + + if(layer >= numLayers) + resize(layer + Max(2, layer)/2, targetFBO); + + glBindTexture(target, texture); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexSubImage3D(target, level, x, y, layer, width >> level, height >> level, 1, format, type, c); + glBindTexture(target, 0); +#endif + } + + void setLayerCompressed(int level, int x, int y, int layer, byte * c, uintsize sizeBytes, uint targetFBO) + { +#if (!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3) + int target = GL_TEXTURE_2D_ARRAY; + + if(layer >= numLayers) + resize(layer + Max(2, layer)/2, targetFBO); + + glBindTexture(target, texture); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glCompressedTexSubImage3D(target, level, x, y, layer, width >> level, height >> level, 1, + format, sizeBytes, c); + glBindTexture(target, 0); +#endif + } + + void set1x1Layer(int layer, ColorAlpha color, uint targetFBO) + { + byte c[4] = { color.color.r, color.color.g, color.color.b, color.a }; + setLayer(0, 0, 0, layer, c, targetFBO); + } + + void bind() + { +#if (!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3) + glBindTexture(GL_TEXTURE_2D_ARRAY, texture); +#endif + } + + int allocateLayer(uint targetFBO) + { + int layer; + if(!spots.spots) + spots.init(numLayers); + layer = spots.spots ? spots.next() : -1; + if(layer == -1 && numLayers < 2048) + { + resize(Min(2048, Max(8, numLayers + numLayers/2)), targetFBO); + layer = spots.next(); + } + if(spots.used >= 2048) + PrintLn("WARNING: Already 2048 layers used in array texture!"); + if(layer == -1) + PrintLn("ERROR: Failure to allocate texture layer!"); + return layer; + } + + void freeLayer(int layer) + { + spots.markFree(layer); + } +}; + +public struct GLDrawCommand +{ + uint count; + uint instanceCount; + uint firstIndex; + uint baseVertex; + uint baseInstance; +}; + +public struct GLMultiDraw +{ + GLMB indexGLMB; + GLMB vertexGLMB; + GLAB idsAB; + GLCAB commandsB; + uint vao; + uint commandsCount; + GLIMTKMode drawMode; + uint commandsAlloced; + uint * drawIDs; + GLDrawCommand * commands; + uint type; + uint idsAlloced; + uint totalInstances; + + // For GL ES lack of baseInstance, we need to know this here... + GLAB transformsAB; + int transformSize; + + // To avoid changing the state of the VAO unnecessarily + uint lastTransformAB; + uint lastIDAB; + uint lastVBO; + uint lastIBO; + + void printStats() + { + PrintLn("* Indices Stats: "); + if(indexGLMB) indexGLMB.printStats(); + PrintLn("* Vertices Stats: "); + if(vertexGLMB) vertexGLMB.printStats(); + PrintLn(""); + } + + property bool ix32 { set { type = value ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; } } + + void init(GLIMTKMode mode, uint minAlloc) + { + drawMode = mode; + type = GL_UNSIGNED_SHORT; + // if(!minAlloc) minAlloc = 8; + if(!vertexGLMB) + vertexGLMB = {}; + if(!indexGLMB) + indexGLMB = {}; +#if (!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3) + if(minAlloc && !vao && glCaps_vao) + glGenVertexArrays(1, &vao); +#endif + if(minAlloc > commandsCount) + resize(minAlloc); + } + + void resize(uint size) + { + drawIDs = renew0 drawIDs uint[size]; + commands = renew0 commands GLDrawCommand[size]; + commandsAlloced = size; + idsAlloced = size; + idsAB.allocate(size * sizeof(uint), null, streamDraw); + if(glCaps_gpuCommands) + commandsB.allocate(size * sizeof(GLDrawCommand), null, streamDraw); + } + + void resizeCommands(uint size) + { + commands = renew0 commands GLDrawCommand[size]; + commandsAlloced = size; + if(glCaps_gpuCommands) + commandsB.allocate(size * sizeof(GLDrawCommand), null, streamDraw); + } + + void resizeIDs(uint size) + { + drawIDs = renew0 drawIDs uint[size]; + idsAlloced = size; + idsAB.allocate(size * sizeof(uint), null, streamDraw); + } + + void free() + { + idsAB.free(); + commandsB.free(); + + if(indexGLMB) indexGLMB.free(), delete indexGLMB; + if(vertexGLMB) vertexGLMB.free(), delete vertexGLMB; + delete drawIDs; + delete commands; +#if (!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3) + if(vao && glCaps_vao) + { + glDeleteVertexArrays(1, &vao); + vao = 0; + } +#endif + commandsAlloced = 0; + idsAlloced = 0; + + lastIDAB = 0; + lastIBO = 0; + lastVBO = 0; + lastTransformAB = 0; + } + + int allocateVbo(uint nVertices, uint vertexSize, const void *data) + { + uint size = nVertices * vertexSize; + BlockEntry block = size ? vertexGLMB.allocate(attributes, size) : 0; + int baseVertex = block ? block.start / vertexSize : -1; + if(data && baseVertex != -1) + vertexGLMB.ab.upload(block.start, size, data); + return baseVertex; + } + + void freeVbo(int baseVertex, uint vertexSize, uint count) + { +#ifdef _DEBUG + if(!vertexGLMB) + PrintLn("WARNING: null vertexGLMB calling freeVbo()"); +#endif + + if(baseVertex != -1) + vertexGLMB.freeBlock(BlockEntry { baseVertex * vertexSize, (baseVertex+count) * vertexSize-1 }); + } + + int allocateIx(uint nIndices, uint indexSize, const void *data) + { + uint size = nIndices * indexSize; + BlockEntry block = size ? indexGLMB.allocate(elements, size) : 0; + int baseIndex = block ? block.start / indexSize : -1; + if(data && baseIndex != -1) + ((GLEAB *)&indexGLMB.ab)->upload(block.start, size, data); + return baseIndex; + } + + void freeIx(int baseIndex, uint indexSize, uint count) + { + if(baseIndex != -1) + indexGLMB.freeBlock(BlockEntry { baseIndex * indexSize, (baseIndex+count) * indexSize-1 }); + } + + void addDrawCommand(uint indexCount, uint instanceCount, uint firstIndex, uint baseVertex, uint baseInstance) + { + addDrawCommandCustomID(indexCount, instanceCount, firstIndex, baseVertex, baseInstance, totalInstances); + } + + void addDrawCommandCustomID(uint indexCount, uint instanceCount, uint firstIndex, uint baseVertex, uint baseInstance, uint layer) + { + if(commandsCount >= commandsAlloced) + resizeCommands(commandsCount + Max(1, commandsCount / 2)); + if(totalInstances + instanceCount >= idsAlloced) + resizeIDs(totalInstances + Max(instanceCount, totalInstances / 2)); + + commands[commandsCount] = + { + count = indexCount, + instanceCount = instanceCount, + firstIndex = firstIndex, + baseVertex = baseVertex, + baseInstance = baseInstance + }; + commandsCount++; + FillBytesBy4(drawIDs + totalInstances, layer, instanceCount); + totalInstances += instanceCount; + } + + void prepare(int vertNCoords, int verticesStride) + { + idsAB.upload(0, totalInstances * sizeof(uint), drawIDs); + if(glCaps_gpuCommands) + commandsB.upload(0, commandsCount * sizeof(GLDrawCommand), commands); + +#if (!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3) + if(glCaps_vao) glBindVertexArray(vao); +#endif + + // TOCHECK: No attrib divisor support in ES 2 -- will it be needed? +#if (!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3) + // Draw IDs + if(glCaps_shaders && (!glCaps_vao || lastIDAB != idsAB.buffer)) + { + GLABBindBuffer(GL_ARRAY_BUFFER, idsAB.buffer); + glVertexAttribIPointer(drawIDAttribute, 1, GL_UNSIGNED_INT, sizeof(uint), 0); + glVertexAttribDivisor(drawIDAttribute, 1); + glEnableVertexAttribArray(drawIDAttribute); + lastIDAB = idsAB.buffer; + } +#endif + if(glCaps_shaders && (!glCaps_vao || lastVBO != vertexGLMB.ab.buffer)) + { + if(vertNCoords) + { + GLAB ab { vertexGLMB.ab.buffer }; + glEnableVertexAttribArray(GLBufferContents::vertex); + ab.use(vertex, vertNCoords, GL_FLOAT, verticesStride, null); + } + lastVBO = vertexGLMB.ab.buffer; + } + if(glCaps_vertexBuffer && (!glCaps_vao || lastIBO != indexGLMB.ab.buffer)) + { + GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexGLMB.ab.buffer); + lastIBO = indexGLMB.ab.buffer; + } + } + + void draw() + { + if(!commandsCount) return; + +#if (!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3) + if(glCaps_vao) glBindVertexArray(vao); +#endif + GLFlushMatrices(); + + // Then render: +#if defined(__UWP__) || ((defined(_GLES) || defined(_GLES2)) && !defined(_GLES3)) // ******* Basic Draw Elements ******* + { + int n; + uint ixSize = type == GL_UNSIGNED_INT ? 4 : 2; + for(n = 0; n < commandsCount; n++) + { + const GLDrawCommand *cmd = &commands[n]; + + //cmd->baseVertex + //cmd->baseInstance + + glDrawElements(drawMode, cmd->count, type, (void *)(uintptr)(cmd->firstIndex * ixSize)); + } + } +#else //if (defined(__ANDROID__) && !defined(__LUMIN__)) || defined(__UWP__) // ******* Instanced Draws with Base Vertex ******* + //This path that isn't taken here is the fallback for when MDEI is not available. TODO: proper condition + if(!glCaps_mdei) + { + int n; + uint ixSize = type == GL_UNSIGNED_INT ? 4 : 2; +#if defined(__ANDROID__) || defined(__UWP__) + uint transformSize = this.transformSize; +#endif + if(glCaps_shaders) + for(n = 0; n < commandsCount; n++) + { + const GLDrawCommand *cmd = &commands[n]; +#if defined(__ANDROID__) || defined(__UWP__) + // NOTE: glVertexAttribPointer might cause VAOs to be re-validated... Uniforms faster? + uint baseInstance = cmd->baseInstance; + GLABBindBuffer(GL_ARRAY_BUFFER, transformsAB.buffer); + glVertexAttribPointer(posOffsetAttribute, transformSize, GL_FLOAT, GL_FALSE, 0, (void *)(uintptr)(baseInstance * 3 * sizeof(float))); + GLABBindBuffer(GL_ARRAY_BUFFER, idsAB.buffer); + glVertexAttribIPointer(drawIDAttribute, 1, GL_UNSIGNED_INT, 0, (void *)(uintptr)(baseInstance * sizeof(uint))); + GLABBindBuffer(GL_ARRAY_BUFFER, 0); + + // OpenGL ES 3.2 has this + glDrawElementsInstancedBaseVertex( + drawMode, cmd->count, type, + (void *)(uintptr)(cmd->firstIndex * ixSize), + cmd->instanceCount, cmd->baseVertex); +#else + glDrawElementsInstancedBaseVertexBaseInstance( + drawMode, cmd->count, type, + (void *)(uintptr)(cmd->firstIndex * ixSize), + cmd->instanceCount, cmd->baseVertex, cmd->baseInstance); +#endif + } + #ifdef _DEBUG + CheckGLErrors(__FILE__,__LINE__); + #endif + } + else +//#else // ******* Indirect Multi Draw ******* + { + GLABBindBuffer(GL_DRAW_INDIRECT_BUFFER, glCaps_gpuCommands ? commandsB.buffer : 0); + + #ifdef _DEBUG + CheckGLErrors(__FILE__,__LINE__); + #endif + + glMultiDrawElementsIndirect( + drawMode, + type, + glCaps_gpuCommands ? 0 : commands, + commandsCount, 0); + + #ifdef _DEBUG + CheckGLErrors(__FILE__,__LINE__); + #endif + + GLABBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); + } +#endif +#if (!defined(_GLES) && !defined(_GLES2)) || defined(_GLES3) + if(glCaps_vao) glBindVertexArray(0); +#endif + } +}; + +public void GLMultisampling(bool value) +{ +#if !defined(_GLES) && !defined(_GLES2) && !defined(__UWP__) + (value ? glEnable : glDisable)(GL_MULTISAMPLE); +#endif +} + +#endif diff --git a/ecere/src/gfx/drivers/gl3/default.frag b/ecere/src/gfx/drivers/gl3/default.frag index 7d34066c4b..92f8c55a0f 100644 --- a/ecere/src/gfx/drivers/gl3/default.frag +++ b/ecere/src/gfx/drivers/gl3/default.frag @@ -1,12 +1,26 @@ #if GLSL_FLOAT_PRECISION precision highp float; #endif +#if TEXTURE_EXTERNAL +#extension GL_OES_EGL_image_external : enable +#endif + +#if MODERN_GLSL +#define varying in +#define texture2D texture +#define gl_FragColor fragColor +out vec4 fragColor; +#endif + +#if ALPHATEST_ON + uniform float alphaFuncValue; +#endif #if LIGHTING_ON #if PER_VERTEX_COLOR varying vec4 diffuseColor; varying vec3 ambientColor; - #else + #elif CONSTANT_COLOR uniform vec4 matDiffuse; uniform vec3 matAmbient; #endif @@ -22,12 +36,16 @@ precision highp float; #if MAT_SPECULAR +#if CONSTANT_COLOR // Materials uniform vec3 matSpecular; +#endif uniform float matPower; #endif +#if CONSTANT_COLOR uniform vec3 matEmissive; +#endif // Lights uniform vec3 lightsPos[NUM_LIGHTS]; @@ -64,6 +82,10 @@ precision highp float; #if TEXTURE_ON || NORMALS_MAPPING || SPECULAR_MAPPING || REFLECTION_MAP || CUBEMAP_ON + #if BLACKTINT + uniform vec3 blackTint; + #endif + #if SPECULAR_MAPPING uniform sampler2D specularTex; #endif @@ -76,8 +98,12 @@ precision highp float; #endif #if TEXTURE_ON + #if TEXTURE_EXTERNAL + uniform samplerExternalOES diffuseTex; + #else uniform sampler2D diffuseTex; #endif + #endif #if NORMALS_MAPPING uniform sampler2D bumpTex; @@ -159,12 +185,10 @@ void main(void) #else texCoord = fTexCoord; #endif -#endif - -#if NORMALS_MAPPING || TEXTURE_ON || SPECULAR_MAPPING || REFLECTION_MAP +#elif NORMALS_MAPPING || TEXTURE_ON || SPECULAR_MAPPING || REFLECTION_MAP vec2 texCoord; #if TEXTURE_MATRIX - texCoord = (vec4(fTexCoord, 0, 1) * texture_matrix).xy; + texCoord = (vec4(fTexCoord, 0.0, 1.0) * texture_matrix).xy; #else texCoord = fTexCoord; #endif @@ -174,9 +198,12 @@ void main(void) vec3 n; #endif -#if LIGHTING_ON && !PER_VERTEX_COLOR +#if LIGHTING_ON && !PER_VERTEX_COLOR && CONSTANT_COLOR vec4 diffuseColor = matDiffuse; vec3 ambientColor = matAmbient; +#elif !PER_VERTEX_COLOR + vec4 diffuseColor = vec4(1.0, 1.0, 1.0, 1.0); + vec3 ambientColor = vec3(1.0, 1.0, 1.0); #endif #if ENVIRONMENT_MAPPING || (LIGHTING_ON && !NON_LOCAL_VIEWER && MAT_SPECULAR) @@ -200,6 +227,7 @@ void main(void) #else n = normalize(tNormal); #endif + n.y = -n.y; #if NUM_LIGHTS > 0 && LIGHT0_ON && LIGHT0_POSITIONAL lights[0] = lightsPos[0] - nnEyeToSurface; @@ -293,9 +321,15 @@ void main(void) #endif c = vec4(min(vec3(1.0), - ambientColor * (ambient + globalAmbient) + diffuseColor.xyz * diffuse + matEmissive + ambientColor * (ambient + globalAmbient) + diffuseColor.xyz * diffuse + #if CONSTANT_COLOR + + matEmissive + #endif #if MAT_SPECULAR && !MAT_SEPARATE_SPECULAR - + matSpecular * specular + + specular + #if CONSTANT_COLOR + * matSpecular + #endif #endif ), diffuseColor.w); } @@ -313,7 +347,17 @@ void main(void) #elif SWIZZLE_RED texel = vec4(1,1,1,texel.r); #endif +#if ALPHATEST_ON + if(texel.a < alphaFuncValue) + discard; +#endif + +#if BLACKTINT + c = vec4(c.rgb * texel.rgb + blackTint.rgb * (vec3(1.0,1.0,1.0)-texel.rgb), c.a * texel.a); +#else c *= texel; +#endif + #elif CUBEMAP_ON vec4 texel; texel = textureCube(diffuseTex, texCoord); @@ -348,7 +392,11 @@ void main(void) #endif #if LIGHTING_ON && MAT_SPECULAR && MAT_SEPARATE_SPECULAR - c = min(vec4(1.0), vec4(vec3(c) + matSpecular * specular, 1.0)); + c = min(vec4(1.0), vec4(vec3(c) + specular + #if CONSTANT_COLOR + * matSpecular + #endif + , 1.0)); #endif #if FOG_ON diff --git a/ecere/src/gfx/drivers/gl3/default.vert b/ecere/src/gfx/drivers/gl3/default.vert index eec74dbadd..9b86db43c4 100644 --- a/ecere/src/gfx/drivers/gl3/default.vert +++ b/ecere/src/gfx/drivers/gl3/default.vert @@ -1,3 +1,8 @@ +#if MODERN_GLSL +#define attribute in +#define varying out +#endif + attribute vec3 vertex; uniform mat4 projection_matrix; @@ -19,7 +24,9 @@ uniform mat4 projection_matrix; attribute vec3 tangent2; uniform float nearPlane; - uniform mat3 normals_matrix; +#if NORMALS_INV_SCALE + uniform vec3 normals_inv_scale2; +#endif #if PER_VERTEX_COLOR varying vec4 diffuseColor; // w: opacity @@ -44,9 +51,8 @@ uniform mat4 projection_matrix; #if CUBEMAP_ON varying vec3 fTexCoord; -#endif - -#if TEXTURE_ON || NORMALS_MAPPING || SPECULAR_MAPPING || REFLECTION_MAP + attribute vec3 texCoord; +#elif TEXTURE_ON || NORMALS_MAPPING || SPECULAR_MAPPING || REFLECTION_MAP attribute vec2 texCoord; varying vec2 fTexCoord; #endif @@ -69,11 +75,33 @@ void main(void) ambientColor = color.xyz; #endif - tNormal = normals_matrix * normal; -#if NORMALS_MAPPING - tTangent1 = normals_matrix * tangent1; - tTangent2 = normals_matrix * tangent2; +#if MODELVIEW +#if NORMALS_INV_SCALE + mat3 normals_matrix = mat3( + normals_inv_scale2.x * modelview_matrix[0][0], normals_inv_scale2.x * modelview_matrix[0][1], normals_inv_scale2.x * modelview_matrix[0][2], + normals_inv_scale2.y * modelview_matrix[1][0], normals_inv_scale2.y * modelview_matrix[1][1], normals_inv_scale2.y * modelview_matrix[1][2], + normals_inv_scale2.z * modelview_matrix[2][0], normals_inv_scale2.z * modelview_matrix[2][1], normals_inv_scale2.z * modelview_matrix[2][2]); +#else + mat3 normals_matrix = mat3( + modelview_matrix[0][0], modelview_matrix[0][1], modelview_matrix[0][2], + modelview_matrix[1][0], modelview_matrix[1][1], modelview_matrix[1][2], + modelview_matrix[2][0], modelview_matrix[2][1], modelview_matrix[2][2]); #endif + + tNormal = normals_matrix * normal; + #if NORMALS_MAPPING + tTangent1 = normals_matrix * tangent1; + tTangent2 = normals_matrix * tangent2; + #endif +#else + tNormal = normal; + #if NORMALS_MAPPING + tTangent1 = tangent1; + tTangent2 = tangent2; + #endif + +#endif + #elif PER_VERTEX_COLOR fColor = matDiffuse * color; #endif diff --git a/ecere/src/gfx/drivers/gl3/defaultShader.ec b/ecere/src/gfx/drivers/gl3/defaultShader.ec index 13861170a4..c976f46d5b 100644 --- a/ecere/src/gfx/drivers/gl3/defaultShader.ec +++ b/ecere/src/gfx/drivers/gl3/defaultShader.ec @@ -1,7 +1,21 @@ import "shaders" +#if defined(__linux__) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__i386__) +asm(".symver log,log@GLIBC_2.2.5"); +#endif + #include "gl123es.h" +#define GL_CLAMP_TO_EDGE 0x812F + +#if defined _GLES1 +#define glClampFunction(version) (GL_CLAMP) +#elif defined(_GLES2) || defined(__UWP__) +#define glClampFunction(version) (GL_CLAMP_TO_EDGE) +#else +#define glClampFunction(version) (version >= 2 ? GL_CLAMP_TO_EDGE : GL_CLAMP) +#endif + namespace gfx::drivers; // ********** Default Shader ********** @@ -22,6 +36,7 @@ public: SwizzleMode swizzle:2; bool textureMatrix:1; bool texturing:1; + bool alphaTest:1; bool cubeMap:1; bool modelView:1; bool fog:1; @@ -32,6 +47,14 @@ public: bool reflectionMap:1; bool refraction:1; bool debugging:1; + bool constantColor:1; + bool normalsInvScale2:1; + bool externalTexture:1; + bool blackTint:1; + bool textureArray:1; + bool multiDraw:1; + bool transform3D:1; + bool squishFactor:1; }; public class CompiledDefaultShader : CompiledShader @@ -42,6 +65,7 @@ public: int uMVMatrix; int uTextureMatrix; int uNormalsMatrix; + int uNormalsInvScale2; int uFogDensity; int uFogColor; int uGlobalAmbient; @@ -68,6 +92,10 @@ public: int uRefractionETA; int uMatReflectivity; int uCubeMapMatrix; + int uAlphaFuncValue; + int uBlackTint; + + bool initialSetup; initialSetup = true; void registerUniforms(int program, DefaultShaderBits state) { @@ -113,6 +141,7 @@ public: uNearPlane = glGetUniformLocation(program, "nearPlane"); uNormalsMatrix = glGetUniformLocation(program, "normals_matrix"); + uNormalsInvScale2 = glGetUniformLocation(program, "normals_inv_scale2"); uGlobalAmbient = glGetUniformLocation(program, "globalAmbient"); uMatAmbient = glGetUniformLocation(program, "matAmbient"); uMatEmissive = glGetUniformLocation(program, "matEmissive"); @@ -122,11 +151,15 @@ public: uMatPower = glGetUniformLocation(program, "matPower"); } } + if(state.alphaTest) + uAlphaFuncValue = glGetUniformLocation(program, "alphaFuncValue"); if((state.specularMapping || state.normalsMapping || state.texturing || state.reflectionMap || state.cubeMap) && state.textureMatrix) uTextureMatrix = glGetUniformLocation(program, "texture_matrix"); if(state.texturing || state.cubeMap) uDiffuseTex = glGetUniformLocation(program, "diffuseTex"); + if(state.blackTint) + uBlackTint = glGetUniformLocation(program, "blackTint"); if(state.normalsMapping) uBumpTex = glGetUniformLocation(program, "bumpTex"); if(state.specularMapping) @@ -155,16 +188,20 @@ public: public class DefaultShader : Shader { +#if defined(__LUMIN__) + vertexShaderFile = "data/ecere/shaders/default.vert"; + fragmentShaderFile = "data/ecere/shaders/default.frag"; +#else vertexShaderFile = "<:ecere>shaders/default.vert"; fragmentShaderFile = "<:ecere>shaders/default.frag"; +#endif -public: float modelView[16]; float projection[16]; float matTexture[16]; float cubemap_matrix[9]; - float normalsMatrix[9]; + Vector3Df normalsInvScale2; float nearPlane; float globalAmbient[3]; @@ -192,7 +229,9 @@ public: float fogColor[3]; float color[4]; + Color blackTint; +public: DefaultShaderBits backLightState; backLightState = DefaultShaderBits { separateSpecular = true }; DefaultShader() @@ -240,13 +279,20 @@ public: //PrintLn("Compiling shader for state: ", state); #endif -#if defined(__EMSCRIPTEN__) +#if defined(__UWP__) + defs.concatf("#version 300 es\n"); + defs.concatf("#define GLSL_FLOAT_PRECISION 1\n"); + defs.concatf("#define MODERN_GLSL 1\n"); +#elif defined(_GLES2) || defined(_GLES3) defs.concatf("#version 100\n"); defs.concatf("#define GLSL_FLOAT_PRECISION 1\n"); + defs.concatf("#define MODERN_GLSL 0\n"); #else defs.concatf("#version 110\n"); defs.concatf("#define GLSL_FLOAT_PRECISION 0\n"); + defs.concatf("#define MODERN_GLSL 0\n"); #endif + defs.concatf("\n#define NUM_LIGHTS %d", 8); defs.concatf("\n#define MODELVIEW %d", state.modelView ? 1 : 0); defs.concatf("\n#define PER_VERTEX_COLOR %d", state.perVertexColor ? 1 : 0); @@ -260,15 +306,21 @@ public: defs.concatf("\n#define ENVIRONMENT_REFLECTION %d", state.reflection ? 1 : 0); defs.concatf("\n#define ENVIRONMENT_REFRACTION %d", state.refraction ? 1 : 0); defs.concatf("\n#define REFLECTION_MAP %d", state.reflectionMap ? 1 : 0); + defs.concatf("\n#define CONSTANT_COLOR %d", state.constantColor ? 1 : 0); defs.concatf("\n#define NORMALS_MAPPING %d", state.normalsMapping ? 1 : 0); defs.concatf("\n#define CUBEMAP_ON %d", state.cubeMap ? 1 : 0); defs.concatf("\n#define LIGHTING_SPECULAR_BLINN %d", state.blinnSpecular ? 1 : 0); + defs.concatf("\n#define ALPHATEST_ON %d", state.alphaTest ? 1 : 0); defs.concatf("\n#define TEXTURE_ON %d", state.texturing ? 1 : 0); defs.concatf("\n#define TEXTURE_MATRIX %d", state.textureMatrix ? 1 : 0); defs.concatf("\n#define SWIZZLE_ALPHA %d", state.swizzle == alpha ? 1 : 0); defs.concatf("\n#define SWIZZLE_RED %d", state.swizzle == red ? 1 : 0); defs.concatf("\n#define FOG_ON %d\n", state.fog ? 1 : 0); defs.concatf("\n#define DEBUGGING %d\n", state.debugging ? 1 : 0); + defs.concatf("\n#define NORMALS_INV_SCALE %d", state.normalsInvScale2 ? 1 : 0); + defs.concatf("\n#define TEXTURE_EXTERNAL %d", state.externalTexture ? 1 : 0); + defs.concatf("\n#define BLACKTINT %d", state.blackTint ? 1 : 0); + for(i = 0; i < 8; i++) { LightMode mode = (LightMode)((state.lightBits & (0x7 << (3*i))) >> (3*i)); @@ -315,78 +367,103 @@ public: } */ - glUniformMatrix4fv(shader.uPrjMatrix, 1, GL_FALSE, projection); + if(shader.initialSetup) + { + shader.initialSetup = false; + + if(state.texturing || state.cubeMap) glUniform1i(shader.uDiffuseTex, 0); + if(state.normalsMapping) glUniform1i(shader.uBumpTex, 1); + if(state.specularMapping) glUniform1i(shader.uSpecularTex, 2); + if(state.environmentMapping) glUniform1i(shader.uEnvTex, 3); + if(state.environmentMapping && state.reflectionMap) glUniform1i(shader.uReflectTex, 4); + if(state.alphaTest) glUniform1f(shader.uAlphaFuncValue, 0.5f); + } - if(state.modelView) + if(modifiedUniforms.matPrj) + { + if(state.lighting) + glUniform1f(shader.uNearPlane, nearPlane); + glUniformMatrix4fv(shader.uPrjMatrix, 1, GL_FALSE, projection); + } + + if(state.modelView && modifiedUniforms.matMV) glUniformMatrix4fv(shader.uMVMatrix, 1, GL_FALSE, modelView); if(state.lighting) { // Lights - int i; - for(i = 0; i < 8; i++) + if(modifiedUniforms.light) { - LightMode mode = (LightMode)((state.lightBits & (0x7 << (3*i))) >> (3*i)); - if(mode) + int i; + for(i = 0; i < 8; i++) { - if(mode == posSpot || mode == posSpotAtt) + LightMode mode = (LightMode)((state.lightBits & (0x7 << (3*i))) >> (3*i)); + if(mode) { - glUniform3fv(shader.uLightsSpotDir[i], 1, lSpotDir[i]); - glUniform1f(shader.uLightsSpotCutOffCos[i], lCutOffCos[i]); - glUniform1f(shader.uLightsSpotExp[i], lSpotExp[i]); + if(mode == posSpot || mode == posSpotAtt) + { + glUniform3fv(shader.uLightsSpotDir[i], 1, lSpotDir[i]); + glUniform1f(shader.uLightsSpotCutOffCos[i], lCutOffCos[i]); + glUniform1f(shader.uLightsSpotExp[i], lSpotExp[i]); + } + if(mode == posAtt || mode == posSpotAtt) + glUniform3fv(shader.uLightsAtt[i], 1, lAtt[i]); + if(state.specular) + glUniform3fv(shader.uLightsSpecular[i], 1, lSpecular[i]); + glUniform3fv(shader.uLightsPos[i], 1, lPosition[i]); + glUniform3fv(shader.uLightsDiffuse[i], 1, lDiffuse[i]); + glUniform3fv(shader.uLightsAmbient[i], 1, lAmbient[i]); } - if(mode == posAtt || mode == posSpotAtt) - glUniform3fv(shader.uLightsAtt[i], 1, lAtt[i]); - if(state.specular) - glUniform3fv(shader.uLightsSpecular[i], 1, lSpecular[i]); - glUniform3fv(shader.uLightsPos[i], 1, lPosition[i]); - glUniform3fv(shader.uLightsDiffuse[i], 1, lDiffuse[i]); - glUniform3fv(shader.uLightsAmbient[i], 1, lAmbient[i]); } + + glUniform3fv(shader.uGlobalAmbient, 1, globalAmbient); } - // Material - glUniform4fv(shader.uMatDiffuse, 1, diffuse); - glUniform3fv(shader.uMatAmbient, 1, ambient); - glUniform3fv(shader.uMatEmissive, 1, emissive); - if(state.specular) + if(modifiedUniforms.material) { - glUniform3fv(shader.uMatSpecular, 1, specular); - glUniform1f(shader.uMatPower, state.blinnSpecular ? power : power / 4.0f); + // Material + if(state.constantColor) + { + glUniform4fv(shader.uMatDiffuse, 1, diffuse); + glUniform3fv(shader.uMatAmbient, 1, ambient); + glUniform3fv(shader.uMatEmissive, 1, emissive); + if(state.specular) + glUniform3fv(shader.uMatSpecular, 1, specular); + } + if(state.specular) + glUniform1f(shader.uMatPower, state.blinnSpecular ? power : power / 4.0f); } - glUniform1f(shader.uNearPlane, nearPlane); - glUniform3fv(shader.uGlobalAmbient, 1, globalAmbient); - - glUniformMatrix3fv(shader.uNormalsMatrix, 1, GL_FALSE, normalsMatrix); - - if(state.normalsMapping) - glUniform1i(shader.uBumpTex, 1); - - if(state.specularMapping) - glUniform1i(shader.uSpecularTex, 2); + if(modifiedUniforms.matMV && state.normalsInvScale2) + glUniform3fv(shader.uNormalsInvScale2, 1, (float *)&normalsInvScale2); } else + { glUniform4fv(shader.uMatDiffuse, 1, color); + } + if(state.blackTint) + { + float blackTint[3] = { this.blackTint.r / 255.0f, this.blackTint.g / 255.0f, this.blackTint.b / 255.0f }; + glUniform3fv(shader.uBlackTint, 1, blackTint); + } if(state.environmentMapping) { - glUniform1i(shader.uEnvTex, 3); - glUniformMatrix3fv(shader.uCubeMapMatrix, 1, GL_FALSE, cubemap_matrix); - if(state.reflection) - glUniform1f(shader.uMatReflectivity, reflectivity); - if(state.refraction) - glUniform1f(shader.uRefractionETA, refractionETA); - if(state.reflectionMap) - glUniform1i(shader.uReflectTex, 4); + if(modifiedUniforms.matTex) + glUniformMatrix3fv(shader.uCubeMapMatrix, 1, GL_FALSE, cubemap_matrix); + + if(modifiedUniforms.material) + { + if(state.reflection) + glUniform1f(shader.uMatReflectivity, reflectivity); + if(state.refraction) + glUniform1f(shader.uRefractionETA, refractionETA); + } } - if(state.texturing || state.cubeMap) - glUniform1i(shader.uDiffuseTex, 0); - if((state.texturing || state.normalsMapping || state.specularMapping || state.reflectionMap || state.cubeMap) && state.textureMatrix) + if(state.textureMatrix && modifiedUniforms.matTex && (state.texturing || state.normalsMapping || state.specularMapping || state.reflectionMap || state.cubeMap)) glUniformMatrix4fv(shader.uTextureMatrix, 1, GL_FALSE, matTexture); - - if(state.fog) + if(modifiedUniforms.material && state.fog) { glUniform1f(shader.uFogDensity, fogDensity); glUniform3fv(shader.uFogColor, 1, fogColor); @@ -404,58 +481,68 @@ public: (float)-c[8], (float) c[9], (float) c[10] }; memcpy(cubemap_matrix, m, 9 * sizeof(float)); - uniformsModified = true; + modifiedUniforms.matTex = true; } - void updateMatrix(MatrixMode mode, Matrix matrix, bool isIdentity) + void updateMatrix(MatrixMode mode, float * matrix, bool isIdentity) { - float m[16] = - { - (float)matrix.m[0][0], (float)matrix.m[0][1], (float)matrix.m[0][2], (float)matrix.m[0][3], - (float)matrix.m[1][0], (float)matrix.m[1][1], (float)matrix.m[1][2], (float)matrix.m[1][3], - (float)matrix.m[2][0], (float)matrix.m[2][1], (float)matrix.m[2][2], (float)matrix.m[2][3], - (float)matrix.m[3][0], (float)matrix.m[3][1], (float)matrix.m[3][2], (float)matrix.m[3][3] - }; if(mode == projection) { - memcpy(projection, m, 16 * sizeof(float)); + memcpy(projection, matrix, 16 * sizeof(float)); nearPlane = (float)::nearPlane; - uniformsModified = true; + modifiedUniforms.matPrj = true; } else if(mode == modelView) { - Matrix t, inv; - double * i = inv.array; + if(!isIdentity) + memcpy(modelView, matrix, 16 * sizeof(float)); - memcpy(modelView, m, 16 * sizeof(float)); ((DefaultShaderBits)state).modelView = !isIdentity; //mvModified = true; //prjViewModified = true; - inv = matrix; - inv.Scale(1, -1, 1); - t.Transpose(inv); - inv.Inverse(t); + if(((DefaultShaderBits)state).lighting) { - float m[9] = + if(isIdentity) // Probably not needed? Checking modelView flag? { - (float)i[0],(float)i[1],(float)i[2], - (float)i[4],(float)i[5],(float)i[6], - (float)i[8],(float)i[9],(float)i[10] - }; - memcpy(normalsMatrix, m, 9 * sizeof(float)); + ((DefaultShaderBits)state).normalsInvScale2 = false; + } + else + { + Vector3Df s2; + /*if(scale != null) + s2 = { scale.x * scale.x, scale.y * scale.y, scale.z * scale.z }; + else*/ + { + Vector3Df x { matrix[0], matrix[1], matrix[ 2] }; + Vector3Df y { matrix[4], matrix[5], matrix[ 6] }; + Vector3Df z { matrix[8], matrix[9], matrix[10] }; + s2 = { x.x * x.x + x.y * x.y + x.z * x.z, + y.x * y.x + y.y * y.y + y.z * y.z, + z.x * z.x + z.y * z.y + z.z * z.z }; + } + normalsInvScale2 = { 1.0f / s2.x, 1.0f / s2.y, 1.0f / s2.z }; + if(fabs(s2.x-s2.y) * normalsInvScale2.x < 0.01 && fabs(s2.x-s2.z) * normalsInvScale2.x < 0.01) + { + ((DefaultShaderBits)state).normalsInvScale2 = false; + } + else + { + ((DefaultShaderBits)state).normalsInvScale2 = true; + } + } } - - uniformsModified = true; + modifiedUniforms.matMV = true; } else if(mode == texture) { - memcpy(matTexture, m, 16 * sizeof(float)); - ((DefaultShaderBits)state).textureMatrix = !isIdentity; - if(((DefaultShaderBits)state).texturing || ((DefaultShaderBits)state).normalsMapping || ((DefaultShaderBits)state).specularMapping || - ((DefaultShaderBits)state).reflectionMap || ((DefaultShaderBits)state).cubeMap) - uniformsModified = true; + DefaultShaderBits state = this.state; + if(!isIdentity) + memcpy(matTexture, matrix, 16 * sizeof(float)); + ((DefaultShaderBits)this.state).textureMatrix = !isIdentity; + if(state & { texturing = true, normalsMapping = true, specularMapping = true, reflectionMap = true, cubeMap = true }) + modifiedUniforms.matTex = true; } } @@ -463,13 +550,13 @@ public: { globalAmbient[0] = r, globalAmbient[1] = g, globalAmbient[2] = b; if(((DefaultShaderBits)state).lighting) - uniformsModified = true; + modifiedUniforms.light = true; } void setColor(float r, float g, float b, float a) { color[0] = r, color[1] = g, color[2] = b, color[3] = a; - uniformsModified = true; + modifiedUniforms.material = true; } void lighting(bool on) @@ -477,7 +564,7 @@ public: if(((DefaultShaderBits)state).lighting != on) { ((DefaultShaderBits)state).lighting = on; - uniformsModified = true; + modifiedUniforms.light = true; if(!on) { backLightState = state & @@ -503,7 +590,7 @@ public: { fogOn = on; ((DefaultShaderBits)state).fog = fogOn && fogDensity; - uniformsModified = true; + modifiedUniforms.material = true; } } @@ -514,7 +601,7 @@ public: fogDensity = density; ((DefaultShaderBits)state).fog = fogOn && fogDensity; if(fogOn) - uniformsModified = true; + modifiedUniforms.material = true; } } @@ -522,19 +609,37 @@ public: { fogColor[0] = r, fogColor[1] = g, fogColor[2] = b; if(fogOn) - uniformsModified = true; + modifiedUniforms.material = true; + } + + void useExternalTexture(bool on) + { + if(((DefaultShaderBits)state).externalTexture != on) + { + ((DefaultShaderBits)state).externalTexture = on; + modifiedUniforms.material = true; + } } void texturing(bool on) { - if(((DefaultShaderBits)state).texturing != on) + DefaultShaderBits state = this.state; + if(state.texturing != on) { - ((DefaultShaderBits)state).texturing = on; - if(!on) - state &= ~DefaultShaderBits { swizzle = (SwizzleMode)0x3 }; - if(!on && !((DefaultShaderBits)state).normalsMapping && !((DefaultShaderBits)state).specularMapping && !((DefaultShaderBits)state).reflectionMap && !((DefaultShaderBits)state).cubeMap) - state &= ~DefaultShaderBits { textureMatrix = true }; - uniformsModified = true; + DefaultShaderBits rmBits = 0; + if(on) + { + rmBits |= { cubeMap = true }; + state.texturing = true; + } + else + { + rmBits |= { swizzle = (SwizzleMode)0x3, alphaTest = true, texturing = true }; + if(!state.normalsMapping && !state.specularMapping && !state.reflectionMap && !state.cubeMap) + rmBits |= { textureMatrix = true }; + } + modifiedUniforms.material = true; + this.state = state & ~rmBits; } } @@ -543,26 +648,42 @@ public: if(((DefaultShaderBits)state).debugging != on) { ((DefaultShaderBits)state).debugging = on; - uniformsModified = true; + modifiedUniforms = { true, true, true, true, true }; } } void swizzle(SwizzleMode swizzle) { + DefaultShaderBits state = this.state; #ifdef _DEBUG - if(swizzle && !((DefaultShaderBits)state).texturing && !((DefaultShaderBits)state).cubeMap) + if(swizzle && !state.texturing && !state.cubeMap) printf("swizzle() with texturing off\n"); #endif - if(((DefaultShaderBits)state).swizzle != swizzle) + if(state.swizzle != swizzle) { - ((DefaultShaderBits)state).swizzle = swizzle; - if(((DefaultShaderBits)state).texturing || ((DefaultShaderBits)state).cubeMap) - uniformsModified = true; + ((DefaultShaderBits)this.state).swizzle = swizzle; + if(state.texturing || state.cubeMap) + modifiedUniforms.material = true; } } void setSimpleMaterial(ColorAlpha color, bool twoSided) { + DefaultShaderBits rmBits + { + perVertexColor = true; + normalsMapping = true; + environmentMapping = true; + reflectionMap = true; + specularMapping = true; + reflection = true; + alphaTest = true; + refraction = true; + texturing = true; + cubeMap = true; + specular = true; + twoSided = true; + }; float r = color.color.r / 255.0f; float g = color.color.g / 255.0f; float b = color.color.b / 255.0f; @@ -573,18 +694,8 @@ public: diffuse[0] = r, diffuse[1] = g, diffuse[2] = b, diffuse[3] = color.a / 255.0f; ambient[0] = r, ambient[1] = g, ambient[2] = b; emissive[0] = 0, emissive[1] = 0, emissive[2] = 0; - ((DefaultShaderBits)state).perVertexColor = false; - ((DefaultShaderBits)state).normalsMapping = false; - ((DefaultShaderBits)state).environmentMapping = false; - ((DefaultShaderBits)state).reflectionMap = false; - ((DefaultShaderBits)state).specularMapping = false; - ((DefaultShaderBits)state).reflection = false; - ((DefaultShaderBits)state).refraction = false; - ((DefaultShaderBits)state).texturing = false; - ((DefaultShaderBits)state).cubeMap = false; - ((DefaultShaderBits)state).specular = false; - ((DefaultShaderBits)state).twoSided = twoSided; - uniformsModified = true; + this.state = (this.state & ~rmBits) | DefaultShaderBits { twoSided = twoSided }; + modifiedUniforms.material = true; } void setPerVertexColor(bool perVertexColor) @@ -592,7 +703,20 @@ public: if(((DefaultShaderBits)state).perVertexColor != perVertexColor) { ((DefaultShaderBits)state).perVertexColor = perVertexColor; - uniformsModified = true; + modifiedUniforms.material = true; + } + } + + property Color blackTint + { + set + { + if(blackTint != value); + { + ((DefaultShaderBits)state).blackTint = value != 0; + blackTint = value; + modifiedUniforms.material = true; + } } } @@ -600,134 +724,128 @@ public: void setMaterial(Material material, MeshFeatures flags) { #if ENABLE_GL_SHADERS + __attribute__((unused)) int version = 2; + DefaultShaderBits state = (DefaultShaderBits)this.state; + state &= ~ + { + cubeMap = true, perVertexColor = true, separateSpecular = true, twoSided = true, textureMatrix = true, alphaTest = true, + environmentMapping = true, refraction = true, reflection = true, reflectionMap = true, normalsMapping = true, specularMapping = true + }; + if(material) { - ((DefaultShaderBits)state).specular = material.power && (material.specular.r || material.specular.g || material.specular.b); - ((DefaultShaderBits)state).perVertexColor = flags.colors; - ((DefaultShaderBits)state).separateSpecular = ((DefaultShaderBits)state).specular && material.flags.separateSpecular; - ((DefaultShaderBits)state).cubeMap = material && material.flags.cubeMap && material.baseMap; - ((DefaultShaderBits)state).twoSided = material.flags.doubleSided && !material.flags.singleSideLight; - ((DefaultShaderBits)state).lightBits = material.flags.noLighting ? 0 : lightBits; - ((DefaultShaderBits)state).lighting = (!material.flags.noLighting && lightBits) ? true : false; + MaterialFlags matFlags = material.flags; + bool tile = matFlags.tile; + float * c = (float *)&material.diffuse; + + state.perVertexColor = flags.colors; + state.cubeMap = matFlags.cubeMap && material.baseMap; + if(matFlags.cubeMap) state.texturing = false; + state.twoSided = matFlags.doubleSided && !matFlags.singleSideLight; + state.lightBits = matFlags.noLighting ? 0 : lightBits; + state.lighting = (!matFlags.noLighting && lightBits) ? true : false; color[0] = 1, color[1] = 1, color[2] = 1, color[3] = material.opacity; - diffuse[0] = material.diffuse.r, diffuse[1] = material.diffuse.g, diffuse[2] = material.diffuse.b, diffuse[3] = material.opacity; - ambient[0] = material.ambient.r, ambient[1] = material.ambient.g, ambient[2] = material.ambient.b; - specular[0] = material.specular.r, specular[1] = material.specular.g, specular[2] = material.specular.b; - emissive[0] = material.emissive.r, emissive[1] = material.emissive.g, emissive[2] = material.emissive.b; - power = material.power; - } - else - { - ((DefaultShaderBits)state).cubeMap = false; - ((DefaultShaderBits)state).perVertexColor = false; - ((DefaultShaderBits)state).separateSpecular = false; - ((DefaultShaderBits)state).twoSided = false; - ((DefaultShaderBits)state).textureMatrix = false; - } - if(material && material.bumpMap && flags.tangents) - { - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)material.bumpMap.driverData); - if(material.flags.tile) + if(matFlags.update) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + matFlags.constantColor = material.opacity || + (diffuse[0] != 1 || diffuse[1] != 1 || diffuse[2] != 1 || diffuse[3] != 1) || + (ambient[0] != 1 || ambient[1] != 1 || ambient[2] != 1) || + (state.specular && (specular[0] != 1 || specular[1] != 1 || specular[2] != 1)) || + (emissive[0] != 0 || emissive[1] != 0 || emissive[2] != 0); + matFlags.update = 0; } - else + state.constantColor = matFlags.constantColor; + if(state.constantColor) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + diffuse[0] = c[0], diffuse[1] = c[1], diffuse[2] = c[2], diffuse[3] = material.opacity; + ambient[0] = c[3], ambient[1] = c[4], ambient[2] = c[5]; + specular[0] = c[6], specular[1] = c[7], specular[2] = c[8]; + emissive[0] = c[9], emissive[1] = c[10], emissive[2] = c[11]; } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, material.bumpMap.mipMaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glActiveTexture(GL_TEXTURE0); - ((DefaultShaderBits)state).normalsMapping = true; - } - else - ((DefaultShaderBits)state).normalsMapping = false; - - if(material && material.envMap && (material.refractiveIndex || material.refractiveIndexContainer || material.reflectivity || material.reflectMap)) - { - CubeMap cube = material.envMap; - glActiveTexture(GL_TEXTURE3); - glBindTexture(GL_TEXTURE_CUBE_MAP, (GLuint)(uintptr)cube.driverData); - glActiveTexture(GL_TEXTURE0); - ((DefaultShaderBits)state).environmentMapping = true; + state.specular = material.power && state.constantColor && (material.specular.r || material.specular.g || material.specular.b); + state.separateSpecular = state.specular && matFlags.separateSpecular; + power = material.power; + state.alphaTest = matFlags.partlyTransparent; - if(material.refractiveIndex || material.refractiveIndexContainer) + // Bump mapping + if(material.bumpMap && flags.tangents) { - refractionETA = - (material.refractiveIndexContainer ? material.refractiveIndexContainer : 1.0) / - (material.refractiveIndex ? material.refractiveIndex : 1.0); - - ((DefaultShaderBits)state).refraction = true; + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)material.bumpMap.driverData); + if(matFlags.setupTextures) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tile ? GL_REPEAT : glClampFunction(version)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tile ? GL_REPEAT : glClampFunction(version)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, material.bumpMap.mipMaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + glActiveTexture(GL_TEXTURE0); + state.normalsMapping = true; } - if(material.reflectivity || material.reflectMap) + // Environment mapping + if(material.envMap && (material.refractiveIndex || material.refractiveIndexContainer || material.reflectivity || material.reflectMap)) { - reflectivity = material.reflectivity; - if(!reflectivity && material.reflectMap) reflectivity = 1.0; + CubeMap cube = material.envMap; + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_CUBE_MAP, (GLuint)(uintptr)cube.driverData); + glActiveTexture(GL_TEXTURE0); + state.environmentMapping = true; - if(material.reflectMap) + if(material.refractiveIndex || material.refractiveIndexContainer) { - glActiveTexture(GL_TEXTURE4); - glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)material.reflectMap.driverData); - if(material.flags.tile) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - else - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } + refractionETA = + (material.refractiveIndexContainer ? material.refractiveIndexContainer : 1.0) / + (material.refractiveIndex ? material.refractiveIndex : 1.0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, material.reflectMap.mipMaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glActiveTexture(GL_TEXTURE0); + state.refraction = true; + } - ((DefaultShaderBits)state).reflectionMap = true; + if(material.reflectivity || material.reflectMap) + { + reflectivity = material.reflectivity; + if(!reflectivity && material.reflectMap) reflectivity = 1.0; + + if(material.reflectMap) + { + glActiveTexture(GL_TEXTURE4); + glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)material.reflectMap.driverData); + if(matFlags.setupTextures) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tile ? GL_REPEAT : glClampFunction(version)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tile ? GL_REPEAT : glClampFunction(version)); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, material.reflectMap.mipMaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + glActiveTexture(GL_TEXTURE0); + + state.reflectionMap = true; + } + state.reflection = true; } - else - ((DefaultShaderBits)state).reflectionMap = false; - ((DefaultShaderBits)state).reflection = true; } - } - else - { - ((DefaultShaderBits)state).environmentMapping = false; - ((DefaultShaderBits)state).refraction = false; - ((DefaultShaderBits)state).reflection = false; - ((DefaultShaderBits)state).reflectionMap = false; - } - if(material && material.specularMap) - { - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)material.specularMap.driverData); - if(material.flags.tile) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - else + // Specular mapping + if(material.specularMap) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)material.specularMap.driverData); + if(matFlags.setupTextures) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tile ? GL_REPEAT : glClampFunction(version)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tile ? GL_REPEAT : glClampFunction(version)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, material.specularMap.mipMaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + glActiveTexture(GL_TEXTURE0); + state.specularMapping = true; } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, material.specularMap.mipMaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glActiveTexture(GL_TEXTURE0); - ((DefaultShaderBits)state).specularMapping = true; } - else - ((DefaultShaderBits)state).specularMapping = false; - - uniformsModified = true; + modifiedUniforms.material = true; + this.state = state; #endif } @@ -741,7 +859,7 @@ public: ((DefaultShaderBits)state).lighting = true; if(lightOn || (lightOn != (mode != off))) - uniformsModified = true; + modifiedUniforms.light = true; if(lightOn) { @@ -873,3 +991,19 @@ public: } DefaultShader defaultShader { }; + +static struct ColorsStruct +{ + ColorRGB diffuse; + ColorRGB ambient; + ColorRGB specular; + ColorRGB emissive; +}; + +static ColorsStruct defaultColors +{ + { 1,1,1 }, + { 1,1,1 }, + { 1,1,1 }, + { 0,0,0 } +}; diff --git a/ecere/src/gfx/drivers/gl3/egl.ec b/ecere/src/gfx/drivers/gl3/egl.ec index 48bbb35a20..612332c8f0 100644 --- a/ecere/src/gfx/drivers/gl3/egl.ec +++ b/ecere/src/gfx/drivers/gl3/egl.ec @@ -1,48 +1,139 @@ -#if defined(__ANDROID__) || defined(__ODROID__) +#if defined(__ANDROID__) || defined(__ODROID__) || defined(__UWP__) import "instance" +#define String String_ +#define Size Size_ +#define Alignment Alignment_ + #include +#undef String +#undef Size +#undef Alignment + +#if defined(__LUMIN__) +#define get _get +#define set _set +#include +#include +#undef get +#undef set + +#define bool _bool +#define false _false +#define true _true +#include +#undef bool +#undef false +#undef true + +#endif + EGLDisplay eglDisplay; EGLSurface eglSurface; EGLContext eglContext; int eglWidth, eglHeight; +#if defined(__LUMIN__) +default void (* _ptrc_glMultiDrawElementsIndirect)(int, GLenum, const void *, GLsizei, GLsizei); +default void (* _ptrc_glMapBuffer)(int, GLenum, const void *, GLsizei, GLsizei); +MLHandle graphics_client = ML_INVALID_HANDLE; +#endif + #if defined(__ANDROID__) -bool egl_init_display(ANativeWindow* window) +bool egl_init_display(struct ANativeWindow * window) #else bool egl_init_display(uint window) #endif { - const EGLint attribs[] = + EGLint attribs[] = { +#if defined(__LUMIN__) + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 24, + EGL_STENCIL_SIZE, 8, +#else EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, - EGL_DEPTH_SIZE, 16, //24, + EGL_DEPTH_SIZE, 32, /*EGL_SAMPLE_BUFFERS, 1, EGL_SAMPLES, 0, //2,*/ +#endif EGL_NONE }; EGLint w, h, format; EGLint numConfigs; EGLConfig config; - EGLSurface surface; + EGLSurface surface = EGL_NO_SURFACE; EGLContext context; + EGLint ctxattr[] = + { +#if defined(__LUMIN__) + EGL_CONTEXT_MAJOR_VERSION_KHR, 4, + EGL_CONTEXT_MINOR_VERSION_KHR, 5, +#elif defined(_GLES3) + EGL_CONTEXT_CLIENT_VERSION, 3, +#elif defined(_GLES2) + EGL_CONTEXT_CLIENT_VERSION, 2, +#elif defined(_GLES) + EGL_CONTEXT_CLIENT_VERSION, 1, +#endif + EGL_NONE + }; EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - eglInitialize(display, 0, 0); +#if defined(__LUMIN__) + EGLint major = 4; + EGLint minor = 0; +#else + EGLint major = 3; //0; + EGLint minor = 2; //0; +#endif + eglInitialize(display, &major, &minor); +#if defined(__LUMIN__) + eglBindAPI(EGL_OPENGL_API); +#endif eglChooseConfig(display, attribs, &config, 1, &numConfigs); + if(numConfigs == 0) + { +#ifdef _DEBUG + PrintLn("EGL: No 32 bit depth buffer support"); +#endif + attribs[9] = 24; + eglChooseConfig(display, attribs, &config, 1, &numConfigs); + if(numConfigs == 0) + { +#ifdef _DEBUG + PrintLn("EGL: No 24 bit depth buffer support either :("); +#endif + attribs[9] = 16; + eglChooseConfig(display, attribs, &config, 1, &numConfigs); + } + } +#if defined(__LUMIN__) + eglGetConfigAttrib(display, config, EGL_TRUE, &format); +#else eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format); +#endif +#if !defined(__LUMIN__) surface = eglCreateWindowSurface(display, config, window, null); - context = eglCreateContext(display, config, null, null); +#endif + context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctxattr); if(!eglMakeCurrent(display, surface, surface, context)) return false; +#if defined(__LUMIN__) + _ptrc_glMultiDrawElementsIndirect = (void*)eglGetProcAddress("glMultiDrawElementsIndirect"); + _ptrc_glMapBuffer = (void*)eglGetProcAddress("glMapBuffer"); +#endif eglQuerySurface(display, surface, EGL_WIDTH, &w); eglQuerySurface(display, surface, EGL_HEIGHT, &h); @@ -62,8 +153,10 @@ void egl_term_display() eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if(eglContext != EGL_NO_CONTEXT) eglDestroyContext(eglDisplay, eglContext); +#if !defined(__LUMIN__) if(eglSurface != EGL_NO_SURFACE) eglDestroySurface(eglDisplay, eglSurface); +#endif eglTerminate(eglDisplay); } eglDisplay = EGL_NO_DISPLAY; diff --git a/ecere/src/gfx/drivers/gl3/gl123es.h b/ecere/src/gfx/drivers/gl3/gl123es.h index 57458fd440..06a027d8e8 100644 --- a/ecere/src/gfx/drivers/gl3/gl123es.h +++ b/ecere/src/gfx/drivers/gl3/gl123es.h @@ -1,15 +1,59 @@ -#if defined(__EMSCRIPTEN__) +#if defined(__WIN32__) + #define GetObject + #define MessageBox _MessageBox + #define Sleep _Sleep + #define Polygon _Polygon + #define Size Size_ + #define String String_ + #define Alignment Alignment_ + + #define WIN32_LEAN_AND_MEAN +#endif + +#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) // Moving Android to GLES 2 #if !defined(_GLES2) #define _GLES2 #endif -#elif defined(__ANDROID__) || defined(__ODROID__) +#elif /*defined(__ANDROID__) || */defined(__ODROID__) #ifndef _GLES #define _GLES #endif #endif -#if defined(_GLES2) +#if defined(_GLES2) || defined(_GLES3) +#if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__UWP__) + +#if defined(__LUMIN__) + #include + + #define glMultiDrawElementsIndirect _ptrc_glMultiDrawElementsIndirect + extern void (* _ptrc_glMultiDrawElementsIndirect)(GLenum, GLenum, const void *, GLsizei, GLsizei); + + #define glMapBuffer _ptrc_glMapBuffer + extern void * (* _ptrc_glMapBuffer)(GLenum, GLenum); + + #define GL_FILL 0x1B02 + #define GL_LINE 0x1B01 + +#else + +#if defined(__UWP__) + #define GL_GLEXT_PROTOTYPES +#endif + +#if defined(_GLES3) + //#define GL_GLEXT_PROTOTYPES + + #include + #include +#else #include +#endif +#endif + +#else + #include +#endif #elif defined(_GLES) /* #define uint _uint @@ -47,6 +91,17 @@ #include #endif + +#if defined(__WIN32__) + #undef Polygon + #undef MessageBox + #undef Sleep + #undef GetObject + #undef Size + #undef String + #undef Alignment +#endif + #ifdef _GLES // Frame Buffer Extensions #define GL_FRAMEBUFFER GL_FRAMEBUFFER_OES @@ -84,7 +139,12 @@ #define GL_BGRA_EXT 0x80E1 #endif -// Compiled In Capabilities +// TODO: Compatibility without sRGB support +#ifndef GL_SRGB8_ALPHA8 + #define GL_SRGB8_ALPHA8 0x8C43 +#endif + +/* Clang complains about undefined behavior? #define ENABLE_GL_SHADERS (!defined(_GLES)) #define ENABLE_GL_FFP (!defined(_GLES2)) #define ENABLE_GL_POINTER (!defined(__EMSCRIPTEN__)) @@ -95,6 +155,56 @@ #define ENABLE_GL_SELECT (!defined(_GLES) && !defined(_GLES2)) #define ENABLE_GL_VAO (!defined(_GLES) && !defined(_GLES2)) #define ENABLE_GL_COLORMAT (ENABLE_GL_FFP && !defined(_GLES)) +*/ + +// Compiled In Capabilities +#if defined(__LUMIN__) + #define ENABLE_GL_LEGACY 0 + #define ENABLE_GL_INTDBL 0 + #define ENABLE_GL_MAPBUF 0 // TODO: Lumin in theory supports full GL so all of these could be 1 (testing required) + #define ENABLE_GL_SELECT 0 + #define ENABLE_GL_VAO 1 +#elif defined(_GLES3) + #define ENABLE_GL_LEGACY 0 + #define ENABLE_GL_INTDBL 0 + #define ENABLE_GL_MAPBUF (!defined(__EMSCRIPTEN__)) // WebGL 2 still doesn't support MapBuffer + #define ENABLE_GL_SELECT 0 + #define ENABLE_GL_VAO 1 +#elif !defined(_GLES) && !defined(_GLES2) + #define ENABLE_GL_LEGACY 1 + #define ENABLE_GL_INTDBL 1 + #define ENABLE_GL_MAPBUF 1 + #define ENABLE_GL_SELECT 1 + #define ENABLE_GL_VAO 1 +#else + #define ENABLE_GL_LEGACY 0 + #define ENABLE_GL_INTDBL 0 + #define ENABLE_GL_MAPBUF 0 + #define ENABLE_GL_SELECT 0 + #define ENABLE_GL_VAO 0 +#endif + +#if defined(_GLES2) || defined(_GLES3) + #define ENABLE_GL_FFP 0 +#else + #define ENABLE_GL_FFP 1 +#endif + +#if defined(_GLES) + #define ENABLE_GL_SHADERS 0 + #define ENABLE_GL_COLORMAT 0 +#else + #define ENABLE_GL_SHADERS 1 + #define ENABLE_GL_COLORMAT ENABLE_GL_FFP +#endif + +#if defined(__EMSCRIPTEN__) + #define ENABLE_GL_POINTER 0 + #define ENABLE_GL_FBO 0 +#else + #define ENABLE_GL_POINTER 1 + #define ENABLE_GL_FBO 1 +#endif #if ENABLE_GL_SHADERS && ENABLE_GL_FFP #define GLEnableClientState (glCaps_shaders ? glEnableVertexAttribArray : glEnableClientState) @@ -258,6 +368,14 @@ #define GLColorMaterial(a,b) #endif +#if defined(_GLES2) + #define GLClearDepth(a) glClearDepthf(a) + #define GLDepthRange(a,b) glDepthRangef(a,b) +#else + #define GLClearDepth(a) if(glClearDepth) glClearDepth(a) + #define GLDepthRange(a,b) glDepthRange(a,b) +#endif + #ifdef _GLES #define GLLightModeli glsupLightModeli #else @@ -280,14 +398,31 @@ glCaps_vao = glCaps.vao; \ glCaps_compatible = glCaps.compatible; \ glCaps_select = glCaps.select; \ - glCaps_vertexPointer = glCaps.vertexPointer + glCaps_vertexPointer = glCaps.vertexPointer; \ + glCaps_gpuCommands = glCaps.gpuCommands; \ + glCaps_mdei = glCaps.mdei +extern int glVersion; extern GLCapabilities glCaps; extern bool glCaps_nonPow2Textures, glCaps_vertexBuffer, glCaps_quads, glCaps_intAndDouble, glCaps_legacyFormats, glCaps_compatible, glCaps_vertexPointer; extern bool glCaps_shaders, glCaps_fixedFunction, glCaps_immediate, glCaps_legacy, glCaps_pointSize, glCaps_frameBuffer, glCaps_vao, glCaps_select; +extern bool glCaps_gpuCommands; +extern bool glCaps_mdei; #if ENABLE_GL_INTDBL #define GL_INDEX_INT GL_UNSIGNED_INT #else #define GL_INDEX_INT GL_UNSIGNED_SHORT #endif + +#if defined(__LUMIN__) +#define bool _bool +#define false _false +#define true _true + +#include + +#undef bool +#undef false +#undef true +#endif diff --git a/ecere/src/gfx/drivers/gl3/glab.ec b/ecere/src/gfx/drivers/gl3/glab.ec index 09fd7576ce..d3638004c2 100644 --- a/ecere/src/gfx/drivers/gl3/glab.ec +++ b/ecere/src/gfx/drivers/gl3/glab.ec @@ -17,14 +17,23 @@ public void GLABBindBuffer(int target, uint buffer) glabCurArrayBuffer = buffer; else if(target == GL_ELEMENT_ARRAY_BUFFER) glabCurElementBuffer = buffer; +// NOTE: Actually ES 3.1 is required, separate define? +#if !defined(_GLES) && !defined(_GLES2) && !defined(_GLES3) + else if(target == GL_DRAW_INDIRECT_BUFFER) + glabCurDrawIndirectBuffer = buffer; +#endif } } +#if !defined(_GLES) && !defined(_GLES2) && !defined(_GLES3) +uint glabCurDrawIndirectBuffer; +#endif + public enum GLBufferContents { vertex, normal, texCoord, color, tangent1, tangent2, lightVector }; public enum GLBufferUsage { staticDraw, dynamicDraw, streamDraw }; -static GLint bufferUsages[] = { GL_DYNAMIC_DRAW, GL_STATIC_DRAW, 0x88E0 /*GL_STREAM_DRAW*/ }; +static GLint bufferUsages[] = { GL_STATIC_DRAW, GL_DYNAMIC_DRAW, 0x88E0 /*GL_STREAM_DRAW*/ }; public define noAB = GLAB { 0 }; @@ -39,38 +48,406 @@ void glabTerminate() delete shortVPBuffer; } -public struct GLAB +/////////////// +// FreeBlocks +public class BlockEntry : uint64 +{ +public: + uint start:32:32, end:32:0; +}; + +public class FreeBlockMap : Array +{ + uint totalSize; + + void insert(BlockEntry * after, BlockEntry block) + { + int i = after ? after - array : -1; + if(count + 1 > minAllocSize) + minAllocSize = Max(count, 16) + count / 2; + Insert(i == -1 ? null : (IteratorPointer)(array + i), block); + } + + void remove(BlockEntry * ptr) + { + Remove((IteratorPointer)ptr); + if(minAllocSize - count > count / 2) + minAllocSize = count + count / 2; + } + + BlockEntry * find(uint start) + { + BlockEntry * array = this.array; + int lo = 0, hi = (int)count - 1; + BlockEntry cmp { start }; + if(hi >= lo) + { + int ix = (lo + hi) / 2; + do + { + BlockEntry * ptr = array + ix; + BlockEntry block = *ptr; + bool last = hi == lo; + + if(block <= cmp) + { + if(ix == count-1 || array[ix+1].start > start) + return ptr; + else + { + lo = ix+1; + ix = (lo + hi)/2; + } + } + else + { + hi = ix-1; + ix = (lo + hi)/2; + } + if(last || lo > hi) break; + } while(true); + } + +#if 0 // def _DEBUG + { + int i; + for(i = 0; i < count; i++) + { + BlockEntry * b = &array[i]; + if(b->end < start) + { + PrintLn("Bad bug!"); + return find(start); + } + } + } +#endif + return null; + } + + void addFreeBlock(uint start, uint size) + { + BlockEntry * prevBlock = start ? find(start-1) : null; + BlockEntry * nextBlock = prevBlock && (prevBlock-array) < count-1 ? prevBlock + 1 : (count ? array : null); + + // Try to merge with previous block + if(prevBlock && prevBlock->end + 1 == start) + { + prevBlock->end = prevBlock->end + size; // FIXME: #1200 + + // Try to merge with next block as well + if(nextBlock && nextBlock->start == prevBlock->end + 1) + { + prevBlock->end = nextBlock->end; + remove(nextBlock); + } + } + // Try to merge with next block + else if(nextBlock && nextBlock->start == start + size) + nextBlock->start = start; + // This free block is not connected to any other block + else + insert(prevBlock, { start, start + size - 1 }); +#if 0 //def _DEBUG + checkConsistency(); +#endif + } + +public: + BlockEntry allocate(GLBType type, uint size) + { + uint count = this.count; + BlockEntry * array = this.array; + uint ix; + uint endAvailable = 0; + uint added; + int chosen = -1; + uint chosenAvailable = MAXDWORD; + // if(size == 1) PrintLn("single byte block?"); + + if(!size) return 0; // 0 size did not properly result in 0 result? + + for(ix = 0; ix < count; ix++) + { + BlockEntry * block = array + ix; + endAvailable = block->end - block->start + 1; + if(endAvailable >= size) + { + if(endAvailable < chosenAvailable) + { + chosenAvailable = endAvailable; + chosen = ix; + if(chosenAvailable == size) + break; + } + } + } + if(chosen != -1) + { + BlockEntry * block = array + chosen; + uint start = block->start; + if(block->end - block->start + 1 == size) + remove(block); + else + block->start = block->start + size; + +#if 0 //def _DEBUG + checkConsistency(); +#endif + return { start, start + size - 1 }; + } + + if(count && array[count-1].end != totalSize-1) + endAvailable = 0; + + if((added = onExpand(type, size - endAvailable))) + { + BlockEntry * block = count ? array + count - 1 : null; + uint start; + if(endAvailable) + block->end = block->end + added; // FIXME: #1200 + else + { + addFreeBlock(totalSize - added, added); + block = this.array + this.count - 1; + } + + start = block->start; + if(block->end - block->start + 1 == size) + remove(block); + else + block->start = block->start + size; + +#if 0 //def _DEBUG + checkConsistency(); +#endif + + return { start, start + size - 1 }; + } +#if 0 //def _DEBUG + checkConsistency(); +#endif + return 0; + } + +#ifdef _DEBUG + void checkConsistency() + { + int i; + for(i = 1; i < count; i++) + { + BlockEntry * l = &array[i-1]; + BlockEntry * b = &array[i]; + if(b->start <= l->end) + PrintLn("bad block order!"); + if(b->end >= totalSize) + PrintLn("bad block!"); + if(b->end <= b->start) + PrintLn("bad block!"); + } + } +#endif + + void freeBlock(BlockEntry block) + { +#if 0 //def _DEBUG + int i; + if(block.start > totalSize || block.end >= totalSize) + PrintLn("Start or end beyond size!"); + for(i = 0; i < count; i++) + { + BlockEntry * b = &array[i]; + if(block.start <= b->end && block.end >= b->start) + PrintLn("Overlapping with free block already here!"); + } +#endif + addFreeBlock(block.start, block.end - block.start + 1); + +#if 0 //def _DEBUG + checkConsistency(); +#endif + } + + virtual uint onExpand(GLBType type, uint required) { return 0; } +} + +/////////////// + +public class GLMB : FreeBlockMap +{ +public: + GLB ab; + bool keepSameBufferID; keepSameBufferID = true; + + void printStats() + { + int i; + uint totalFree = 0; + uint maxFree = 0, minFree = MAXDWORD; + PrintLn("GLMB Stats (", ab.buffer, ")"); + PrintLn("Total Size: ", totalSize / 1048576.0, " mb"); + PrintLn("Free Blocks: ", count); + for(i = 0; i < count; i++) + { + uint size = this[i].end - this[i].start + 1; + if(size < minFree) minFree = size; + if(size > maxFree) maxFree = size; + totalFree += size; + } + if(minFree != MAXDWORD) + { + PrintLn("Total Free: ", totalFree / 1048576.0, " mb"); + PrintLn("Smallest free block: ", minFree / 1048576.0, " mb"); + PrintLn("Largest free block: ", maxFree / 1048576.0, " mb"); + } + } + + uint onExpand(GLBType type, uint extraNeeded) + { + uint oldSize = totalSize; + uint newSize = (uint)Min((uint64)MAXDWORD, (uint64)totalSize + Max((uint64)extraNeeded, (uint64)totalSize / 2)); + if(newSize >= (uint64)oldSize + extraNeeded && ab.resize(type, totalSize, newSize, staticDraw, keepSameBufferID)) + { + uint spaceAdded = newSize - totalSize; + totalSize = newSize; + return spaceAdded; + } + else + PrintLn("WARNING: Failed to expand GLMB"); + return 0; + } + + void free() + { + Free(); + ab.free(); + totalSize = 0; + } + + ~GLMB() + { +#ifdef _DEBUG + if(count) + PrintLn("Warning: GLMB destructed without calling free()"); +#endif + free(); + } +}; + +#ifdef _DEBUG +#define GLSTATS +#endif + +public enum GLBType { elements, attributes, commands }; + +public struct GLB { uint buffer; - void allocate(uint size, void * data, GLBufferUsage usage) + bool resize(GLBType type, uint oldSize, uint newSize, GLBufferUsage usage, bool keepSameBufferID) + { + bool result = false; + + // TODO: Update buffers and defrag instead of doing 2 copy? + if(!oldSize) + result = _allocate(type, newSize, null, usage); + else if(keepSameBufferID) + { + // 2 copies (!) needed to preserve buffer ID + GLB tmp { }; + if(tmp._allocate(type, newSize, null, usage)) + { + tmp.copy(this, 0, 0, oldSize); + if(_allocate(type, newSize, null, usage)) + { + copy(tmp, 0, 0, oldSize); + result = true; + } + tmp.free(); + } + } + else + { + GLB tmp { }; + if(tmp._allocate(type, newSize, null, usage)) + { + tmp.copy(this, 0, 0, oldSize); + free(); + buffer = tmp.buffer; + + result = true; + } + } + return result; + } + + void copy(GLB src, uint srcStart, uint dstStart, uint size) + { + // TODO: Additional version check for full GL? +#if defined(__LUMIN__) || defined(_GLES3) || (!defined(__ANDROID__) && !defined(__EMSCRIPTEN__)) // TODO: + if(glCaps_vertexBuffer) + { + glBindBuffer(GL_COPY_READ_BUFFER, src.buffer); + glBindBuffer(GL_COPY_WRITE_BUFFER, buffer); + glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, srcStart, dstStart, size); + } +#endif + } + + bool _allocate(GLBType type, uint size, const void * data, GLBufferUsage usage) { if(this != null) { if(glCaps_vertexBuffer) { + int glBufferType = +#if !defined(_GLES) && !defined(_GLES2) && !defined(_GLES3) + type == commands ? GL_DRAW_INDIRECT_BUFFER : +#endif + type == elements ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER; + if(!buffer) glGenBuffers(1, &buffer); - if(glabCurArrayBuffer != buffer) - GLABBindBuffer(GL_ARRAY_BUFFER, buffer); - glBufferData(GL_ARRAY_BUFFER, size, data, bufferUsages[usage]); + + GLABBindBuffer(glBufferType, buffer); +#ifdef GLSTATS + GLStats::allocBuffer(buffer, size); +#endif + if(size) + glBufferData(glBufferType, size, data, bufferUsages[usage]); } else buffer = 1; + return true; } + return false; } - void upload(uint offset, uint size, void * data) + void _upload(GLBType type, uint offset, uint size, const void * data) { if(this != null && glCaps_vertexBuffer) { - if(glabCurArrayBuffer != buffer) - GLABBindBuffer(GL_ARRAY_BUFFER, buffer); - glBufferSubData(GL_ARRAY_BUFFER, offset, size, data); + int glBufferType = +#if !defined(_GLES) && !defined(_GLES2) && !defined(_GLES3) + type == commands ? GL_DRAW_INDIRECT_BUFFER : +#endif + type == elements ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER; + GLABBindBuffer(glBufferType, buffer); + glBufferSubData(glBufferType, offset, size, data); } } - void ::deleteBuffers(int count, GLAB * buffers) + bool allocate(uint size, const void * data, GLBufferUsage usage) + { + return _allocate(attributes, size, data, usage); + } + + void upload(uint offset, uint size, const void * data) + { + _upload(attributes, offset, size, data); + } + + void ::deleteBuffers(int count, GLB * buffers) { if(glCaps_vertexBuffer) { @@ -84,10 +461,19 @@ public struct GLAB GLABBindBuffer(GL_ARRAY_BUFFER, 0); else if(buffer == glabCurElementBuffer) GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +#if !defined(_GLES) && !defined(_GLES2) && !defined(_GLES3) + else if(buffer == glabCurDrawIndirectBuffer) + GLABBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); +#endif } } if(count && buffers[0].buffer) + { glDeleteBuffers(count, (GLuint *)buffers); +#ifdef GLSTATS + GLStats::freeBuffers(count, (uint *)buffers); +#endif + } } } @@ -100,14 +486,17 @@ public struct GLAB buffer = 0; } } +}; - void use(GLBufferContents contents, int n, int type, uint stride, void * pointer) +public struct GLAB : GLB +{ + void use(GLBufferContents contents, int n, int type, uint stride, const void * pointer) { if(glabCurArrayBuffer != ((this != null) ? buffer : 0) && glCaps_vertexBuffer) GLABBindBuffer(GL_ARRAY_BUFFER, ((this != null) ? buffer : 0)); #if ENABLE_GL_SHADERS - if(glCaps_shaders) - glVertexAttribPointer(contents, n, type, GL_FALSE, stride, pointer); + if(glCaps_shaders) // TODO: Review control over normalization? + glVertexAttribPointer(contents, n, type, type == GL_UNSIGNED_BYTE ? GL_TRUE : GL_FALSE, stride, pointer); #endif #if ENABLE_GL_FFP @@ -123,7 +512,7 @@ public struct GLAB #endif } - void useVertTrans(uint count, int n, int type, uint stride, void * pointer) + void useVertTrans(uint count, int n, int type, uint stride, const void * pointer) { if(!glCaps_intAndDouble) { @@ -169,67 +558,425 @@ uint glabCurElementBuffer; public define noEAB = GLEAB { 0 }; -public struct GLEAB +public struct GLCAB : GLB { - uint buffer; + bool allocate(uint size, const void * data, GLBufferUsage usage) + { + return _allocate(commands, size, data, usage); + } - void allocate(uint size, void * data, GLBufferUsage usage) + void upload(uint offset, uint size, const void * data) { - if(this != null) - { - if(glCaps_vertexBuffer) - { - if(!buffer) - glGenBuffers(1, &buffer); + _upload(commands, offset, size, data); + } +}; - if(glCaps_vertexBuffer && glabCurElementBuffer != buffer) - GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); - if(size) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, bufferUsages[usage]); - else - ; - } - else - buffer = 1; - } +public struct GLEAB : GLB +{ + bool allocate(uint size, const void * data, GLBufferUsage usage) + { + return _allocate(elements, size, data, usage); } - void upload(uint offset, uint size, void * data) + void upload(uint offset, uint size, const void * data) { - if(this != null && glCaps_vertexBuffer) - { - if(glabCurArrayBuffer != buffer) - GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); - glBufferSubData(buffer, offset, size, data); - } + _upload(elements, offset, size, data); } - void free() + void draw(int primType, int count, int type, const void * indices) { - if(this != null && buffer) + if(count && (glCaps_vertexBuffer +#if ENABLE_GL_POINTER + || (glCaps_vertexPointer && !buffer && indices) +#endif + )) { - if(glCaps_vertexBuffer) - GLAB::deleteBuffers(1, (GLAB *)this); - buffer = 0; + +#if !defined(__EMSCRIPTEN__) + if(glCaps_vertexBuffer && glabCurElementBuffer != ((this != null) ? buffer : 0)) +#endif + GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ((this != null) ? buffer : 0)); + if(!glCaps_intAndDouble) + type = GL_UNSIGNED_SHORT; + + GLFlushMatrices(); + + //if(!buffer || buffer) // TOCHECK: Why are we coming here with a 0 buffer? + glDrawElements(primType, count, type, indices); } } - void draw(int primType, int count, int type, void * indices) + void draw2(int primType, int count, int type, const void * indices, uint baseVertex) { - if(glCaps_vertexBuffer + if(count && (glCaps_vertexBuffer #if ENABLE_GL_POINTER || (glCaps_vertexPointer && !buffer && indices) #endif - ) + )) { - if(glCaps_vertexBuffer && glabCurElementBuffer != ((this != null) ? buffer : 0)) +#if !defined(__EMSCRIPTEN__) + if(glCaps_vertexBuffer) +#endif + if(glabCurElementBuffer != ((this != null) ? buffer : 0)) GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ((this != null) ? buffer : 0)); if(!glCaps_intAndDouble) type = GL_UNSIGNED_SHORT; GLFlushMatrices(); - glDrawElements(primType, count, type, indices); + //if(!buffer || buffer) // TOCHECK: Why are we coming here with a 0 buffer? +#if !defined(__UWP__) && (defined(__LUMIN__) || defined(_GLES3) || (!defined(_GLES) && !defined(_GLES2))) + if(baseVertex) + glDrawElementsBaseVertex(primType, count, type, indices, baseVertex); + else +#endif + glDrawElements(primType, count, type, indices); + } + } +}; + +public struct GLFB +{ + int w, h; + uint fbo; + uint color; + uint depth; + uint samples; + uint colorRBO, depthRBO; + int depthFormat, colorFormat; + + void free() + { + if(fbo) + { + glDeleteFramebuffers(1, &fbo); + fbo = 0; + } + if(color) + { +#ifdef GLSTATS + GLStats::freeTextures(1, &color); +#endif + glDeleteTextures(1, &color); + } + if(depth) + { +#ifdef GLSTATS + GLStats::freeTextures(1, &depth); +#endif + glDeleteTextures(1, &depth); + } + color = 0; + depth = 0; + + if(colorRBO) glDeleteRenderbuffers(1, &colorRBO); + if(depthRBO) glDeleteRenderbuffers(1, &depthRBO); + colorRBO = 0; + depthRBO = 0; + } + + void copyToTexture() + { + if(colorRBO) + { + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindTexture(GL_TEXTURE_2D, color); + glCopyTexImage2D(GL_TEXTURE_2D, 0, colorFormat, 0, 0, w, h, 0); + glBindTexture(GL_TEXTURE_2D, depth); + glCopyTexImage2D(GL_TEXTURE_2D, 0, depthFormat, 0, 0, w, h, 0); + glBindTexture(GL_TEXTURE_2D, 0); + } + else if(depthRBO) + { + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindTexture(GL_TEXTURE_2D, depth); + glCopyTexImage2D(GL_TEXTURE_2D, 0, depthFormat, 0, 0, w, h, 0); + glBindTexture(GL_TEXTURE_2D, 0); + } + } + + bool setup(bool textureFBO, bool allocTextures, int samples, int colorFormat, int depthFormat, int width, int height) + { + int s; + bool allocateColor = colorFormat && (w != width || h != height); + bool allocateDepth = depthFormat && (w != width || h != height); + bool result = false; +#if defined(_GLES) || defined(_GLES2) + samples = 1; +#endif +#if !defined(_GLES) && !defined(_GLES2) + int texTarget = samples > 1 ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; +#else + int texTarget = GL_TEXTURE_2D; +#endif + + if(fbo && (colorRBO == 0) != textureFBO) free(); + + if(!fbo) glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + if(colorFormat && !color) + { + allocateColor = true; + if(textureFBO || allocTextures) + glGenTextures(1, &color); + if(!textureFBO) + glGenRenderbuffers(1, &colorRBO); + } + + while(glGetError()); + + // *** Set up Color attachment first *** + if(allocateColor) + { + this.colorFormat = colorFormat; + if(textureFBO || allocTextures) + glBindTexture(texTarget, color); + if(!textureFBO) + glBindRenderbuffer(GL_RENDERBUFFER, colorRBO); + for(s = samples; ; s >>= 1) + { +#if !defined(_GLES) && !defined(_GLES2) && !defined(__UWP__) + if(s > 1) + { + if(textureFBO) + glTexImage2DMultisample(texTarget, s, colorFormat, width, height, GL_FALSE); + else + glRenderbufferStorageMultisample(GL_RENDERBUFFER, s, colorFormat, width, height); + if(!glGetError()) + break; + } + else +#endif + { + texTarget = GL_TEXTURE_2D; + if(textureFBO || allocTextures) + glTexImage2D(texTarget, 0, colorFormat, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null); + if(!textureFBO) + glRenderbufferStorage(GL_RENDERBUFFER, colorFormat, width, height); + break; + } + } + + if(textureFBO || allocTextures) + { + if(samples <= 1) + { + glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//GL_LINEAR); + glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//GL_LINEAR); +#if !defined(_GLES) && !defined(_GLES2) + glTexParameteri(texTarget, GL_TEXTURE_MAX_LEVEL, 0); +#endif + glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + glBindTexture(texTarget, 0); + } + if(textureFBO) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texTarget, color, 0); + else + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRBO); + samples = s; + } + +#if defined(_GLES2) || defined(_GLES) + textureFBO = false; // No support for depth texture on OpenGL ES 2, except with OES_depth_texture +#endif + + if(depthFormat && !depth) + { + allocateDepth = true; + if(textureFBO || allocTextures) + glGenTextures(1, &depth); + if(!textureFBO) + glGenRenderbuffers(1, &depthRBO); } + + // *** Set up Depth attachment *** + if(allocateDepth) + { + this.depthFormat = depthFormat; + // TODO: try other samples for depth only? + if(textureFBO || allocTextures) + { + glBindTexture(texTarget, depth); +#if !defined(_GLES) && !defined(_GLES2) && !defined(__UWP__) + if(samples > 1) + glTexImage2DMultisample(texTarget, samples, depthFormat, width, height, GL_FALSE); + else +#endif + glTexImage2D(texTarget, 0, depthFormat, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, null); + } + if(!textureFBO) + { + glBindRenderbuffer(GL_RENDERBUFFER, depthRBO); +#if !defined(_GLES) && !defined(_GLES2) + if(samples > 1) + glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthFormat, width, height); + else +#endif + glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, width, height); + } + + if(textureFBO || allocTextures) + { + if(samples <= 1) + { + glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + glBindTexture(texTarget, 0); + } + if(textureFBO) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texTarget, depth, 0); + else + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRBO); + } + + this.samples = samples; + this.w = width; + this.h = height; + + { + int status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + result = status == GL_FRAMEBUFFER_COMPLETE; + +#ifdef _DEBUG + if(!result) + PrintLn("Incomplete GL Framebuffer (", status, ")\n"); +#endif + } + return result; + } + + void copy(const GLFB src, const Box srcExtent, const Box dstExtent, ClearType buffers, bool filter) + { +#if !defined(_GLES) && !defined(_GLES2) + int sx = srcExtent != null ? srcExtent.left : 0; + int sy = srcExtent != null ? srcExtent.top : 0; + int sx1 = srcExtent != null ? srcExtent.right : src.w; + int sy1 = srcExtent != null ? srcExtent.bottom : src.h; + int dx = dstExtent != null ? dstExtent.left : 0; + int dy = dstExtent != null ? dstExtent.top : 0; + int dx1 = dstExtent != null ? dstExtent.right : w; + int dy1 = dstExtent != null ? dstExtent.bottom : h; + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, src.fbo); + glBlitFramebuffer( + sx, sy, sx1, sy1, + dx, dy, dx1, dy1, + buffers == colorAndDepth ? GL_COLOR_BUFFER_BIT : + buffers == depthBuffer ? GL_DEPTH_BUFFER_BIT : + (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT), + filter ? GL_LINEAR : GL_NEAREST); +#endif + } + + void read(Bitmap bitmap, ClearType buffer, bool sRGB) + { +#if !defined(_GLES) && !defined(_GLES2) && !defined(__UWP__) + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glReadBuffer(buffer == depthBuffer ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0); + if(sRGB) glEnable( GL_FRAMEBUFFER_SRGB ); + glReadPixels(0, 0, bitmap.width, bitmap.height, GL_BGRA, GL_UNSIGNED_BYTE, bitmap.picture); + if(sRGB) glDisable( GL_FRAMEBUFFER_SRGB ); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif } }; + +static Map textures { }; +static Map buffers { }; + +static uint64 texMem; +static uint64 bufMem; + +public class GLStats +{ +public: + void ::allocTexture(uint tex, uint w, uint h, bool mipMaps) + { + uint64 currentMem = textures[tex]; + uint64 newMem = (uint64)w * h * 4; + + if(mipMaps) + newMem += (uint64)(newMem * 1.33); + texMem += (int64)newMem - (int64)currentMem; + + textures[tex] = newMem; + } + + void ::freeTextures(uint count, uint * texs) + { + int i; + for(i = 0; i < count; i++) + { + uint tex = texs[i]; + MapIterator it { map = textures }; + if(it.Index(tex, false)) + { + texMem -= it.data; + it.Remove(); + } + } + } + + void ::allocBuffer(uint buf, uint size) + { + uint64 currentMem = buffers[buf]; + bufMem += (int64)size - (int64)currentMem; + buffers[buf] = size; + } + + void ::freeBuffers(uint count, uint * bufs) + { + int i; + for(i = 0; i < count; i++) + { + uint buf = bufs[i]; + MapIterator it { map = buffers }; + if(it.Index(buf, false)) + { + bufMem -= it.data; + it.Remove(); + } + } + } + + void ::print() + { + char tmp[256]; + PrintLn(""); + PrintLn("OpenGL Textures & Buffers Memory Stats"); + PrintLn("======================================"); + PrintSize(tmp, texMem, 2); + PrintLn(textures.count, " textures allocated; ", tmp); + PrintSize(tmp, bufMem, 2); + PrintLn(buffers.count, " buffers allocated; ", tmp); + PrintLn(""); + } + + void ::printBuf(char * output, uint size) + { + char tmp[256]; + char * s = output; + int l; + + *s = 0; + l = PrintLnBuf(output, size, ""); + size -= l, s += l; + l = PrintLnBuf(s, size, "OpenGL Textures & Buffers Memory Stats"); + size -= l, s += l; + l = PrintLnBuf(s, size, "======================================"); + size -= l, s += l; + PrintSize(tmp, texMem, 2); + l = PrintLnBuf(s, size, textures.count, " textures allocated; ", tmp); + size -= l, s += l; + PrintSize(tmp, bufMem, 2); + l = PrintLnBuf(s, size, buffers.count, " buffers allocated; ", tmp); + size -= l, s += l; + l = PrintLnBuf(s, size, ""); + size -= l, s += l; + } +} diff --git a/ecere/src/gfx/drivers/gl3/immediate.ec b/ecere/src/gfx/drivers/gl3/immediate.ec index 754a40015d..e1873eaf9d 100644 --- a/ecere/src/gfx/drivers/gl3/immediate.ec +++ b/ecere/src/gfx/drivers/gl3/immediate.ec @@ -51,9 +51,9 @@ static struct FloatGLAB : GLAB if(bufSize > this.bufSize) { this.bufSize = bufSize; - GLAB::allocate(bufSize, null, dynamicDraw); + GLB::allocate(bufSize, null, streamDraw); } - GLAB::upload(0, bufSize, verticesBuf.pointer); + GLB::upload(0, bufSize, verticesBuf->pointer); } static inline void free() @@ -62,16 +62,23 @@ static struct FloatGLAB : GLAB count = 0; size = 0; delete pointer; - GLAB::free(); + GLB::free(); } }; -FloatGLAB verticesBuf { stride = 4 }; +#define NUM_BUFFERS 40 + +FloatGLAB verticesBuffers[NUM_BUFFERS]; FloatGLAB normalsBuf { stride = 3 }; +FloatGLAB * verticesBuf; + +static int curBuf = 0; void glimtkTerminate() { - verticesBuf.free(); + int i; + for(i = 0; i < NUM_BUFFERS; i++) + verticesBuffers[i].free(); normalsBuf.free(); } @@ -87,21 +94,28 @@ public void glimtkRecti(int a, int b, int c, int d) public void glimtkBegin(GLIMTKMode mode) { + int stride = 4; + + verticesBuf = &verticesBuffers[curBuf]; + if(++curBuf >= NUM_BUFFERS) curBuf = 0; + beginMode = mode; vertexColorValues = false; beginCount = 0; vertexOffset = 2; - verticesBuf.count = 0; - verticesBuf.stride = 4; + if(verticesBuf->stride != stride) + verticesBuf->size = verticesBuf->size * verticesBuf->stride / stride; + verticesBuf->count = 0; + verticesBuf->stride = stride; numCoords = 2; } public void glimtkTexCoord2f(float x, float y) { - int stride = verticesBuf.stride; + int stride = verticesBuf->stride; bool quadsAdd = beginMode == quads && !glCaps_quads && ((beginCount % 4) == 3); - float * buf = verticesBuf.ensure(quadsAdd ? 3 : 1); + float * buf = verticesBuf->ensure(quadsAdd ? 3 : 1); buf[0] = x; buf[1] = y; buf += stride; @@ -122,15 +136,20 @@ public void glimtkTexCoord2fv(float * a) { glimtkTexCoord2f(a[0], a[1]); public void glimtkVertex2f(float x, float y) { + int stride; + numCoords = 2; - verticesBuf.stride = vertexOffset + numCoords; + stride = vertexOffset + numCoords; + if(verticesBuf->stride != stride) + verticesBuf->size = verticesBuf->size * verticesBuf->stride / stride; + verticesBuf->stride = stride; { - int stride = verticesBuf.stride; + int stride = verticesBuf->stride; bool quadsAdd = beginMode == quads && !glCaps_quads && ((beginCount % 4) == 3); - float * buf = verticesBuf.ensure(quadsAdd ? 3 : 1) + vertexOffset; + float * buf = verticesBuf->ensure(quadsAdd ? 3 : 1) + vertexOffset; buf[0] = x; buf[1] = y; - verticesBuf.count++; + verticesBuf->count++; if(quadsAdd) { buf += stride; @@ -139,7 +158,7 @@ public void glimtkVertex2f(float x, float y) buf += stride; buf[0] = buf[-3*stride]; buf[1] = buf[-3*stride+1]; - verticesBuf.count+=2; + verticesBuf->count+=2; } } beginCount++; @@ -149,16 +168,20 @@ public void glimtkVertex2d(double x, double y) { glimtkVertex2f((float)x, (flo public void glimtkVertex3f( float x, float y, float z ) { + int stride; + numCoords = 3; - verticesBuf.stride = vertexOffset + numCoords; + stride = vertexOffset + numCoords; + if(verticesBuf->stride != stride) + verticesBuf->size = verticesBuf->size * verticesBuf->stride / stride; + verticesBuf->stride = stride; { - int stride = verticesBuf.stride; bool quadsAdd = beginMode == quads && !glCaps_quads && ((beginCount % 4) == 3); - float * buf = verticesBuf.ensure(quadsAdd ? 3 : 1) + vertexOffset; + float * buf = verticesBuf->ensure(quadsAdd ? 3 : 1) + vertexOffset; buf[0] = x; buf[1] = y; buf[2] = z; - verticesBuf.count++; + verticesBuf->count++; if(quadsAdd) { buf += stride; @@ -169,7 +192,7 @@ public void glimtkVertex3f( float x, float y, float z ) buf[0] = buf[-3*stride]; buf[1] = buf[-3*stride+1]; buf[2] = buf[-3*stride+2]; - verticesBuf.count+=2; + verticesBuf->count+=2; } } beginCount++; @@ -183,14 +206,18 @@ public void glimtkColor4f(float r, float g, float b, float a) { if(beginMode != unset) { + int stride; // Called within glBegin()/glEnd() vertexColorValues = true; vertexOffset = 6; - verticesBuf.stride = vertexOffset + numCoords; + stride = vertexOffset + numCoords; + if(verticesBuf->stride != stride) + verticesBuf->size = verticesBuf->size * verticesBuf->stride / stride; + verticesBuf->stride = stride; + { - int stride = verticesBuf.stride; bool quadsAdd = beginMode == quads && !glCaps_quads && ((beginCount % 4) == 3); - float * buf = verticesBuf.ensure(quadsAdd ? 3 : 1) + 2; + float * buf = verticesBuf->ensure(quadsAdd ? 3 : 1) + 2; buf[0] = r, buf[1] = g, buf[2] = b, buf[3] = a; if(quadsAdd) @@ -237,7 +264,7 @@ public void glimtkColor4fv(float * a) { glimtkColor4f(a[0] public void glimtkNormal3f(float x, float y, float z) { - normalsBuf.count = verticesBuf.count; + normalsBuf.count = verticesBuf->count; { int stride = normalsBuf.stride; bool quadsAdd = beginMode == quads && !glCaps_quads && ((beginCount % 4) == 3); @@ -277,32 +304,39 @@ public void glimtkEnd() if(glCaps_vertexBuffer) { - verticesBuf.upload(); - verticesBuf.use(texCoord, 2, GL_FLOAT, verticesBuf.stride * sizeof(float), 0); + verticesBuf->upload(); + verticesBuf->use(texCoord, 2, GL_FLOAT, verticesBuf->stride * sizeof(float), 0); } else - noAB.use(texCoord, 2, GL_FLOAT, verticesBuf.stride * sizeof(float), verticesBuf.pointer); + noAB.use(texCoord, 2, GL_FLOAT, verticesBuf->stride * sizeof(float), verticesBuf->pointer); if(vertexColorValues) { GLEnableClientState(COLORS); if(glCaps_vertexBuffer) - verticesBuf.use(color, 4, GL_FLOAT, verticesBuf.stride * sizeof(float), (void *)(2 * sizeof(float))); + verticesBuf->use(color, 4, GL_FLOAT, verticesBuf->stride * sizeof(float), (void *)(2 * sizeof(float))); else - noAB.use(color, 4, GL_FLOAT, verticesBuf.stride * sizeof(float), verticesBuf.pointer + 2); + noAB.use(color, 4, GL_FLOAT, verticesBuf->stride * sizeof(float), verticesBuf->pointer + 2); #if ENABLE_GL_SHADERS if(glCaps_shaders) defaultShader.setPerVertexColor(true); #endif } + else + { +#if ENABLE_GL_SHADERS + if(glCaps_shaders) + defaultShader.setPerVertexColor(false); +#endif + } if(glCaps_vertexBuffer) - verticesBuf.use(vertex, numCoords, GL_FLOAT, verticesBuf.stride * sizeof(float), (void *)(vertexOffset * sizeof(float))); + verticesBuf->use(vertex, numCoords, GL_FLOAT, verticesBuf->stride * sizeof(float), (void *)(vertexOffset * sizeof(float))); else - noAB.use(vertex, numCoords, GL_FLOAT, verticesBuf.stride * sizeof(float), verticesBuf.pointer + vertexOffset); + noAB.use(vertex, numCoords, GL_FLOAT, verticesBuf->stride * sizeof(float), verticesBuf->pointer + vertexOffset); - if(normalsBuf.count && normalsBuf.count == verticesBuf.count) + if(normalsBuf.count && normalsBuf.count == verticesBuf->count) { GLEnableClientState(NORMALS); if(glCaps_vertexBuffer) @@ -311,11 +345,11 @@ public void glimtkEnd() normalsBuf.use(normal, 3, GL_FLOAT, 3*sizeof(float), 0); } else - noAB.use(normal, 3, GL_FLOAT, 3*sizeof(float),normalsBuf.pointer); + noAB.use(normal, 3, GL_FLOAT, 3*sizeof(float), normalsBuf.pointer); } GLFlushMatrices(); - glDrawArrays(mode, 0, verticesBuf.count); + glDrawArrays(mode, 0, verticesBuf->count); if(normalsBuf.count) GLDisableClientState(NORMALS); diff --git a/ecere/src/gfx/drivers/gl3/matrixStack.ec b/ecere/src/gfx/drivers/gl3/matrixStack.ec index 08b164b6f0..55acaeabdc 100644 --- a/ecere/src/gfx/drivers/gl3/matrixStack.ec +++ b/ecere/src/gfx/drivers/gl3/matrixStack.ec @@ -106,9 +106,12 @@ public void glmsLoadMatrix(Matrix matrix) public void glmsLoadIdentity() { int ix = matrixIndex[curStack]; - isIdentity[curStack][ix] = true; - matrixStack[curStack][ix].Identity(); - stackModified[curStack] = true; + if(!isIdentity[curStack][ix]) + { + isIdentity[curStack][ix] = true; + matrixStack[curStack][ix].Identity(); + stackModified[curStack] = true; + } } public void glmsPushMatrix() @@ -179,7 +182,7 @@ public void glmsScaled(double a, double b, double c) m.Identity(); m.Scale(a,b,c); - r.Multiply(m, matrixStack[curStack][ix]); + r.Multiply3x4(m, matrixStack[curStack][ix]); matrixStack[curStack][ix] = r; stackModified[curStack] = true; } @@ -192,7 +195,7 @@ public void glmsTranslated( double a, double b, double c) m.Identity(); m.Translate(a,b,c); - r.Multiply(m, matrixStack[curStack][ix]); + r.Multiply3x4(m, matrixStack[curStack][ix]); matrixStack[curStack][ix] = r; stackModified[curStack] = true; } @@ -240,7 +243,7 @@ public void glmsRotated( double angle, double x, double y, double z) n.Normalize({ -x, -y, -z }); q.RotationAxis(n, angle); m.RotationQuaternion(q); - r.Multiply(m, matrixStack[curStack][ix]); + r.Multiply3x4(m, matrixStack[curStack][ix]); matrixStack[curStack][ix] = r; stackModified[curStack] = true; } @@ -256,6 +259,24 @@ public void glmsMultMatrixd(double * i) stackModified[curStack] = true; } +public void glmsMultMatrixf(float * i) +{ + double m[16] = + { + i[0*4+0], i[0*4+1], i[0*4+2], i[0*4+3], + i[1*4+0], i[1*4+1], i[1*4+2], i[1*4+3], + i[2*4+0], i[2*4+1], i[2*4+2], i[2*4+3], + i[3*4+0], i[3*4+1], i[3*4+2], i[3*4+3] + }; + Matrix r; + int ix = matrixIndex[curStack]; + isIdentity[curStack][ix] = false; + + r.Multiply((Matrix *)m, matrixStack[curStack][ix]); + matrixStack[curStack][ix] = r; + stackModified[curStack] = true; +} + public void glmsGetDoublev(GLMSWhatToGet what, double * i) { int ix; @@ -291,21 +312,22 @@ public void glmsFlushMatrices() { int ix = matrixIndex[stack]; Matrix * matrix = &matrixStack[stack][ix]; + double * dm = matrix->array; + float m[16] = + { + (float)dm[0 ], (float)dm[ 1], (float)dm[ 2], (float)dm[ 3], + (float)dm[4 ], (float)dm[ 5], (float)dm[ 6], (float)dm[ 7], + (float)dm[8 ], (float)dm[ 9], (float)dm[10], (float)dm[11], + (float)dm[12], (float)dm[13], (float)dm[14], (float)dm[15] + }; #if ENABLE_GL_SHADERS if(glCaps_shaders) - activeShader.updateMatrix((MatrixMode) (0x1700 + stack), matrix, isIdentity[stack][ix]); + activeShader.updateMatrix((MatrixMode) (0x1700 + stack), m, isIdentity[stack][ix]); #endif #if ENABLE_GL_FFP if(!glCaps_shaders) { - float m[16] = - { - (float)matrix->m[0][0], (float)matrix->m[0][1], (float)matrix->m[0][2], (float)matrix->m[0][3], - (float)matrix->m[1][0], (float)matrix->m[1][1], (float)matrix->m[1][2], (float)matrix->m[1][3], - (float)matrix->m[2][0], (float)matrix->m[2][1], (float)matrix->m[2][2], (float)matrix->m[2][3], - (float)matrix->m[3][0], (float)matrix->m[3][1], (float)matrix->m[3][2], (float)matrix->m[3][3] - }; glMatrixMode(stack + 0x1700); glLoadMatrixf(m); } diff --git a/ecere/src/gfx/drivers/gl3/shaders.ec b/ecere/src/gfx/drivers/gl3/shaders.ec index 948607f824..1811bfd146 100644 --- a/ecere/src/gfx/drivers/gl3/shaders.ec +++ b/ecere/src/gfx/drivers/gl3/shaders.ec @@ -5,6 +5,16 @@ import "String" #include "gl123es.h" +#if defined(__ANDROID__) +#include +#define printf(...) ((void)__android_log_print(ANDROID_LOG_INFO, "ecere-app", __VA_ARGS__)) +#define puts PrintLn +// #define _DEBUG +#elif defined(__UWP__) +#define printf Logf +#define puts(x) Logf("%s\n", x) +#endif + namespace gfx::drivers; // Generic Shader @@ -40,6 +50,12 @@ public: } }; +public class ShaderModifiedUniforms : uint32 +{ +public: + bool matMV:1, light:1, material:1, matPrj:1, matTex:1; +} + public class Shader { Map programs { }; @@ -87,8 +103,18 @@ public: } } + property CompiledShader activeCompiledShader + { + get + { + return shader; + } + } + uint64 state; - bool uniformsModified; + ShaderModifiedUniforms modifiedUniforms; + + modifiedUniforms = { true, true, true, true, true }; private: char * vertexShaderFile; @@ -112,7 +138,12 @@ private: return true; } else + { printf("Error accessing shader %s.\n", file); +#if defined(__LUMIN__) + PrintLn("Error accessing shader ", file, "."); +#endif + } return false; } @@ -139,14 +170,17 @@ public: CompiledShader shader = null; #if ENABLE_GL_SHADERS MapIterator it { map = programs }; - if(!vertexShaderSource && vertexShaderFile) - loadShader(vertexShaderFile, &vertexShaderSource, &vsLen); - if(!fragmentShaderSource && fragmentShaderFile) - loadShader(fragmentShaderFile, &fragmentShaderSource, &fsLen); + //PrintLn("Loading ", _class.name, " shader for state 0x", state, " (", (DefaultShaderBits)state, ")"); if(!it.Index(state, true)) { + if(!vertexShaderSource && vertexShaderFile) + loadShader(vertexShaderFile, &vertexShaderSource, &vsLen); + if(!fragmentShaderSource && fragmentShaderFile) + loadShader(fragmentShaderFile, &fragmentShaderSource, &fsLen); + #ifdef _DEBUG + PrintLn("Compiling ", _class.name, " shader for state 0x", state, " (", (DefaultShaderBits)state, ")"); printf("We've got OpenGL Version %s\n\n", (char *)glGetString(GL_VERSION)); printf("We've got Shading Language Version %s\n\n", (char *)glGetString(GL_SHADING_LANGUAGE_VERSION)); #endif @@ -157,6 +191,9 @@ public: int fShader = glCreateShader(GL_FRAGMENT_SHADER); int vStatus = 0, fStatus = 0; ZString definitions = getDefinitions(state); + + //PrintLn("Created program ", program, " for ", _class.name, " shader state 0x", state, " (", (DefaultShaderBits)state, ")"); + if(definitions) { const char * vSources[2] = { definitions._string, vertexShaderSource }; @@ -245,8 +282,16 @@ public: shader.program = program; shader.vertex = vShader; shader.fragment = fShader; + +#ifdef _DEBUG + PrintLn("Successfully registered program ", program, " for ", _class.name, " shader state 0x", state, " (", (DefaultShaderBits)state, ")"); +#endif } + else + PrintLn("Failure (2) to register program ", program, " for ", _class.name, " shader state 0x", state, " (", (DefaultShaderBits)state, ")"); } + else + PrintLn("Failure to register program ", program, " for ", _class.name, " shader state 0x", state, " (", (DefaultShaderBits)state, ")"); } #if defined(_DEBUG) && defined(__WIN32__) if(!shader) @@ -264,6 +309,12 @@ public: bool result = false; #if ENABLE_GL_SHADERS CompiledShader shader = this.shader; + + if(!glCaps_shaders) return false; + + if(activeShader != this) + activeShader = this; + result = true; if(activeState != state || !shader) { @@ -274,7 +325,7 @@ public: { if(shader != this.shader) { - uniformsModified = true; + modifiedUniforms = { true, true, true, true, true }; activeState = state; this.shader = shader; } @@ -283,10 +334,10 @@ public: activeProgram = shader.program; glUseProgram(shader.program); } - if(uniformsModified) + if(modifiedUniforms) { uploadUniforms(shader); - uniformsModified = false; + modifiedUniforms = 0; } } #endif @@ -302,7 +353,15 @@ public: for(i = 0; i < 3; i++) { int ix = matrixIndex[i]; - updateMatrix(MatrixMode::modelView + i, matrixStack[i][ix], isIdentity[i][ix]); + double * dm = matrixStack[i][ix].array; + float m[16] = + { + (float)dm[0 ], (float)dm[ 1], (float)dm[ 2], (float)dm[ 3], + (float)dm[4 ], (float)dm[ 5], (float)dm[ 6], (float)dm[ 7], + (float)dm[8 ], (float)dm[ 9], (float)dm[10], (float)dm[11], + (float)dm[12], (float)dm[13], (float)dm[14], (float)dm[15] + }; + updateMatrix(MatrixMode::modelView + i, m, isIdentity[i][ix]); } } } @@ -318,7 +377,7 @@ public: #endif } virtual CompiledShader registerShader(int program, uint64 state) { return CompiledShader { }; } - virtual void updateMatrix(MatrixMode mode, Matrix matrix, bool isIdentity); + virtual void updateMatrix(MatrixMode mode, float * matrix, bool isIdentity); virtual void uploadUniforms(CompiledShader shader); #if !defined(ECERE_NO3D) virtual void setMaterial(Material material, MeshFeatures flags); diff --git a/ecere/src/gfx/drivers/lfbBlit.ec b/ecere/src/gfx/drivers/lfbBlit.ec index 61f4047bc7..ba458e56f9 100644 --- a/ecere/src/gfx/drivers/lfbBlit.ec +++ b/ecere/src/gfx/drivers/lfbBlit.ec @@ -1,6 +1,7 @@ namespace gfx::drivers; import "Bitmap" +import "Color" ////////////////////////////////////////////////////////////////////////////// // BLITTING ROUTINES ///////////////////////////////////////////////////////// @@ -57,7 +58,7 @@ BLIT(DTF, uint32, uint32, BLIT_INIT_DEC, FLIP(TRANSPUT(*source))) #define BLITSD { { BLIT_D, BLIT_DF}, { BLIT_DT, BLIT_DTF } } void (* blits_table[PixelFormat][2][2]) (BLIT_ARGS) = -{ BLITSB, BLITSB, BLITSW, BLITSW, BLITSW, BLITSD, BLITSB, BLITSW, BLITSD }; +{ BLITSB, BLITSB, BLITSW, BLITSW, BLITSW, BLITSD, BLITSB, BLITSW, BLITSD, BLITSW }; // 8 bit to other formats #define PALINIT ColorAlpha * palette = src.palette; @@ -125,7 +126,8 @@ void (* blits_8bit_table[PixelFormat][2][2]) (BLIT_ARGS) = { { BLIT_B888, BLIT_B888F }, { BLIT_B888T, BLIT_B888TF } }, { { null, null }, { null, null } }, { { null, null }, { null, null } }, - { { BLIT_BRGBA, BLIT_BRGBAF }, { BLIT_BRGBAT, BLIT_BRGBATF } } + { { BLIT_BRGBA, BLIT_BRGBAF }, { BLIT_BRGBAT, BLIT_BRGBATF } }, + { { null, null }, { null, null } } }; // Using Palette Shades @@ -155,7 +157,8 @@ void (* shades_blit_table[PixelFormat][2]) (BLIT_ARGS) = { BLIT_BI888,BLIT_BI888F }, { null, null }, { null, null }, - { BLIT_BIRGBA,BLIT_BIRGBAF } + { BLIT_BIRGBA,BLIT_BIRGBAF }, + { null, null } }; ////////////////////////////////////////////////////////////////////////////// @@ -261,7 +264,8 @@ void (* stretches_8bit_table[PixelFormat][2][2]) (STRETCH_ARGS) = { { STRETCH_B888, STRETCH_B888F }, { STRETCH_B888T, STRETCH_B888TF } }, { { null, null }, { null, null } }, { { null, null }, { null, null } }, - { { STRETCH_BRGBA, STRETCH_BRGBAF }, { STRETCH_BRGBAT, STRETCH_BRGBATF } } + { { STRETCH_BRGBA, STRETCH_BRGBAF }, { STRETCH_BRGBAT, STRETCH_BRGBATF } }, + { { null, null }, { null, null } } }; // Using Palette Shades @@ -289,8 +293,9 @@ void (* shades_stretch_table[PixelFormat][2]) (STRETCH_ARGS) = { STRETCH_BI888,STRETCH_BI888F }, { null, null }, { null, null }, - { STRETCH_BIRGBA,STRETCH_BIRGBAF } - //{ STRETCH_BI888,STRETCH_BI888F } + { STRETCH_BIRGBA,STRETCH_BIRGBAF }, + //{ STRETCH_BI888,STRETCH_BI888F }. + { null, null } }; ////////////////////////////////////////////////////////////////////////////// @@ -431,7 +436,172 @@ FILTER(565F, Color565, Color565,-1, 0, (ColorAlpha)(color), (Color565)(color)) FILTER(565T, Color565, Color565, 1, 1, (ColorAlpha)(color), (Color565)(color)) FILTER(565TF, Color565, Color565,-1, 1, (ColorAlpha)(color), (Color565)(color)) -FILTER(888, ColorAlpha, ColorAlpha, 1, 0, color, color) +// FILTER(888, ColorAlpha, ColorAlpha, 1, 0, color, color) + +class ColorAlpha; + +static void FILTER_888(Bitmap src, Bitmap dst, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh) +{ + uint dstStride = dst.stride, srcStride = src.stride; + ColorAlpha * source = ((ColorAlpha *)src.picture) + sy * srcStride + sx; + ColorAlpha * dest = ((ColorAlpha *)dst.picture) + dy * dstStride + dx; + uint addDest = dstStride - w; + + if (w > sw && h > sh) + { + float scaleX = (float)sw / w; + float scaleY = (float)sh / h; + int y; + for (y = 0; y < h; y++) + { + int y0 = y * sh / h; + int y1 = Min(y0 + 1, sh - 1); + float alpha = y * scaleY - y0; + int x; + for (x = 0; x < w; x++, dest += 1) + { + int x0 = x * sw / w; + int x1 = Min(x0 + 1, sw - 1); + float beta = x * scaleX - x0; + ColorAlpha src00 = source[y0 * srcStride + x0]; + ColorAlpha src01 = source[y0 * srcStride + x1]; + ColorAlpha src10 = source[y1 * srcStride + x0]; + ColorAlpha src11 = source[y1 * srcStride + x1]; + Color color00 = src00.color, color01 = src01.color, color10 = src10.color, color11 = src11.color; + float a1 = (src00.a) * (1.0f - beta) + (src01.a) * beta; + float r1 = (color00.r) * (1.0f - beta) + (color01.r) * beta; + float g1 = (color00.g) * (1.0f - beta) + (color01.g) * beta; + float b1 = (color00.b) * (1.0f - beta) + (color01.b) * beta; + float a2 = (src10.a) * (1.0f - beta) + (src11.a) * beta; + float r2 = (color10.r) * (1.0f - beta) + (color11.r) * beta; + float g2 = (color10.g) * (1.0f - beta) + (color11.g) * beta; + float b2 = (color10.b) * (1.0f - beta) + (color11.b) * beta; + float a = a1 * (1.0f - alpha) + a2 * alpha; + float r = r1 * (1.0f - alpha) + r2 * alpha; + float g = g1 * (1.0f - alpha) + g2 * alpha; + float b = b1 * (1.0f - alpha) + b2 * alpha; + *dest = ColorAlpha { (byte) a, { (byte) r, (byte) g, (byte) b } }; + } + dest += addDest; + } + } + else + { + int y; + float scaleX = (float)sw / w; + float scaleY = (float)sh / h; + + for(y = 0; y < h; y++) + { + int y0 = (int)(y * scaleY); + int yc = Min(y0 + 2, sh) - y0; + int x; + for(x = 0; x < w; x++, dest += 1) + { + int x0 = (int)(x * scaleX); + int xc = Min(x0 + 2, sw) - x0; + uint addSrc = srcStride - xc; + uint64 a = 0, r = 0, g = 0, b = 0; + int i, j, numPixels = yc * xc; + ColorAlpha * src = source + y0 * srcStride + x0; + + for(i = y0; i < y0 + yc; i++, src += addSrc) + { + for(j = 0; j < xc; j++) + { + ColorAlpha pixel = *(src++); + Color c = pixel.color; + a += pixel.a, r += c.r, g += c.g, b += c.b; + } + } + a /= numPixels; + r /= numPixels; + g /= numPixels; + b /= numPixels; + + *dest = ColorAlpha { (byte) a, { (byte) r, (byte) g, (byte) b } }; + } + dest += addDest; + } + } +} + +static void FILTER_A16T(Bitmap src, Bitmap dst, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh) +{ + uint dstStride = dst.stride, srcStride = src.stride; + uint16 * source = ((uint16 *)src.picture) + sy * srcStride + sx; + uint16 * dest = ((uint16 *)dst.picture) + dy * dstStride + dx; + uint addDest = dstStride - w; + + if (w > sw && h > sh) + { + float scaleX = (float)sw / w; + float scaleY = (float)sh / h; + int y; + for (y = 0; y < h; y++) + { + int y0 = y * sh / h; + int y1 = Min(y0 + 1, sh - 1); + float alpha = y * scaleY - y0; + int x; + for (x = 0; x < w; x++, dest += 1) + { + int x0 = x * sw / w; + int x1 = Min(x0 + 1, sw - 1); + float beta = x * scaleX - x0; + uint16 src00 = source[y0 * srcStride + x0]; + uint16 src01 = source[y0 * srcStride + x1]; + uint16 src10 = source[y1 * srcStride + x0]; + uint16 src11 = source[y1 * srcStride + x1]; + uint16 v00 = src00, v01 = src01, v10 = src10, v11 = src11; + float v1 = v00 && v01 ? (v00) * (1.0f - beta) + (v01) * beta : v00 ? v00 : v01; + float v2 = v10 && v11 ? (v10) * (1.0f - beta) + (v11) * beta : v10 ? v10 : v11; + float v = v1 && v2 ? v1 * (1.0f - alpha) + v2 * alpha : v1 ? v1 : v2; + *dest = (uint16)v; + } + dest += addDest; + } + } + else + { + int y; + float scaleX = (float)sw / w; + float scaleY = (float)sh / h; + + for(y = 0; y < h; y++) + { + int y0 = (int)(y * scaleY); + int yc = Min(y0 + 2, sh) - y0; + int x; + for(x = 0; x < w; x++, dest += 1) + { + int x0 = (int)(x * scaleX); + int xc = Min(x0 + 2, sw) - x0; + uint addSrc = srcStride - xc; + uint64 v = 0; + int i, j, numPixels = yc * xc; + uint16 * src = source + y0 * srcStride + x0; + + for(i = y0; i < y0 + yc; i++, src += addSrc) + { + for(j = 0; j < xc; j++) + { + uint16 pixel = *(src++); + if(pixel) + v += pixel; + else + numPixels--; + } + } + if(numPixels) v /= numPixels; + *dest = (uint16)v; + } + dest += addDest; + } + } +} + + FILTER(888TF, ColorAlpha, ColorAlpha,-1, 0, color, color) FILTER(888T, ColorAlpha, ColorAlpha, 1, 1, color, color) FILTER(888F, ColorAlpha, ColorAlpha,-1, 1, color, color) @@ -441,6 +611,9 @@ FILTER(ATF, byte, byte,-1, 0, color, (byte)color) FILTER(AT, byte, byte, 1, 1, color, (byte)color) FILTER(AF, byte, byte,-1, 1, color, (byte)color) +FILTER(A16, uint16, uint16, 1, 0, color, (uint16)color) +//FILTER(A16T, uint16, uint16, 1, 1, color, (uint16)color) + void (* filters_table[PixelFormat][2][2]) (FILTER_ARGS) = { { { null, null }, { null, null} }, @@ -451,5 +624,6 @@ void (* filters_table[PixelFormat][2][2]) (FILTER_ARGS) = { { FILTER_888, FILTER_888F }, { FILTER_888T, FILTER_888TF } }, { { FILTER_A, FILTER_AF }, { FILTER_AT, FILTER_ATF } }, // Alpha { { null, null }, { null, null} }, // Text - { { FILTER_888, FILTER_888F }, { FILTER_888T, FILTER_888TF } } // RGBA + { { FILTER_888, FILTER_888F }, { FILTER_888T, FILTER_888TF } }, // RGBA + { { FILTER_A16, null }, { FILTER_A16T, null} } // A16 }; diff --git a/ecere/src/gfx/drivers/lfbConvert.ec b/ecere/src/gfx/drivers/lfbConvert.ec index e08747d4f5..731d34771b 100644 --- a/ecere/src/gfx/drivers/lfbConvert.ec +++ b/ecere/src/gfx/drivers/lfbConvert.ec @@ -423,15 +423,23 @@ static void BMPRGBA_A(LFBSystem lfbSystem, Bitmap src, Bitmap dst) CONVERT(pixel.a, ColorRGBA, byte) } +static void BMPA16_888(LFBSystem lfbSystem, Bitmap src, Bitmap dst) +{ + uint c; + uint16 pixel; + CONVERT((ColorAlpha { (byte)(pixel * 255 / 65535), white }), uint16, ColorAlpha) +} + void (* converters_table[PixelFormat][PixelFormat]) (LFBSystem, Bitmap src, Bitmap dst) = { - { null,null,null,null,null,null,null, null }, - { null, BMP8_8, BMP8_444, BMP8_555, BMP8_565, BMP8_888, null, null, BMP8_RGBA }, - { null, BMP444_8, null, BMP444_555, BMP444_565, BMP444_888, null, null }, - { null, BMP555_8, BMP555_444, null, BMP555_565, BMP555_888, null, null }, - { null, BMP565_8, BMP565_444, BMP565_555, null, BMP565_888, null, null }, - { null, BMP888_8, BMP888_444, BMP888_555, BMP888_565, null, BMP888_A, null, BMP888_RGBA }, - { null, null, null, null, null, BMPA_888, null, null }, - { null,null,null,null,null,null,null, null }, - { null,BMPRGBA_8,null,BMPRGBA_555,BMPRGBA_565,BMPRGBA_888,BMPRGBA_A, null } + { null,null,null,null,null,null,null, null, null }, + { null, BMP8_8, BMP8_444, BMP8_555, BMP8_565, BMP8_888, null, null, BMP8_RGBA, null }, + { null, BMP444_8, null, BMP444_555, BMP444_565, BMP444_888, null, null, null }, + { null, BMP555_8, BMP555_444, null, BMP555_565, BMP555_888, null, null, null }, + { null, BMP565_8, BMP565_444, BMP565_555, null, BMP565_888, null, null, null }, + { null, BMP888_8, BMP888_444, BMP888_555, BMP888_565, null, BMP888_A, null, BMP888_RGBA, null }, + { null, null, null, null, null, BMPA_888, null, null, null }, + { null,null,null,null,null,null,null, null, null }, + { null,BMPRGBA_8,null,BMPRGBA_555,BMPRGBA_565,BMPRGBA_888,BMPRGBA_A, null, null }, + { null,null,null,null,null,BMPA16_888,null, null, null } }; diff --git a/ecere/src/gfx/fontManagement.ec b/ecere/src/gfx/fontManagement.ec index 2b3f7140de..33eabdba38 100644 --- a/ecere/src/gfx/fontManagement.ec +++ b/ecere/src/gfx/fontManagement.ec @@ -4,9 +4,13 @@ namespace gfx; #if defined(__WIN32__) #define WIN32_LEAN_AND_MEAN - #define String _String + #define String String_ + #define Size Size_ + #define Alignment Alignment_ #include #undef String + #undef Size + #undef Alignment #elif !defined(ECERE_NOTRUETYPE) && !defined(ECERE_NOFONTCONFIG) #define set _set #include @@ -14,7 +18,7 @@ namespace gfx; #undef set #endif -#if defined(__WIN32__) && !defined(ECERE_NOTRUETYPE) +#if defined(__WIN32__) && !defined(ECERE_NOTRUETYPE) && !defined(__UWP__) struct FontData { char fileName[MAX_FILENAME]; @@ -84,6 +88,7 @@ static int CALLBACK MyFontProc(ENUMLOGFONTEX * font, NEWTEXTMETRICEX *lpntme, in public class FaceInfo : struct { +public: String fileName; bool fakeItalic; int fontID; @@ -112,6 +117,11 @@ public Array ResolveFont(const String faceName, float size, FontFlags linkCfg = FileOpen(linkCfgPath, read); } #endif + +#if defined(__ANDROID__) + if(SearchString(faceName, 0, "Arial", false, false)) + faceName = flags.bold ? "/sdcard/fonts/Arial-Unicode-Bold.ttf" : "/sdcard/fonts/Arial-Unicode-Regular.ttf"; +#endif strcpy(fileName, faceName); if(!FileExists(fileName)) @@ -156,7 +166,7 @@ public Array ResolveFont(const String faceName, float size, FontFlags } } -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) if(!FileExists(fileName)) { FontData fontData = { { 0 } }; @@ -263,7 +273,7 @@ public Array ResolveFont(const String faceName, float size, FontFlags { char links[1024] = ""; int linksPos = 0; -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) HKEY key; links[0] = 0; if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink",0,KEY_READ,&key) || @@ -276,7 +286,7 @@ public Array ResolveFont(const String faceName, float size, FontFlags memset(links + size, 0, 1024 - size); RegCloseKey(key); } -#else +#elif !defined(__UWP__) links[0] = 0; if(linkCfg) { @@ -327,7 +337,7 @@ public Array ResolveFont(const String faceName, float size, FontFlags fontName[c] = 0; if(fontName[0] || ch == ',') { -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) GetWindowsDirectory(fileName, MAX_LOCATION); PathCat(fileName, "fonts"); PathCat(fileName, fontName); @@ -410,7 +420,7 @@ public struct FontInfo bool defaultOrAnsiCharSet; }; -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) static int CALLBACK fontLister(ENUMLOGFONTEXW * font, NEWTEXTMETRICEX *lpntme, int fontType, LPARAM lParam) { // const String faceName = font->elfLogFont.lfFaceName; @@ -432,7 +442,7 @@ public Map ListAvailableFonts() { Map fonts { }; -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) LOGFONTW logFont = { 0 }; HDC hdc = GetDC(0); logFont.lfCharSet = DEFAULT_CHARSET; diff --git a/ecere/src/gfx/fontRendering.ec b/ecere/src/gfx/fontRendering.ec index 3d82d01c08..a615432fa5 100644 --- a/ecere/src/gfx/fontRendering.ec +++ b/ecere/src/gfx/fontRendering.ec @@ -2,7 +2,7 @@ namespace gfx; import "fontManagement" -#if (defined(ECERE_VANILLA) || defined(ECERE_ONEDRIVER)) && defined(__WIN32__) +#if (defined(ECERE_VANILLA) || defined(ECERE_ONEDRIVER) || defined(ECERE_NO3D)) && defined(__WIN32__) #define ECERE_NOTRUETYPE #endif @@ -32,10 +32,14 @@ static uint16 * utf16 = null; #if !defined(ECERE_VANILLA) import "imgDistMap" + +#if !defined(ECERE_NOGL) import "immediate" #include "gl123es.h" #endif +#endif + #define MAX_FONT_LINK_ENTRIES 10 static HB_Script theCurrentScript; @@ -694,7 +698,7 @@ class GlyphPack : BTNode if(displaySystem && displaySystem.pixelFormat != pixelFormat4) // TODO: Add none PixelFormat { displaySystem.Lock(); -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) // Is this check still required? if(displaySystem.driver == class(OpenGLDisplayDriver) @@ -705,14 +709,14 @@ class GlyphPack : BTNode ) #endif { -#if !defined(ECERE_VANILLA) +#if !defined(ECERE_VANILLA) && !defined(ECERE_NOGL) if(displaySystem.driver == class(OpenGLDisplayDriver) && lastBlitTex) GLEnd(); #endif bitmap.MakeDD(displaySystem); if(outline) outline.MakeDD(displaySystem); -#if !defined(ECERE_VANILLA) +#if !defined(ECERE_VANILLA) && !defined(ECERE_NOGL) if(displaySystem.driver == class(OpenGLDisplayDriver) && lastBlitTex) GLBegin(GLIMTKMode::quads); #endif diff --git a/ecere/src/gfx/newFonts/atlasBuilder.ec b/ecere/src/gfx/newFonts/atlasBuilder.ec new file mode 100644 index 0000000000..183c125ab3 --- /dev/null +++ b/ecere/src/gfx/newFonts/atlasBuilder.ec @@ -0,0 +1,227 @@ +/* ***************************************************************************** + * Original Version Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +import "instance" + +namespace gfx; + +struct AtlasNode { short x, y, width; }; + +class AtlasBuilder +{ + int width, height; + AtlasNode *nodes; + int nodecount; + int nodealloc; + + // Create atlas of given width and height, internal node count will grow as necessary + bool create( int w, int h, int nodealloc ) + { + bool result = false; + + width = w; + height = h; + + // Allocate space for skyline nodes + if( nodealloc < 32 ) + nodealloc = 32; + if( ( nodes = new0 AtlasNode[nodealloc] ) ) + { + nodecount = 0; + this.nodealloc = nodealloc; + + // Init root node. + nodes[0].width = (short)w; + nodecount++; + + result = true; + } + return result; + } + + ~AtlasBuilder( ) + { + delete nodes; + } + + static bool insertNode( int nodeindex, int x, int y, int width ) + { + bool result = false; + + // Insert node + if( nodecount >= nodealloc ) + { + nodealloc <<= 1; + nodes = renew nodes AtlasNode[nodealloc]; + } + if(nodes) + { + int i; + for( i = nodecount; i > nodeindex ; i-- ) + nodes[i] = nodes[i-1]; + nodes[nodeindex].x = (short)x; + nodes[nodeindex].y = (short)y; + nodes[nodeindex].width = (short)width; + nodecount++; + + result = true; + } + return result; + } + + static void removeNode( int nodeindex ) + { + if( nodecount) + { + int i; + for( i = nodeindex ; i < nodecount-1 ; i++ ) + nodes[i] = nodes[i+1]; + nodecount--; + } + } + + static bool addSkylineLevel( int nodeindex, int x, int y, int width, int height ) + { + int i, shrink; + + // Insert new node + if( insertNode( nodeindex, x, y+height, width ) == 0 ) + return false; + + // Delete skyline segments that fall under the shadow of the new segment. + for( i = nodeindex+1 ; i < nodecount ; i++ ) + { + if( nodes[i].x < ( nodes[i-1].x + nodes[i-1].width ) ) + { + shrink = nodes[i-1].x + nodes[i-1].width - nodes[i].x; + nodes[i].x += (short)shrink; + nodes[i].width -= (short)shrink; + if( nodes[i].width <= 0 ) + { + removeNode( i ); + i--; + } + else + break; + } + else + break; + } + + // Merge same height skyline segments that are next to each other. + for( i = 0 ; i < nodecount - 1 ; ) + { + if( nodes[i].y != nodes[i+1].y ) + i++; + else + { + nodes[i].width += nodes[i+1].width; + removeNode( i+1 ); + } + } + return 1; + } + + static int rectFits( int nodeindex, int width, int height ) + { + int x, y, rem; + /* Checks if there is enough space at the location of skyline span 'i', */ + /* and return the max height of all skyline spans under that at that location, */ + /* (think tetris block being dropped at that position). Or -1 if no space found. */ + x = nodes[nodeindex].x; + y = nodes[nodeindex].y; + if( ( x + width ) > this.width ) + return -1; + for( rem = width ; rem > 0 ; nodeindex++ ) + { + if( nodeindex == nodecount ) + return -1; + if( y < this.nodes[nodeindex].y ) + y = nodes[nodeindex].y; + if( ( y + height ) > this.height ) + return -1; + rem -= nodes[nodeindex].width; + } + return y; + } + + // Place a rectangle of specified dimensions, return 1 on success, retx&rety store offsets + bool addRect( int width, int height, int *retx, int *rety ) + { + int besth, bestw, besti, bestx, besty, y, nodeindex; + + besth = this.height; + bestw = this.width; + besti = -1; + bestx = -1; + besty = -1; + + // Bottom left fit heuristic. + for( nodeindex = 0 ; nodeindex < this.nodecount ; nodeindex++ ) + { + y = rectFits( nodeindex, width, height ); + if( y != -1 ) + { + if( ( ( y + height ) < besth ) || ( ( ( y + height ) == besth ) && ( this.nodes[nodeindex].width < bestw ) ) ) + { + besti = nodeindex; + bestw = this.nodes[nodeindex].width; + besth = y + height; + bestx = this.nodes[nodeindex].x; + besty = y; + } + } + } + + if( besti == -1 ) + return 0; + + // Perform the actual packing. + if( addSkylineLevel( besti, bestx, besty, width, height ) == 0 ) + return 0; + + *retx = bestx; + *rety = besty; + + return 1; + } + + // Expand atlas to given dimensions + void expand( int width, int height ) + { + // Insert node for empty space + if( width > this.width ) + insertNode( nodecount, this.width, 0, width - this.width ); + this.width = width; + this.height = height; + } + + // Clean up the atlas + void reset( int width, int height ) + { + this.width = width; + this.height = height; + nodecount = 0; + + // Init root node. + nodes[0].x = 0; + nodes[0].y = 0; + nodes[0].width = (short)width; + nodecount++; + } + + // Return the maximum y value of all allocated rectangles + int getAtlasMaxHeight() + { + int i, maxy = 0; + + for( i = 0 ; i < nodecount ; i++ ) + { + if( maxy < nodes[i].y ) + maxy = nodes[i].y; + } + return maxy; + } +} diff --git a/ecere/src/gfx/newFonts/cc/cc.c b/ecere/src/gfx/newFonts/cc/cc.c new file mode 100644 index 0000000000..0746b7e878 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/cc.c @@ -0,0 +1,2062 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "cc.h" +#include "ccstr.h" + +#if CC_UNIX + #include + #include + #include + #include + #include + #include /* For uname() */ + #include /* For readdir() */ + #include /* For statvfs( ) */ +#elif CC_WINDOWS + #define WIN32_LEAN_AND_MEAN + #include + #include + #include + #include +#else + #error Unknown/Unsupported platform! +#endif + + +#if defined(__linux__) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__i386__) + +asm(".symver __xstat,__xstat@GLIBC_2.2.5"); + +int stat_glibcwrapper(const char *fn, struct stat * buf); +#define stat(a, b) stat_glibcwrapper(a, b) + +#endif + +//// + + + +const size_t ccTypeSize[CC_TYPE_COUNT] = +{ + [CC_TYPE_UINT8] = sizeof(uint8_t), + [CC_TYPE_INT8] = sizeof(int8_t), + [CC_TYPE_UINT16] = sizeof(uint16_t), + [CC_TYPE_INT16] = sizeof(int16_t), + [CC_TYPE_UINT32] = sizeof(uint32_t), + [CC_TYPE_INT32] = sizeof(int32_t), + [CC_TYPE_UINT64] = sizeof(uint64_t), + [CC_TYPE_INT64] = sizeof(int64_t), + [CC_TYPE_FLOAT] = sizeof(float), + [CC_TYPE_DOUBLE] = sizeof(double) +}; + + +//// + + +#define CC_HASH_READ8(d,o) ((uint32_t)(((uint8_t *)d)[o])) +#define CC_HASH_AREAD16(d,o) ((uint32_t)(*((uint16_t *)ADDRESS(d,o)))) +#define CC_HASH_UREAD16(d,o) ((((uint32_t)(((uint8_t *)(d))[o+1]))<<8)+(uint32_t)(((uint8_t *)(d))[o])) + +uint32_t ccHash32Data( void *data, int size ) +{ + uint32_t hash; + int rem; + rem = size & 3; + size >>= 2; + hash = 0; + if( !( ( (uintptr_t)data ) & 0x1 ) ) + { + for( ; size ; size-- ) + { + hash += CC_HASH_AREAD16( data, 0 ); + hash = ( hash << 16 ) ^ ( ( CC_HASH_AREAD16( data, 2 ) << 11 ) ^ hash ); + hash += hash >> 11; + data = ADDRESS( data, 4 ); + } + } + else + { + for( ; size ; size-- ) + { + hash += CC_HASH_UREAD16( data, 0 ); + hash = ( hash << 16 ) ^ ( ( CC_HASH_UREAD16( data, 2 ) << 11 ) ^ hash ); + hash += hash >> 11; + data = ADDRESS( data, 4 ); + } + } + switch( rem ) + { + case 3: + hash += CC_HASH_UREAD16( data, 0 ); + hash ^= hash << 16; + hash ^= CC_HASH_READ8( data, 2 ) << 18; + hash += hash >> 11; + break; + case 2: + hash += CC_HASH_UREAD16( data, 0 ); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: + hash += CC_HASH_READ8( data, 0 ); + hash ^= hash << 10; + hash += hash >> 1; + break; + case 0: + break; + } + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +uint32_t ccHash32Int32( uint32_t i ) +{ + uint32_t hash; + hash = i & 0xFFFF; + hash = ( ( hash << 16 ) ^ hash ) ^ ( ( i & 0xFFFF0000 ) >> 5 ); + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +uint32_t ccHash32Int64( uint64_t i ) +{ + uint32_t hash; + hash = i & 0xFFFF; + hash = ( ( hash << 16 ) ^ hash ) ^ ( ( (uint32_t)( i >> 16 ) & 0xFFFF ) << 11 ); + hash += ( hash >> 11 ) + ( (uint32_t)( i >> 32 ) & 0xFFFF ); + hash = ( ( hash << 16 ) ^ hash ) ^ (uint32_t)( ( i >> 37 ) & 0x7FFF800 ); + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +uint32_t ccHash32Array32( uint32_t *data, int count ) +{ + uint32_t hash; + hash = 0; + for( ; count ; count-- ) + { + hash += *data & 0xFFFF; + hash = ( ( hash << 16 ) ^ hash ) ^ ( ( *data & 0xFFFF0000 ) >> 5 ); + hash += hash >> 11; + data = ADDRESS( data, 4 ); + } + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +uint32_t ccHash32Array64( uint64_t *data, int count ) +{ + uint32_t hash; + uint64_t v; + hash = 0; + for( ; count ; count-- ) + { + v = *data; + hash += v & 0xFFFF; + hash = ( ( hash << 16 ) ^ hash ) ^ ( ( (uint32_t)( v >> 16 ) & 0xFFFF ) << 11 ); + hash += ( hash >> 11 ) + ( (uint32_t)( v >> 32 ) & 0xFFFF ); + hash = ( ( hash << 16 ) ^ hash ) ^ (uint32_t)( ( v >> 37 ) & 0x7FFF800 ); + hash += hash >> 11; + data = ADDRESS( data, 8 ); + } + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + + +//// + + +int ccMemCmp( void *s0, void *s1, int size ) +{ + int i; + uint8_t *t0, *t1; + t0 = s0; + t1 = s1; + for( i = 0 ; i < size ; i++ ) + { + if( t0[i] != t1[i] ) + return 0; + } + return 1; +} + +int ccMemCmp32( uint32_t *s0, uint32_t *s1, int count ) +{ + int i; + for( i = 0 ; i < count ; i++ ) + { + if( s0[i] != s1[i] ) + return 0; + } + return 1; +} + +int ccMemCmp64( uint64_t *s0, uint64_t *s1, int count ) +{ + int i; + for( i = 0 ; i < count ; i++ ) + { + if( s0[i] != s1[i] ) + return 0; + } + return 1; +} + +int ccMemCmpSize( void *s0, void *s1, int size ) +{ + int i; + uint8_t *t0, *t1; + t0 = s0; + t1 = s1; + for( i = 0 ; i < size ; i++ ) + { + if( t0[i] != t1[i] ) + break; + } + return i; +} + + +//// + + +uint8_t ccLog2Int8( uint8_t v ) +{ + uint8_t r = 0; + if( v & 0xC ) + { + v >>= 2; + r |= 2; + } + if( v & 0x2 ) + { + v >>= 1; + r |= 1; + } + return r; +} + +uint16_t ccLog2Int16( uint16_t v ) +{ + uint16_t r = 0; + if( v & 0xFF00 ) + { + v >>= 8; + r |= 8; + } + if( v & 0xF0 ) + { + v >>= 4; + r |= 4; + } + if( v & 0xC ) + { + v >>= 2; + r |= 2; + } + if( v & 0x2 ) + { + v >>= 1; + r |= 1; + } + return r; +} + +uint32_t ccLog2Int32( uint32_t v ) +{ + uint32_t r = 0; + if( v & 0xFFFF0000 ) + { + v >>= 16; + r |= 16; + } + if( v & 0xFF00 ) + { + v >>= 8; + r |= 8; + } + if( v & 0xF0 ) + { + v >>= 4; + r |= 4; + } + if( v & 0xC ) + { + v >>= 2; + r |= 2; + } + if( v & 0x2 ) + { + v >>= 1; + r |= 1; + } + return r; +} + +uint64_t ccLog2Int64( uint64_t v ) +{ + uint64_t r = 0; + if( v & 0xFFFFFFFF00000000LL ) + { + v >>= 32; + r |= 32; + } + if( v & 0xFFFF0000 ) + { + v >>= 16; + r |= 16; + } + if( v & 0xFF00 ) + { + v >>= 8; + r |= 8; + } + if( v & 0xF0 ) + { + v >>= 4; + r |= 4; + } + if( v & 0xC ) + { + v >>= 2; + r |= 2; + } + if( v & 0x2 ) + { + v >>= 1; + r |= 1; + } + return r; +} + + +//// + + +#define CC_SORT_SWAP(a,b) ({temp=table[b];table[b]=table[a];table[a]=temp;}) + +#define CC_SORT_STACK_DEPTH (512) + +#define CC_SORT_MIN_QSORT_COUNT (5) + +typedef struct +{ + void *table; + int count; +} ccQuickSortStack; + +static void ccQuickSortPart( void **table, int count, int (*sortfunc)( void *t0, void *t1 ) ) +{ + void *temp; + switch( count ) + { + case 4: + if( sortfunc( table[0], table[1] ) ) + CC_SORT_SWAP( 1, 0 ); + if( sortfunc( table[2], table[3] ) ) + CC_SORT_SWAP( 3, 2 ); + if( sortfunc( table[0], table[2] ) ) + { + temp = table[2]; + table[2] = table[1]; + table[1] = table[0]; + table[0] = temp; + if( sortfunc( table[2], table[3] ) ) + { + CC_SORT_SWAP( 3, 2 ); + if( sortfunc( table[1], table[2] ) ) + CC_SORT_SWAP( 2, 1 ); + } + } + else + { + if( sortfunc( table[1], table[2] ) ) + { + CC_SORT_SWAP( 2, 1 ); + if( sortfunc( table[2], table[3] ) ) + CC_SORT_SWAP( 3, 2 ); + } + } + break; + case 3: + if( sortfunc( table[0], table[1] ) ) + { + if( sortfunc( table[1], table[2] ) ) + { + /* [1]>[0], [2]>[1] = [2]>[1]>[0] */ + CC_SORT_SWAP( 2, 0 ); + } + else + { + if( sortfunc( table[0], table[2] ) ) + { + /* [1]>[0], [2]<[1], [2]>[0] = [1]>[2]>[0] */ + temp = table[0]; + table[0] = table[1]; + table[1] = table[2]; + table[2] = temp; + } + else + { + /* [1]>[0], [2]<[1], [2]<[0] = [1]>[0]>[2] */ + CC_SORT_SWAP( 1, 0 ); + } + } + } + else + { + if( sortfunc( table[1], table[2] ) ) + { + if( sortfunc( table[0], table[2] ) ) + { + /* [1]<[0], [2]>[1], [2]>[0] = [2]>[0]>[1] */ + temp = table[2]; + table[2] = table[1]; + table[1] = table[0]; + table[0] = temp; + } + else + { + /* [1]<[0], [2]>[1], [2]<[0] = [0]>[2]>[1] */ + CC_SORT_SWAP( 1, 2 ); + } + } + else + { + /* [1]<[0], [2]<[1] = [0]>[1]>[2] */ + } + } + break; + case 2: + if( sortfunc( table[0], table[1] ) ) + CC_SORT_SWAP( 1, 0 ); + break; + case 1: + case 0: + default: + break; + } + return; +} + + +void ccQuickSort( void **table, int count, int (*sortfunc)( void *t0, void *t1 ), uint32_t randmask ) +{ + ssize_t i, pivotindex, leftcount, rightcount, highindex, pivotstore; + void *temp; + void *pivot; + ccQuickSortStack stack[CC_SORT_STACK_DEPTH]; + ccQuickSortStack *sp; + + if( count < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortPart( table, count, sortfunc ); + return; + } + + sp = stack; + for( ; ; ) + { + /* Select pivot */ + randmask += count; + pivotindex = 1 + ( randmask % ( count-2 ) ); + + if( sortfunc( table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + if( sortfunc( table[pivotindex], table[count-1] ) ) + { + CC_SORT_SWAP( count-1, pivotindex ); + if( sortfunc( table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + } + + /* Quick sort on both sides of the pivot */ + pivot = table[pivotindex]; + highindex = count - 2; + pivotstore = highindex; + CC_SORT_SWAP( pivotstore, pivotindex ); + pivotindex = 1; + for( i = highindex ; --i ; ) + { + if( sortfunc( pivot, table[pivotindex] ) ) + pivotindex++; + else + { + highindex--; + CC_SORT_SWAP( highindex, pivotindex ); + } + } + CC_SORT_SWAP( pivotindex, pivotstore ); + + /* Count of entries on both sides of the pivot */ + leftcount = pivotindex; + pivotindex++; + rightcount = count - pivotindex; + + /* Fast sort small chunks, iterate */ + if( leftcount < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortPart( table, leftcount, sortfunc ); + table += pivotindex; + count = rightcount; + if( rightcount < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortPart( table, count, sortfunc ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + } + } + else if( rightcount < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortPart( &table[pivotindex], rightcount, sortfunc ); + count = leftcount; + } + else if( leftcount < rightcount ) + { + sp->table = &table[pivotindex]; + sp->count = rightcount; + sp++; + count = leftcount; + } + else + { + sp->table = table; + sp->count = leftcount; + sp++; + table += pivotindex; + count = rightcount; + } + } + + return; +} + +static void ccQuickSortContextPart( void **table, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context ) +{ + void *temp; + switch( count ) + { + case 4: + if( sortfunc( context, table[0], table[1] ) ) + CC_SORT_SWAP( 1, 0 ); + if( sortfunc( context, table[2], table[3] ) ) + CC_SORT_SWAP( 3, 2 ); + if( sortfunc( context, table[0], table[2] ) ) + { + temp = table[2]; + table[2] = table[1]; + table[1] = table[0]; + table[0] = temp; + if( sortfunc( context, table[2], table[3] ) ) + { + CC_SORT_SWAP( 3, 2 ); + if( sortfunc( context, table[1], table[2] ) ) + CC_SORT_SWAP( 2, 1 ); + } + } + else + { + if( sortfunc( context, table[1], table[2] ) ) + { + CC_SORT_SWAP( 2, 1 ); + if( sortfunc( context, table[2], table[3] ) ) + CC_SORT_SWAP( 3, 2 ); + } + } + break; + case 3: + if( sortfunc( context, table[0], table[1] ) ) + { + if( sortfunc( context, table[1], table[2] ) ) + { + /* [1]>[0], [2]>[1] = [2]>[1]>[0] */ + CC_SORT_SWAP( 2, 0 ); + } + else + { + if( sortfunc( context, table[0], table[2] ) ) + { + /* [1]>[0], [2]<[1], [2]>[0] = [1]>[2]>[0] */ + temp = table[0]; + table[0] = table[1]; + table[1] = table[2]; + table[2] = temp; + } + else + { + /* [1]>[0], [2]<[1], [2]<[0] = [1]>[0]>[2] */ + CC_SORT_SWAP( 1, 0 ); + } + } + } + else + { + if( sortfunc( context, table[1], table[2] ) ) + { + if( sortfunc( context, table[0], table[2] ) ) + { + /* [1]<[0], [2]>[1], [2]>[0] = [2]>[0]>[1] */ + temp = table[2]; + table[2] = table[1]; + table[1] = table[0]; + table[0] = temp; + } + else + { + /* [1]<[0], [2]>[1], [2]<[0] = [0]>[2]>[1] */ + CC_SORT_SWAP( 1, 2 ); + } + } + else + { + /* [1]<[0], [2]<[1] = [0]>[1]>[2] */ + } + } + break; + case 2: + if( sortfunc( context, table[0], table[1] ) ) + CC_SORT_SWAP( 1, 0 ); + break; + case 1: + case 0: + default: + break; + } + return; +} + +void ccQuickSortContext( void **table, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context, uint32_t randmask ) +{ + ssize_t i, pivotindex, leftcount, rightcount, highindex, pivotstore; + void *temp; + void *pivot; + ccQuickSortStack stack[CC_SORT_STACK_DEPTH]; + ccQuickSortStack *sp; + + if( count < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortContextPart( table, count, sortfunc, context ); + return; + } + + sp = stack; + for( ; ; ) + { + /* Select pivot */ + randmask += count; + pivotindex = 1 + ( randmask % ( count-2 ) ); + + if( sortfunc( context, table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + if( sortfunc( context, table[pivotindex], table[count-1] ) ) + { + CC_SORT_SWAP( count-1, pivotindex ); + if( sortfunc( context, table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + } + + /* Quick sort on both sides of the pivot */ + pivot = table[pivotindex]; + highindex = count - 2; + pivotstore = highindex; + CC_SORT_SWAP( pivotstore, pivotindex ); + pivotindex = 1; + for( i = highindex ; --i ; ) + { + if( sortfunc( context, pivot, table[pivotindex] ) ) + pivotindex++; + else + { + highindex--; + CC_SORT_SWAP( highindex, pivotindex ); + } + } + CC_SORT_SWAP( pivotindex, pivotstore ); + + /* Count of entries on both sides of the pivot */ + leftcount = pivotindex; + pivotindex++; + rightcount = count - pivotindex; + + /* Fast sort small chunks, iterate */ + if( leftcount < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortContextPart( table, leftcount, sortfunc, context ); + table += pivotindex; + count = rightcount; + if( rightcount < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortContextPart( table, count, sortfunc, context ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + } + } + else if( rightcount < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortContextPart( &table[pivotindex], rightcount, sortfunc, context ); + count = leftcount; + } + else if( leftcount < rightcount ) + { + sp->table = &table[pivotindex]; + sp->count = rightcount; + sp++; + count = leftcount; + } + else + { + sp->table = table; + sp->count = leftcount; + sp++; + table += pivotindex; + count = rightcount; + } + } + + return; +} + + +//// + + +typedef struct +{ + void **src; + void **dst; + int count; + int mergeflag; + int depthbit; +} ccMergeSortStack; + +int ccMergeSort( void **src, void **tmp, int count, int (*sortfunc)( void *t0, void *t1 ) ) +{ + int swapflag, depthbit, maxdepthbit; + ssize_t i, leftcount, rightcount; + void **dst, **sl, **sr, *temp, **swap; + ccMergeSortStack stack[CC_SORT_STACK_DEPTH]; + ccMergeSortStack *sp; + + dst = tmp; + sp = stack; + swapflag = 0; + depthbit = 0; + + if( count <= 1 ) + return 0; + + leftcount = count; + for( maxdepthbit = 1 ; ; maxdepthbit ^= 1 ) + { + leftcount = leftcount - ( leftcount >> 1 ); + if( leftcount <= 4 ) + break; + } + + for( ; ; ) + { + if( count <= 4 ) + { + if( !( depthbit ^ maxdepthbit ) ) + { + if( ( count == 4 ) && sortfunc( src[2], src[3] ) ) + { + temp = src[2]; + src[2] = src[3]; + src[3] = temp; + } + if( sortfunc( src[0], src[1] ) ) + { + temp = src[0]; + src[0] = src[1]; + src[1] = temp; + } + swapflag = 0; + } + else + { + if( count == 4 ) + { + if( sortfunc( src[2], src[3] ) ) + { + dst[2] = src[3]; + dst[3] = src[2]; + } + else + { + dst[2] = src[2]; + dst[3] = src[3]; + } + } + else if( count == 3 ) + dst[2] = src[2]; + if( sortfunc( src[0], src[1] ) ) + { + dst[0] = src[1]; + dst[1] = src[0]; + } + else + { + dst[0] = src[0]; + dst[1] = src[1]; + } + swap = src; + src = dst; + dst = swap; + swapflag = 1; + } + } + else + { + rightcount = count >> 1; + leftcount = count - rightcount; + sp->src = src; + sp->dst = dst; + sp->count = count; + sp->mergeflag = 1; + sp->depthbit = depthbit; + depthbit ^= 1; + sp++; + sp->src = src + leftcount; + sp->dst = dst + leftcount; + sp->count = rightcount; + sp->mergeflag = 0; + sp->depthbit = depthbit; + sp++; + count = leftcount; + continue; + } + + for( ; ; ) + { + rightcount = count >> 1; + leftcount = count - rightcount; + sl = src; + sr = src + leftcount; + for( ; ; ) + { + if( sortfunc( *sl, *sr ) ) + { + *dst++ = *sr++; + if( --rightcount ) + continue; + for( i = 0 ; i < leftcount ; i++ ) + dst[i] = sl[i]; + break; + } + else + { + *dst++ = *sl++; + if( --leftcount ) + continue; + for( i = 0 ; i < rightcount ; i++ ) + dst[i] = sr[i]; + break; + } + } + + if( sp == stack ) + return swapflag ^ 1; + sp--; + src = sp->src; + dst = sp->dst; + count = sp->count; + depthbit = sp->depthbit; + if( !( sp->mergeflag ) ) + break; + swapflag ^= 1; + if( swapflag ) + { + src = sp->dst; + dst = sp->src; + } + } + } + + return 0; +} + +int ccMergeSortContext( void **src, void **tmp, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context ) +{ + int swapflag, depthbit, maxdepthbit; + ssize_t i, leftcount, rightcount; + void **dst, **sl, **sr, *temp, **swap; + ccMergeSortStack stack[CC_SORT_STACK_DEPTH]; + ccMergeSortStack *sp; + + dst = tmp; + sp = stack; + swapflag = 0; + depthbit = 0; + + if( count <= 1 ) + return 0; + + leftcount = count; + for( maxdepthbit = 1 ; ; maxdepthbit ^= 1 ) + { + leftcount = leftcount - ( leftcount >> 1 ); + if( leftcount <= 4 ) + break; + } + + for( ; ; ) + { + if( count <= 4 ) + { + if( !( depthbit ^ maxdepthbit ) ) + { + if( ( count == 4 ) && sortfunc( context, src[2], src[3] ) ) + { + temp = src[2]; + src[2] = src[3]; + src[3] = temp; + } + if( sortfunc( context, src[0], src[1] ) ) + { + temp = src[0]; + src[0] = src[1]; + src[1] = temp; + } + swapflag = 0; + } + else + { + if( count == 4 ) + { + if( sortfunc( context, src[2], src[3] ) ) + { + dst[2] = src[3]; + dst[3] = src[2]; + } + else + { + dst[2] = src[2]; + dst[3] = src[3]; + } + } + else if( count == 3 ) + dst[2] = src[2]; + if( sortfunc( context, src[0], src[1] ) ) + { + dst[0] = src[1]; + dst[1] = src[0]; + } + else + { + dst[0] = src[0]; + dst[1] = src[1]; + } + swap = src; + src = dst; + dst = swap; + swapflag = 1; + } + } + else + { + rightcount = count >> 1; + leftcount = count - rightcount; + sp->src = src; + sp->dst = dst; + sp->count = count; + sp->mergeflag = 1; + sp->depthbit = depthbit; + depthbit ^= 1; + sp++; + sp->src = src + leftcount; + sp->dst = dst + leftcount; + sp->count = rightcount; + sp->mergeflag = 0; + sp->depthbit = depthbit; + sp++; + count = leftcount; + continue; + } + + for( ; ; ) + { + rightcount = count >> 1; + leftcount = count - rightcount; + sl = src; + sr = src + leftcount; + for( ; ; ) + { + if( sortfunc( context, *sl, *sr ) ) + { + *dst++ = *sr++; + if( --rightcount ) + continue; + for( i = 0 ; i < leftcount ; i++ ) + dst[i] = sl[i]; + break; + } + else + { + *dst++ = *sl++; + if( --leftcount ) + continue; + for( i = 0 ; i < rightcount ; i++ ) + dst[i] = sr[i]; + break; + } + } + + if( sp == stack ) + return swapflag ^ 1; + sp--; + src = sp->src; + dst = sp->dst; + count = sp->count; + depthbit = sp->depthbit; + if( !( sp->mergeflag ) ) + break; + swapflag ^= 1; + if( swapflag ) + { + src = sp->dst; + dst = sp->src; + } + } + } + + return 0; +} + + +//// + + +typedef struct +{ + void *table; + int count; + int depth; +} ccHybridSortStack; + +void ccHybridSort( void **table, void **tmp, int count, int (*sortfunc)( void *t0, void *t1 ), uint32_t randmask ) +{ + int msortindex, depth, depthmax; + ssize_t i, pivotindex, leftcount, rightcount, highindex, pivotstore; + void *temp; + void *pivot; + ccHybridSortStack stack[CC_SORT_STACK_DEPTH]; + ccHybridSortStack *sp; + + depth = 0; + depthmax = 1 + ccLog2Int32( count ); + + sp = stack; + for( ; ; ) + { + if( count < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortPart( table, count, sortfunc ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + depth = sp->depth; + continue; + } + + /* Pathological case, switch to merge sort */ + if( depth >= depthmax ) + { + msortindex = ccMergeSort( table, tmp, count, sortfunc ); + if( msortindex ) + memcpy( table, tmp, count * sizeof(void *) ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + depth = sp->depth; + continue; + } + + /* Select pivot */ + randmask += count; + pivotindex = 1 + ( randmask % ( count-2 ) ); + if( sortfunc( table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + if( sortfunc( table[pivotindex], table[count-1] ) ) + { + CC_SORT_SWAP( count-1, pivotindex ); + if( sortfunc( table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + } + + /* Quick sort on both sides of the pivot */ + pivot = table[pivotindex]; + highindex = count - 2; + pivotstore = highindex; + CC_SORT_SWAP( pivotstore, pivotindex ); + pivotindex = 1; + for( i = highindex ; --i ; ) + { + if( sortfunc( pivot, table[pivotindex] ) ) + pivotindex++; + else + { + highindex--; + CC_SORT_SWAP( highindex, pivotindex ); + } + } + CC_SORT_SWAP( pivotindex, pivotstore ); + + /* Count of entries on both sides of the pivot */ + leftcount = pivotindex; + pivotindex++; + rightcount = count - pivotindex; + + /* Fast sort small chunks, iterate */ + if( leftcount < rightcount ) + { + depth++; + sp->table = &table[pivotindex]; + sp->count = rightcount; + sp->depth = depth; + sp++; + count = leftcount; + } + else + { + depth++; + sp->table = table; + sp->count = leftcount; + sp->depth = depth; + sp++; + table += pivotindex; + count = rightcount; + } + } + + return; +} + + +void ccHybridSortContext( void **table, void **tmp, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context, uint32_t randmask ) +{ + int msortindex, depth, depthmax; + ssize_t i, pivotindex, leftcount, rightcount, highindex, pivotstore; + void *temp; + void *pivot; + ccHybridSortStack stack[CC_SORT_STACK_DEPTH]; + ccHybridSortStack *sp; + + depth = 0; + depthmax = 1 + ccLog2Int32( count ); + + sp = stack; + for( ; ; ) + { + if( count < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortContextPart( table, count, sortfunc, context ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + depth = sp->depth; + continue; + } + + /* Pathological case, switch to merge sort */ + if( depth >= depthmax ) + { + msortindex = ccMergeSortContext( table, tmp, count, sortfunc, context ); + if( msortindex ) + memcpy( table, tmp, count * sizeof(void *) ); + if( sp == stack ) + break; + sp--; + depth--; + table = sp->table; + count = sp->count; + depth = sp->depth; + continue; + } + + /* Select pivot */ + randmask += count; + pivotindex = 1 + ( randmask % ( count-2 ) ); + if( sortfunc( context, table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + if( sortfunc( context, table[pivotindex], table[count-1] ) ) + { + CC_SORT_SWAP( count-1, pivotindex ); + if( sortfunc( context, table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + } + + /* Quick sort on both sides of the pivot */ + pivot = table[pivotindex]; + highindex = count - 2; + pivotstore = highindex; + CC_SORT_SWAP( pivotstore, pivotindex ); + pivotindex = 1; + for( i = highindex ; --i ; ) + { + if( sortfunc( context, pivot, table[pivotindex] ) ) + pivotindex++; + else + { + highindex--; + CC_SORT_SWAP( highindex, pivotindex ); + } + } + CC_SORT_SWAP( pivotindex, pivotstore ); + + /* Count of entries on both sides of the pivot */ + leftcount = pivotindex; + pivotindex++; + rightcount = count - pivotindex; + + /* Fast sort small chunks, iterate */ + if( leftcount < rightcount ) + { + depth++; + sp->table = &table[pivotindex]; + sp->count = rightcount; + sp->depth = depth; + sp++; + count = leftcount; + } + else + { + depth++; + sp->table = table; + sp->count = leftcount; + sp->depth = depth; + sp++; + table += pivotindex; + count = rightcount; + } + } + + return; +} + + +//// + + +#define CC_DEBUG_LOG_SIZE (4096) + +void ccDebugLog( char *filename, char *string, ... ) +{ + int slen, bufsize; + char buffer[CC_DEBUG_LOG_SIZE]; + char *wbuf; + va_list ap; + FILE *file; + + if( !( file = fopen( filename, "a" ) ) ) + return; + + wbuf = buffer; + bufsize = CC_DEBUG_LOG_SIZE; + for( ; ; ) + { + va_start( ap, string ); + slen = vsnprintf( wbuf, bufsize, string, ap ); + va_end( ap ); +#if CC_WINDOWS + if( slen == -1 ) + slen = bufsize << 1; +#endif + if( slen < bufsize ) + break; + if( wbuf != buffer ) + free( wbuf ); + bufsize = slen + 2; + wbuf = malloc( bufsize ); + } + + fprintf( file, "%s", wbuf ); + + if( wbuf != buffer ) + free( wbuf ); + fclose( file ); + + return; +} + + +//// + + +void ccGrowthInit( ccGrowth *growth, int defaultsize ) +{ + growth->allocsize = CC_MAX( defaultsize, 512 ); + growth->offset = 0; + growth->data = malloc( growth->allocsize ); + return; +} + +int ccGrowthPrintf( ccGrowth *growth, char *format, ... ) +{ + int strsize, clampsize; + va_list ap; + + for( ; ; ) + { + va_start( ap, format ); + clampsize = growth->allocsize - growth->offset; + strsize = vsnprintf( (char *)ADDRESS( growth->data, growth->offset ), clampsize, format, ap ); + va_end( ap ); +#if CC_WINDOWS + if( strsize == -1 ) + strsize = growth->allocsize << 1; +#endif + if( strsize < clampsize ) + break; + growth->allocsize = CC_MAX( growth->offset + strsize + 1, growth->allocsize << 1 ); + growth->data = realloc( growth->data, growth->allocsize ); + } + growth->offset += strsize; + + return 1; +} + +int ccGrowthData( ccGrowth *growth, void *data, size_t size ) +{ + if( ( growth->offset + size ) >= growth->allocsize ) + { + growth->allocsize = CC_MAX( growth->offset + size, growth->allocsize << 1 ); + growth->data = realloc( growth->data, growth->allocsize ); + } + memcpy( ADDRESS( growth->data, growth->offset ), data, size ); + growth->offset += size; + return 1; +} + +int ccGrowthSeek( ccGrowth *growth, int offset ) +{ + if( offset >= growth->allocsize ) + { + growth->allocsize = CC_MAX( offset, growth->allocsize << 1 ); + growth->data = realloc( growth->data, growth->allocsize ); + } + if( offset > growth->offset ) + memset( ADDRESS( growth->data, growth->offset ), 0, offset - growth->offset ); + growth->offset = offset; + return 1; +} + +void ccGrowthFree( ccGrowth *growth ) +{ + free( growth->data ); + memset( growth, 0, sizeof(ccGrowth) ); + return; +} + +void ccGrowthElapsedTimeString( ccGrowth *growth, int64_t timecount, int maxfieldcount ) +{ + int fieldcount, unitcount; + fieldcount = 0; + if( timecount <= 0 ) + { + ccGrowthPrintf( growth, "Just now" ); + return; + } + if( timecount >= (24*60*60) ) + { + unitcount = timecount / (24*60*60); + timecount = timecount % (24*60*60); + ccGrowthPrintf( growth, "%d day%s", (int)unitcount, ( unitcount > 1 ? "s" : "" ) ); + fieldcount++; + if( fieldcount >= maxfieldcount ) + return; + } + if( timecount >= (60*60) ) + { + if( fieldcount ) + ccGrowthPrintf( growth, ", " ); + unitcount = timecount / (60*60); + timecount = timecount % (60*60); + ccGrowthPrintf( growth, "%d hour%s", (int)unitcount, ( unitcount > 1 ? "s" : "" ) ); + fieldcount++; + if( fieldcount >= maxfieldcount ) + return; + } + if( timecount >= (60) ) + { + if( fieldcount ) + ccGrowthPrintf( growth, ", " ); + unitcount = timecount / (60); + timecount = timecount % (60); + ccGrowthPrintf( growth, "%d minute%s", (int)unitcount, ( unitcount > 1 ? "s" : "" ) ); + fieldcount++; + if( fieldcount >= maxfieldcount ) + return; + } + if( timecount >= 1 ) + { + if( fieldcount ) + ccGrowthPrintf( growth, ", " ); + unitcount = timecount; + ccGrowthPrintf( growth, "%d second%s", (int)unitcount, ( unitcount > 1 ? "s" : "" ) ); + fieldcount++; + if( fieldcount >= maxfieldcount ) + return; + } + if( !( fieldcount ) ) + ccGrowthPrintf( growth, "Long, long" ); + return; +} + + + +//// + + +void *ccFileLoad( const char *path, size_t maxsize, size_t *retsize ) +{ + FILE *file; + size_t size; + char *data; + + file = fopen( path, "rb" ); + if( !( file ) ) + return 0; + fseek( file, 0, SEEK_END ); + size = ftell( file ); + fseek( file, 0, SEEK_SET ); + if( ( maxsize ) && ( size > maxsize ) ) + { + fclose( file ); + return 0; + } + data = malloc( size + 1 ); + data[size] = 0; + if( fread( data, size, 1, file ) != 1 ) + { + free( data ); + data = 0; + } + fclose( file ); + if( retsize ) + *retsize = size; + + return data; +} + + +size_t ccFileLoadDirect( const char *path, void *data, size_t minsize, size_t maxsize ) +{ + FILE *file; + size_t size; + + file = fopen( path, "rb" ); + if( !( file ) ) + return 0; + fseek( file, 0, SEEK_END ); + size = ftell( file ); + fseek( file, 0, SEEK_SET ); + if( ( size < minsize ) || ( size > maxsize ) ) + size = 0; + else if( fread( data, size, 1, file ) != 1 ) + size = 0; + fclose( file ); + + return size; +} + + +int ccFileStore( const char *path, void *data, size_t datasize, int fsyncflag ) +{ + int retval; +#if CC_UNIX + int fd; + if( ( fd = open( path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR ) ) == -1 ) + return 0; + retval = 1; + if( write( fd, data, datasize ) != datasize ) + retval = 0; + if( fsyncflag ) + { + #if CC_LINUX + fdatasync( fd ); + #else + fsync( fd ); + #endif + } + if( close( fd ) != 0 ) + retval = 0; +#elif CC_WINDOWS && !defined(__UWP__) + HANDLE file; + DWORD byteswritten; + file = CreateFileA( path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ); + if( file == INVALID_HANDLE_VALUE ) + return 0; + retval = 1; + if( !( WriteFile( file, data, datasize, &byteswritten, 0 ) ) ) + retval = 0; + if( fsyncflag ) + FlushFileBuffers( file ); + if( CloseHandle( file ) == 0 ) + retval = 0; +#else + FILE *file; + file = fopen( path, "wb" ); + if( !( file ) ) + return 0; + retval = 1; + if( fwrite( data, datasize, 1, file ) != 1 ) + retval = 0; + if( fclose( file ) != 0 ) + retval = 0; +#endif + return retval; +} + + +int ccFileExists( char *path ) +{ + int statret; +#if CC_UNIX + struct stat filestat; + statret = stat( path, &filestat ); + return ( statret == 0 ); +#elif CC_WINDOWS + struct _stat filestat; + statret = _stat( path, &filestat ); + return ( statret == 0 ); +#else + FILE *file; + file = fopen( path, "rb" ); + if( !( file ) ) + return 0; + fclose( file ); + return 1; +#endif +} + + +int ccFileStat( char *path, size_t *retfilesize, time_t *retfiletime ) +{ + int statret; +#if CC_UNIX + struct stat filestat; + statret = stat( path, &filestat ); + if( !( statret ) ) + { + if( retfilesize ) + *retfilesize = filestat.st_size; + if( retfiletime ) + *retfiletime = filestat.st_mtime; + return 1; + } +#elif CC_WINDOWS + struct _stat filestat; + statret = _stat( path, &filestat ); + if( !( statret ) ) + { + if( retfilesize ) + *retfilesize = filestat.st_size; + if( retfiletime ) + *retfiletime = filestat.st_mtime; + return 1; + } +#endif + if( retfilesize ) + *retfilesize = 0; + if( retfiletime ) + *retfiletime = 0; + return 0; +} + + +int ccRenameFile( char *oldpath, char *newpath ) +{ +#if CC_WINDOWS + int attemptindex, attemptcount; + /* On Windows, file indexing or anti-virus software could be scanning the file and prevent rename() */ + attemptcount = 16; + for( attemptindex = 0 ; ; attemptindex++ ) + { + if( MoveFileEx( oldpath, newpath, MOVEFILE_REPLACE_EXISTING ) ) + break; + if( attemptindex >= attemptcount ) + return 0; + ccSleep( 250 ); + } +#else + if( rename( oldpath, newpath ) ) + return 0; +#endif + return 1; +} + + +//// + + +struct _ccDir +{ +#if CC_UNIX + void *dirhandle; +#elif CC_WINDOWS + HANDLE dirhandle; + WIN32_FIND_DATA direntry; + int firstflag; +#endif +}; + + +ccDir *ccOpenDir( char *path ) +{ + ccDir *dir; +#if CC_UNIX + dir = malloc( sizeof(ccDir) ); + dir->dirhandle = opendir( path ); + if( !( dir->dirhandle ) ) + { + free( dir ); + return 0; + } + return dir; +#elif CC_WINDOWS + dir = malloc( sizeof(ccDir) ); + dir->dirhandle = FindFirstFile( path, &dir->direntry ); + if( dir->dirhandle == INVALID_HANDLE_VALUE ) + { + free( dir ); + return 0; + } + dir->firstflag = 1; + return dir; +#else + return 0; +#endif +} + +char *ccReadDir( ccDir *dir ) +{ +#if CC_UNIX + struct dirent *direntry; + direntry = readdir( dir->dirhandle ); + if( direntry ) + return direntry->d_name; + return 0; +#elif CC_WINDOWS + if( dir->firstflag ) + { + dir->firstflag = 0; + return (dir->direntry).cFileName; + } + if( FindNextFile( dir->dirhandle, &dir->direntry ) ) + return (dir->direntry).cFileName; + return 0; +#else + return 0; +#endif +} + +void ccCloseDir( ccDir *dir ) +{ +#if CC_UNIX + closedir( dir->dirhandle ); +#elif CC_WINDOWS + FindClose( dir->dirhandle ); +#endif + free( dir ); + return; +} + + +//// + + +int64_t ccGetFreeDiskSpace( char *dirpath ) +{ + int64_t freespace; +#if CC_UNIX + struct statvfs fsdata; + if( ( statvfs( dirpath, &fsdata ) ) != 0 ) + return -1; + if( ( fsdata.f_bfree == 0 ) || ( fsdata.f_bfree == -1 ) || ( fsdata.f_frsize == 0 ) || ( fsdata.f_frsize == -1 ) ) + return -1; + freespace = (int64_t)fsdata.f_bfree * (int64_t)fsdata.f_frsize; +#elif CC_WINDOWS + ULARGE_INTEGER winfreespace; + if( !( GetDiskFreeSpaceExA( dirpath, &winfreespace, 0, 0 ) ) ) + return -1; + freespace = (int64_t)winfreespace.QuadPart; +#else + freespace = -1; +#endif + return freespace; +} + + +//// + + +int ccGetTimeOfDay( struct timeval *tv ) +{ +#ifdef CC_WIN32 + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL + FILETIME ft; + uint64_t curtime; + if( tv ) + { + GetSystemTimeAsFileTime( &ft ); + curtime = ft.dwHighDateTime; + curtime <<= 32; + curtime |= ft.dwLowDateTime; + curtime /= 10; + curtime -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long)( curtime / 1000000UL ); + tv->tv_usec = (long)( curtime % 1000000UL ); + } +#else + if( tv ) + gettimeofday( tv, 0 ); +#endif + return 0; +} + + +void ccSleep( int milliseconds ) +{ +#if CC_UNIX + struct timespec nanosleeptime; + nanosleeptime.tv_sec = milliseconds / 1000; + nanosleeptime.tv_nsec = ( milliseconds % 1000 ) * 1000000; + nanosleep( &nanosleeptime, 0 ); +#elif CC_WINDOWS + Sleep( milliseconds ); +#else + sleep( (milliseconds+999)/1000 ); +#endif + return; +} + + +//// + + +/* Returned string must be free()d */ +char *ccGetSystemName() +{ + char *string; +#if CC_UNIX + struct utsname unamebuf; + if( uname( &unamebuf ) ) + return 0; + string = ccStrAllocPrintf( "%s %s, Build %s, %s", unamebuf.sysname, unamebuf.release, unamebuf.version, unamebuf.machine ); +#elif CC_WINDOWS + #ifndef VER_SUITE_WH_SERVER + #define VER_SUITE_WH_SERVER 0x00008000 + #endif + #ifndef PRODUCT_PROFESSIONAL + #define PRODUCT_PROFESSIONAL 0x00000030 + #endif + #ifndef PRODUCT_ULTIMATE + #define PRODUCT_ULTIMATE 0x00000001 + #endif + #ifndef PRODUCT_HOME_BASIC + #define PRODUCT_HOME_BASIC 0x00000002 + #endif + #ifndef PRODUCT_HOME_PREMIUM + #define PRODUCT_HOME_PREMIUM 0x00000003 + #endif + #ifndef PRODUCT_ENTERPRISE + #define PRODUCT_ENTERPRISE 0x00000004 + #endif + #ifndef PRODUCT_BUSINESS + #define PRODUCT_BUSINESS 0x00000006 + #endif + #ifndef PRODUCT_STANDARD_SERVER + #define PRODUCT_STANDARD_SERVER 0x00000007 + #endif + #ifndef PRODUCT_DATACENTER_SERVER + #define PRODUCT_DATACENTER_SERVER 0x00000008 + #endif + #ifndef PRODUCT_SMALLBUSINESS_SERVER + #define PRODUCT_SMALLBUSINESS_SERVER 0x00000009 + #endif + #ifndef PRODUCT_ENTERPRISE_SERVER + #define PRODUCT_ENTERPRISE_SERVER 0x0000000A + #endif + #ifndef PRODUCT_STARTER + #define PRODUCT_STARTER 0x0000000B + #endif + #ifndef PRODUCT_DATACENTER_SERVER_CORE + #define PRODUCT_DATACENTER_SERVER_CORE 0x0000000C + #endif + #ifndef PRODUCT_STANDARD_SERVER_CORE + #define PRODUCT_STANDARD_SERVER_CORE 0x0000000D + #endif + #ifndef PRODUCT_ENTERPRISE_SERVER_CORE + #define PRODUCT_ENTERPRISE_SERVER_CORE 0x0000000E + #endif + #ifndef PRODUCT_ENTERPRISE_SERVER_IA64 + #define PRODUCT_ENTERPRISE_SERVER_IA64 0x0000000F + #endif + #ifndef PRODUCT_WEB_SERVER + #define PRODUCT_WEB_SERVER 0x00000011 + #endif + #ifndef PRODUCT_CLUSTER_SERVER + #define PRODUCT_CLUSTER_SERVER 0x00000012 + #endif + #ifndef PRODUCT_SMALLBUSINESS_SERVER_PREMIUM + #define PRODUCT_SMALLBUSINESS_SERVER_PREMIUM 0x00000019 + #endif + + typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); + typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD); + + OSVERSIONINFOEX osvi; + SYSTEM_INFO si; + DWORD dwType; + PGPI pGPI; + PGNSI pGNSI; + char *sysname, *detailname, *packname, *archname; + int buildnumber; + + ZeroMemory( &si, sizeof(SYSTEM_INFO) ); + ZeroMemory( &osvi, sizeof(OSVERSIONINFOEX) ); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); +#if !defined(__UWP__) + if( !( GetVersionEx( (OSVERSIONINFO*) &osvi ) ) ) +#endif + return 0; + +#if !defined(__UWP__) + pGNSI = (PGNSI)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "GetNativeSystemInfo" ); + if( pGNSI ) + pGNSI( &si ); + else + GetSystemInfo( &si ); + if( ( osvi.dwPlatformId != VER_PLATFORM_WIN32_NT ) || ( osvi.dwMajorVersion <= 4 ) ) + return 0; + + sysname = "Unknown"; + detailname = 0; + packname = 0; + buildnumber = 0; + archname = 0; + + if( osvi.dwMajorVersion == 6 ) + { + if( osvi.dwMinorVersion == 0 ) + sysname = ( osvi.wProductType == VER_NT_WORKSTATION ? "Windows Vista" : "Windows Server 2008" ); + else if ( osvi.dwMinorVersion == 1 ) + sysname = ( osvi.wProductType == VER_NT_WORKSTATION ? "Windows 7" : "Windows Server 2008 R2" ); + else if ( osvi.dwMinorVersion == 2 ) + sysname = ( osvi.wProductType == VER_NT_WORKSTATION ? "Windows 8" : "Windows Server 2012" ); + else + sysname = "Windows 8 or more"; + + pGPI = (PGPI)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "GetProductInfo" ); + + pGPI( osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType ); + switch( dwType ) + { + case PRODUCT_ULTIMATE: + detailname = "Ultimate Edition"; + break; + case PRODUCT_PROFESSIONAL: + detailname = "Professional"; + break; + case PRODUCT_HOME_PREMIUM: + detailname = "Home Premium Edition"; + break; + case PRODUCT_HOME_BASIC: + detailname = "Home Basic Edition"; + break; + case PRODUCT_ENTERPRISE: + detailname = "Enterprise Edition"; + break; + case PRODUCT_BUSINESS: + detailname = "Business Edition"; + break; + case PRODUCT_STARTER: + detailname = "Starter Edition"; + break; + case PRODUCT_CLUSTER_SERVER: + detailname = "Cluster Server Edition"; + break; + case PRODUCT_DATACENTER_SERVER: + detailname = "Datacenter Edition"; + break; + case PRODUCT_DATACENTER_SERVER_CORE: + detailname = "Datacenter Edition (core installation)"; + break; + case PRODUCT_ENTERPRISE_SERVER: + detailname = "Enterprise Edition"; + break; + case PRODUCT_ENTERPRISE_SERVER_CORE: + detailname = "Enterprise Edition (core installation)"; + break; + case PRODUCT_ENTERPRISE_SERVER_IA64: + detailname = "Enterprise Edition for Itanium-based Systems"; + break; + case PRODUCT_SMALLBUSINESS_SERVER: + detailname = "Small Business Server"; + break; + case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: + detailname = "Small Business Server Premium Edition"; + break; + case PRODUCT_STANDARD_SERVER: + detailname = "Standard Edition"; + break; + case PRODUCT_STANDARD_SERVER_CORE: + detailname = "Standard Edition (core installation)"; + break; + case PRODUCT_WEB_SERVER: + detailname = "Web Server Edition"; + break; + default: + break; + } + } + else if( ( osvi.dwMajorVersion == 5 ) && ( osvi.dwMinorVersion == 2 ) ) + { + if( GetSystemMetrics(SM_SERVERR2) ) + sysname = "Windows Server 2003 R2, "; + else if ( osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER ) + sysname = "Windows Storage Server 2003"; + else if ( osvi.wSuiteMask & VER_SUITE_WH_SERVER ) + sysname = "Windows Home Server"; + else if( ( osvi.wProductType == VER_NT_WORKSTATION ) && ( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ) ) + sysname = "Windows XP Professional x64 Edition"; + else + sysname ="Windows Server 2003, "; + if( osvi.wProductType != VER_NT_WORKSTATION ) + { + if( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 ) + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + detailname = "Datacenter Edition for Itanium-based Systems"; + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + detailname = "Enterprise Edition for Itanium-based Systems"; + } + else if( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ) + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + detailname = "Datacenter x64 Edition"; + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + detailname = "Enterprise x64 Edition"; + else + detailname = "Standard x64 Edition"; + } + else + { + if( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER ) + detailname = "Compute Cluster Edition"; + else if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + detailname = "Datacenter Edition"; + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + detailname = "Enterprise Edition"; + else if ( osvi.wSuiteMask & VER_SUITE_BLADE ) + detailname = "Web Edition"; + else + detailname = "Standard Edition"; + } + } + } + else if( ( osvi.dwMajorVersion == 5 ) && ( osvi.dwMinorVersion == 1 ) ) + { + sysname = "Windows XP "; + if( osvi.wSuiteMask & VER_SUITE_PERSONAL ) + sysname = "Home Edition"; + else + sysname = "Professional"; + } + else if( ( osvi.dwMajorVersion == 5 ) && ( osvi.dwMinorVersion == 0 ) ) + { + sysname = "Windows 2000 "; + if( osvi.wProductType == VER_NT_WORKSTATION ) + detailname = "Professional"; + else + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + detailname = "Datacenter Server"; + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + detailname = "Advanced Server"; + else + detailname = "Server"; + } + } + packname = osvi.szCSDVersion; + buildnumber = osvi.dwBuildNumber; + if( osvi.dwMajorVersion >= 6 ) + { + if( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ) + archname = ", 64-bit"; + else if( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL ) + archname = ", 32-bit"; + } + + /* Finally build the string */ + string = ccStrAllocPrintf( "%s%s%s%s%s (build %d )%s", sysname, ( detailname ? ", " : "" ), ( detailname ? detailname : "" ), ( packname ? ", " : "" ), ( packname ? packname : "" ), buildnumber, ( archname ? archname : "" ) ); +#endif + +#endif + return string; +} diff --git a/ecere/src/gfx/newFonts/cc/cc.h b/ecere/src/gfx/newFonts/cc/cc.h new file mode 100644 index 0000000000..3e966e38f7 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/cc.h @@ -0,0 +1,1911 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ + +#ifndef CC_H +#define CC_H + +#include +#include +#include + +#include "cpuconfig.h" + +#if defined(__linux__) && !defined(__ANDROID__) +asm(".symver pow,pow@GLIBC_2.2.5"); +asm(".symver log,log@GLIBC_2.2.5"); +asm(".symver exp,exp@GLIBC_2.2.5"); +#endif + +#if defined(__linux__) || defined(__gnu_linux__) || defined(__linux) || defined(__linux) + #define CC_LINUX (1) + #define CC_UNIX (1) +#elif defined(__APPLE__) + #define CC_OSX (1) + #define CC_UNIX (1) +#elif defined(__unix__) || defined(__unix) || defined(unix) + #define CC_UNIX (1) +#elif defined(_WIN64) || defined(__WIN64__) || defined(WIN64) + #define CC_WIN64 (1) + #define CC_WIN32 (1) + #define CC_WINDOWS (1) +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + #define CC_WIN32 (1) + #define CC_WINDOWS (1) +#endif + +#if CC_UNIX || defined(__MINGW32__) +#include +#endif + +#if CC_WINDOWS + +#if defined(__UWP__) || !defined(__MINGW32__) +struct timeval +{ + long tv_sec; + long tv_usec; +}; +#endif + +#if !defined(ssize_t) && !defined(__MINGW32__) +#if defined(_WIN64) +#define ssize_t long long +#else +#define ssize_t long +#endif +#endif + +#endif +//// + + +#ifndef ADDRESS + #define ADDRESS(p,o) ((void *)(((char *)p)+(o))) +#endif + +#ifndef ADDRESSDIFF + #define ADDRESSDIFF(a,b) (((char *)a)-((char *)b)) +#endif + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) + #define CC_NOINLINE __attribute__((noinline)) + #define CC_ALWAYSINLINE __attribute__((always_inline)) + #define CC_LIKELY(x) __builtin_expect(!!(x), 1) + #define CC_UNLIKELY(x) __builtin_expect(!!(x), 0) + #define CC_UNUSED __attribute__((unused)) + #define CC_DEPRECATED __attribute__((deprecated)) + #define CC_DEPRECATED_MSG(x) __attribute__((deprecated(x))) + #define MM_UNREACHABLE __builtin_unreachable() +#elif defined(_MSC_VER) + #define CC_NOINLINE __declspec(noinline) + #define CC_ALWAYSINLINE __forceinline + #define CC_LIKELY(x) (x) + #define CC_UNLIKELY(x) (x) + #define CC_UNUSED + #define CC_DEPRECATED _declspec(deprecated) + #define CC_DEPRECATED_MSG(x) _declspec(deprecated(x)) + #define MM_UNREACHABLE _assume(0) + #ifndef inline + #define inline __inline + #endif + #ifndef restrict + #define restrict __restrict + #endif + #ifndef ssize_t + #if CC_ARCH_AMD64 + #define ssize_t int64_t + #else + #define ssize_t int32_t + #endif + #endif +#else + #define CC_NOINLINE + #define CC_ALWAYSINLINE + #define CC_LIKELY(x) (x) + #define CC_UNLIKELY(x) (x) + #define CC_UNUSED + #define CC_DEPRECATED + #define CC_DEPRECATED_MSG(x) + #define MM_UNREACHABLE +#endif + +#if CC_UNIX + #define CC_DIR_SEPARATOR_CHAR '/' + #define CC_DIR_SEPARATOR_STRING "/" +#elif CC_WINDOWS + #define CC_DIR_SEPARATOR_CHAR '\\' + #define CC_DIR_SEPARATOR_STRING "\\" +#else + #define CC_DIR_SEPARATOR_CHAR '/' + #define CC_DIR_SEPARATOR_STRING "/" +#endif + +#if CC_WINDOWS + #define CC_LL "I64" + #define CC_LLD "%I64d" + #define CC_LLU "%I64u" + #define CC_LLX "%I64x" +#else + #define CC_LL "ll" + #define CC_LLD "%lld" + #define CC_LLU "%llu" + #define CC_LLX "%llx" +#endif + +#define CC_SIZEOF_ALIGN4(x) ((sizeof(x)+0x3)&~0x3) +#define CC_SIZEOF_ALIGN8(x) ((sizeof(x)+0x7)&~0x7) +#define CC_SIZEOF_ALIGN16(x) ((sizeof(x)+0xF)&~0xF) +#define CC_SIZEOF_ALIGN32(x) ((sizeof(x)+0x1F)&~0x1F) +#define CC_SIZEOF_ALIGN64(x) ((sizeof(x)+0x3F)&~0x3F) + + +//// + + +#define CC_MIN(x,y) ((x)>(y)?(y):(x)) +#define CC_MAX(x,y) ((x)<(y)?(y):(x)) +#define CC_CLAMP(x,min,max) ((x)<(min)?(min):((x)>(max)?(max):(x))) + + +#define CC_MAX_INT8(x,y) (((int8_t)x)-((((int8_t)x)-((int8_t)y))&((((int8_t)x)-((int8_t)y))>>7))) +#define CC_MAX_INT16(x,y) (((int16_t)x)-((((int16_t)x)-((int16_t)y))&((((int16_t)x)-((int16_t)y))>>15))) +#define CC_MAX_INT32(x,y) (((int32_t)x)-((((int32_t)x)-((int32_t)y))&((((int32_t)x)-((int32_t)y))>>31))) +#define CC_MAX_INT64(x,y) (((int64_t)x)-((((int64_t)x)-((int64_t)y))&((((int64_t)x)-((int64_t)y))>>63))) + +#define CC_MIN_INT8(x,y) (((int8_t)y)+((((int8_t)x)-((int8_t)y))&((((int8_t)x)-((int8_t)y))>>7))) +#define CC_MIN_INT16(x,y) (((int16_t)y)+((((int16_t)x)-((int16_t)y))&((((int16_t)x)-((int16_t)y))>>15))) +#define CC_MIN_INT32(x,y) (((int32_t)y)+((((int32_t)x)-((int32_t)y))&((((int32_t)x)-((int32_t)y))>>31))) +#define CC_MIN_INT64(x,y) (((int64_t)y)+((((int64_t)x)-((int64_t)y))&((((int64_t)x)-((int64_t)y))>>63))) + +#define CC_SHIFTDIV_INT8(value,shift) ({uint8_t _s=((uint8_t)value)>>7;((int8_t)((value)+(_s<>shift;}) +#define CC_SHIFTDIV_INT16(value,shift) ({uint16_t _s=((uint16_t)value)>>15;((int16_t)((value)+(_s<>shift;}) +#define CC_SHIFTDIV_INT32(value,shift) ({uint32_t _s=((uint32_t)value)>>31;((int32_t)((value)+(_s<>shift;}) +#define CC_SHIFTDIV_INT64(value,shift) ({uint64_t _s=((uint64_t)value)>>63;((int64_t)((value)+(_s<>shift;}) + +#define CC_SHIFTDIVROUND(value,shift) ((value>>shift)+(((value&((1<=(1<>shift)+((((value&((1<>7))<<1)>=(1<>shift)+((((value&((1<>15))<<1)>=(1<>shift)+((((value&((1<>31))<<1)>=(1<>shift)+((((value&((1<>63))<<1)>=(1<>2)):(CC_NUMBITS2(n))) +#define CC_NUMBITS8(n) ((n&0xF0)?(4+CC_NUMBITS4(n>>4)):(CC_NUMBITS4(n))) +#define CC_NUMBITS16(n) ((n&0xFF00)?(8+CC_NUMBITS8(n>>8)):(CC_NUMBITS8(n))) +#define CC_NUMBITS32(n) ((n&0xFFFF0000)?(16+CC_NUMBITS16(n>>16)):(CC_NUMBITS16(n))) +#define CC_NUMBITS(n) (n==0?0:CC_NUMBITS32(n)+1) + + +//// + + +#define CC_STRINGIFY_IN(s) #s +#define CC_STRINGIFY(s) CC_STRINGIFY_IN(s) + +#define CC_CONCATENATE_IN(s,n) s ## n +#define CC_CONCATENATE(s,n) CC_CONCATENATE_IN(s,n) + +#define CC_STRINGIFY_BYTE_0 "\x00" +#define CC_STRINGIFY_BYTE_1 "\x01" +#define CC_STRINGIFY_BYTE_2 "\x02" +#define CC_STRINGIFY_BYTE_3 "\x03" +#define CC_STRINGIFY_BYTE_4 "\x04" +#define CC_STRINGIFY_BYTE_5 "\x05" +#define CC_STRINGIFY_BYTE_6 "\x06" +#define CC_STRINGIFY_BYTE_7 "\x07" +#define CC_STRINGIFY_BYTE_8 "\x08" +#define CC_STRINGIFY_BYTE_9 "\x09" +#define CC_STRINGIFY_BYTE_10 "\x0a" +#define CC_STRINGIFY_BYTE_11 "\x0b" +#define CC_STRINGIFY_BYTE_12 "\x0c" +#define CC_STRINGIFY_BYTE_13 "\x0d" +#define CC_STRINGIFY_BYTE_14 "\x0e" +#define CC_STRINGIFY_BYTE_15 "\x0f" +#define CC_STRINGIFY_BYTE_16 "\x10" +#define CC_STRINGIFY_BYTE_17 "\x11" +#define CC_STRINGIFY_BYTE_18 "\x12" +#define CC_STRINGIFY_BYTE_19 "\x13" +#define CC_STRINGIFY_BYTE_20 "\x14" +#define CC_STRINGIFY_BYTE_21 "\x15" +#define CC_STRINGIFY_BYTE_22 "\x16" +#define CC_STRINGIFY_BYTE_23 "\x17" +#define CC_STRINGIFY_BYTE_24 "\x18" +#define CC_STRINGIFY_BYTE_25 "\x19" +#define CC_STRINGIFY_BYTE_26 "\x1a" +#define CC_STRINGIFY_BYTE_27 "\x1b" +#define CC_STRINGIFY_BYTE_28 "\x1c" +#define CC_STRINGIFY_BYTE_29 "\x1d" +#define CC_STRINGIFY_BYTE_30 "\x1e" +#define CC_STRINGIFY_BYTE_31 "\x1f" +#define CC_STRINGIFY_BYTE_32 "\x20" +#define CC_STRINGIFY_BYTE_33 "\x21" +#define CC_STRINGIFY_BYTE_34 "\x22" +#define CC_STRINGIFY_BYTE_35 "\x23" +#define CC_STRINGIFY_BYTE_36 "\x24" +#define CC_STRINGIFY_BYTE_37 "\x25" +#define CC_STRINGIFY_BYTE_38 "\x26" +#define CC_STRINGIFY_BYTE_39 "\x27" +#define CC_STRINGIFY_BYTE_40 "\x28" +#define CC_STRINGIFY_BYTE_41 "\x29" +#define CC_STRINGIFY_BYTE_42 "\x2a" +#define CC_STRINGIFY_BYTE_43 "\x2b" +#define CC_STRINGIFY_BYTE_44 "\x2c" +#define CC_STRINGIFY_BYTE_45 "\x2d" +#define CC_STRINGIFY_BYTE_46 "\x2e" +#define CC_STRINGIFY_BYTE_47 "\x2f" +#define CC_STRINGIFY_BYTE_48 "\x30" +#define CC_STRINGIFY_BYTE_49 "\x31" +#define CC_STRINGIFY_BYTE_50 "\x32" +#define CC_STRINGIFY_BYTE_51 "\x33" +#define CC_STRINGIFY_BYTE_52 "\x34" +#define CC_STRINGIFY_BYTE_53 "\x35" +#define CC_STRINGIFY_BYTE_54 "\x36" +#define CC_STRINGIFY_BYTE_55 "\x37" +#define CC_STRINGIFY_BYTE_56 "\x38" +#define CC_STRINGIFY_BYTE_57 "\x39" +#define CC_STRINGIFY_BYTE_58 "\x3a" +#define CC_STRINGIFY_BYTE_59 "\x3b" +#define CC_STRINGIFY_BYTE_60 "\x3c" +#define CC_STRINGIFY_BYTE_61 "\x3d" +#define CC_STRINGIFY_BYTE_62 "\x3e" +#define CC_STRINGIFY_BYTE_63 "\x3f" +#define CC_STRINGIFY_BYTE_64 "\x40" +#define CC_STRINGIFY_BYTE_65 "\x41" +#define CC_STRINGIFY_BYTE_66 "\x42" +#define CC_STRINGIFY_BYTE_67 "\x43" +#define CC_STRINGIFY_BYTE_68 "\x44" +#define CC_STRINGIFY_BYTE_69 "\x45" +#define CC_STRINGIFY_BYTE_70 "\x46" +#define CC_STRINGIFY_BYTE_71 "\x47" +#define CC_STRINGIFY_BYTE_72 "\x48" +#define CC_STRINGIFY_BYTE_73 "\x49" +#define CC_STRINGIFY_BYTE_74 "\x4a" +#define CC_STRINGIFY_BYTE_75 "\x4b" +#define CC_STRINGIFY_BYTE_76 "\x4c" +#define CC_STRINGIFY_BYTE_77 "\x4d" +#define CC_STRINGIFY_BYTE_78 "\x4e" +#define CC_STRINGIFY_BYTE_79 "\x4f" +#define CC_STRINGIFY_BYTE_80 "\x50" +#define CC_STRINGIFY_BYTE_81 "\x51" +#define CC_STRINGIFY_BYTE_82 "\x52" +#define CC_STRINGIFY_BYTE_83 "\x53" +#define CC_STRINGIFY_BYTE_84 "\x54" +#define CC_STRINGIFY_BYTE_85 "\x55" +#define CC_STRINGIFY_BYTE_86 "\x56" +#define CC_STRINGIFY_BYTE_87 "\x57" +#define CC_STRINGIFY_BYTE_88 "\x58" +#define CC_STRINGIFY_BYTE_89 "\x59" +#define CC_STRINGIFY_BYTE_90 "\x5a" +#define CC_STRINGIFY_BYTE_91 "\x5b" +#define CC_STRINGIFY_BYTE_92 "\x5c" +#define CC_STRINGIFY_BYTE_93 "\x5d" +#define CC_STRINGIFY_BYTE_94 "\x5e" +#define CC_STRINGIFY_BYTE_95 "\x5f" +#define CC_STRINGIFY_BYTE_96 "\x60" +#define CC_STRINGIFY_BYTE_97 "\x61" +#define CC_STRINGIFY_BYTE_98 "\x62" +#define CC_STRINGIFY_BYTE_99 "\x63" +#define CC_STRINGIFY_BYTE_100 "\x64" +#define CC_STRINGIFY_BYTE_101 "\x65" +#define CC_STRINGIFY_BYTE_102 "\x66" +#define CC_STRINGIFY_BYTE_103 "\x67" +#define CC_STRINGIFY_BYTE_104 "\x68" +#define CC_STRINGIFY_BYTE_105 "\x69" +#define CC_STRINGIFY_BYTE_106 "\x6a" +#define CC_STRINGIFY_BYTE_107 "\x6b" +#define CC_STRINGIFY_BYTE_108 "\x6c" +#define CC_STRINGIFY_BYTE_109 "\x6d" +#define CC_STRINGIFY_BYTE_110 "\x6e" +#define CC_STRINGIFY_BYTE_111 "\x6f" +#define CC_STRINGIFY_BYTE_112 "\x70" +#define CC_STRINGIFY_BYTE_113 "\x71" +#define CC_STRINGIFY_BYTE_114 "\x72" +#define CC_STRINGIFY_BYTE_115 "\x73" +#define CC_STRINGIFY_BYTE_116 "\x74" +#define CC_STRINGIFY_BYTE_117 "\x75" +#define CC_STRINGIFY_BYTE_118 "\x76" +#define CC_STRINGIFY_BYTE_119 "\x77" +#define CC_STRINGIFY_BYTE_120 "\x78" +#define CC_STRINGIFY_BYTE_121 "\x79" +#define CC_STRINGIFY_BYTE_122 "\x7a" +#define CC_STRINGIFY_BYTE_123 "\x7b" +#define CC_STRINGIFY_BYTE_124 "\x7c" +#define CC_STRINGIFY_BYTE_125 "\x7d" +#define CC_STRINGIFY_BYTE_126 "\x7e" +#define CC_STRINGIFY_BYTE_127 "\x7f" +#define CC_STRINGIFY_BYTE_128 "\x80" +#define CC_STRINGIFY_BYTE_129 "\x81" +#define CC_STRINGIFY_BYTE_130 "\x82" +#define CC_STRINGIFY_BYTE_131 "\x83" +#define CC_STRINGIFY_BYTE_132 "\x84" +#define CC_STRINGIFY_BYTE_133 "\x85" +#define CC_STRINGIFY_BYTE_134 "\x86" +#define CC_STRINGIFY_BYTE_135 "\x87" +#define CC_STRINGIFY_BYTE_136 "\x88" +#define CC_STRINGIFY_BYTE_137 "\x89" +#define CC_STRINGIFY_BYTE_138 "\x8a" +#define CC_STRINGIFY_BYTE_139 "\x8b" +#define CC_STRINGIFY_BYTE_140 "\x8c" +#define CC_STRINGIFY_BYTE_141 "\x8d" +#define CC_STRINGIFY_BYTE_142 "\x8e" +#define CC_STRINGIFY_BYTE_143 "\x8f" +#define CC_STRINGIFY_BYTE_144 "\x90" +#define CC_STRINGIFY_BYTE_145 "\x91" +#define CC_STRINGIFY_BYTE_146 "\x92" +#define CC_STRINGIFY_BYTE_147 "\x93" +#define CC_STRINGIFY_BYTE_148 "\x94" +#define CC_STRINGIFY_BYTE_149 "\x95" +#define CC_STRINGIFY_BYTE_150 "\x96" +#define CC_STRINGIFY_BYTE_151 "\x97" +#define CC_STRINGIFY_BYTE_152 "\x98" +#define CC_STRINGIFY_BYTE_153 "\x99" +#define CC_STRINGIFY_BYTE_154 "\x9a" +#define CC_STRINGIFY_BYTE_155 "\x9b" +#define CC_STRINGIFY_BYTE_156 "\x9c" +#define CC_STRINGIFY_BYTE_157 "\x9d" +#define CC_STRINGIFY_BYTE_158 "\x9e" +#define CC_STRINGIFY_BYTE_159 "\x9f" +#define CC_STRINGIFY_BYTE_160 "\xa0" +#define CC_STRINGIFY_BYTE_161 "\xa1" +#define CC_STRINGIFY_BYTE_162 "\xa2" +#define CC_STRINGIFY_BYTE_163 "\xa3" +#define CC_STRINGIFY_BYTE_164 "\xa4" +#define CC_STRINGIFY_BYTE_165 "\xa5" +#define CC_STRINGIFY_BYTE_166 "\xa6" +#define CC_STRINGIFY_BYTE_167 "\xa7" +#define CC_STRINGIFY_BYTE_168 "\xa8" +#define CC_STRINGIFY_BYTE_169 "\xa9" +#define CC_STRINGIFY_BYTE_170 "\xaa" +#define CC_STRINGIFY_BYTE_171 "\xab" +#define CC_STRINGIFY_BYTE_172 "\xac" +#define CC_STRINGIFY_BYTE_173 "\xad" +#define CC_STRINGIFY_BYTE_174 "\xae" +#define CC_STRINGIFY_BYTE_175 "\xaf" +#define CC_STRINGIFY_BYTE_176 "\xb0" +#define CC_STRINGIFY_BYTE_177 "\xb1" +#define CC_STRINGIFY_BYTE_178 "\xb2" +#define CC_STRINGIFY_BYTE_179 "\xb3" +#define CC_STRINGIFY_BYTE_180 "\xb4" +#define CC_STRINGIFY_BYTE_181 "\xb5" +#define CC_STRINGIFY_BYTE_182 "\xb6" +#define CC_STRINGIFY_BYTE_183 "\xb7" +#define CC_STRINGIFY_BYTE_184 "\xb8" +#define CC_STRINGIFY_BYTE_185 "\xb9" +#define CC_STRINGIFY_BYTE_186 "\xba" +#define CC_STRINGIFY_BYTE_187 "\xbb" +#define CC_STRINGIFY_BYTE_188 "\xbc" +#define CC_STRINGIFY_BYTE_189 "\xbd" +#define CC_STRINGIFY_BYTE_190 "\xbe" +#define CC_STRINGIFY_BYTE_191 "\xbf" +#define CC_STRINGIFY_BYTE_192 "\xc0" +#define CC_STRINGIFY_BYTE_193 "\xc1" +#define CC_STRINGIFY_BYTE_194 "\xc2" +#define CC_STRINGIFY_BYTE_195 "\xc3" +#define CC_STRINGIFY_BYTE_196 "\xc4" +#define CC_STRINGIFY_BYTE_197 "\xc5" +#define CC_STRINGIFY_BYTE_198 "\xc6" +#define CC_STRINGIFY_BYTE_199 "\xc7" +#define CC_STRINGIFY_BYTE_200 "\xc8" +#define CC_STRINGIFY_BYTE_201 "\xc9" +#define CC_STRINGIFY_BYTE_202 "\xca" +#define CC_STRINGIFY_BYTE_203 "\xcb" +#define CC_STRINGIFY_BYTE_204 "\xcc" +#define CC_STRINGIFY_BYTE_205 "\xcd" +#define CC_STRINGIFY_BYTE_206 "\xce" +#define CC_STRINGIFY_BYTE_207 "\xcf" +#define CC_STRINGIFY_BYTE_208 "\xd0" +#define CC_STRINGIFY_BYTE_209 "\xd1" +#define CC_STRINGIFY_BYTE_210 "\xd2" +#define CC_STRINGIFY_BYTE_211 "\xd3" +#define CC_STRINGIFY_BYTE_212 "\xd4" +#define CC_STRINGIFY_BYTE_213 "\xd5" +#define CC_STRINGIFY_BYTE_214 "\xd6" +#define CC_STRINGIFY_BYTE_215 "\xd7" +#define CC_STRINGIFY_BYTE_216 "\xd8" +#define CC_STRINGIFY_BYTE_217 "\xd9" +#define CC_STRINGIFY_BYTE_218 "\xda" +#define CC_STRINGIFY_BYTE_219 "\xdb" +#define CC_STRINGIFY_BYTE_220 "\xdc" +#define CC_STRINGIFY_BYTE_221 "\xdd" +#define CC_STRINGIFY_BYTE_222 "\xde" +#define CC_STRINGIFY_BYTE_223 "\xdf" +#define CC_STRINGIFY_BYTE_224 "\xe0" +#define CC_STRINGIFY_BYTE_225 "\xe1" +#define CC_STRINGIFY_BYTE_226 "\xe2" +#define CC_STRINGIFY_BYTE_227 "\xe3" +#define CC_STRINGIFY_BYTE_228 "\xe4" +#define CC_STRINGIFY_BYTE_229 "\xe5" +#define CC_STRINGIFY_BYTE_230 "\xe6" +#define CC_STRINGIFY_BYTE_231 "\xe7" +#define CC_STRINGIFY_BYTE_232 "\xe8" +#define CC_STRINGIFY_BYTE_233 "\xe9" +#define CC_STRINGIFY_BYTE_234 "\xea" +#define CC_STRINGIFY_BYTE_235 "\xeb" +#define CC_STRINGIFY_BYTE_236 "\xec" +#define CC_STRINGIFY_BYTE_237 "\xed" +#define CC_STRINGIFY_BYTE_238 "\xee" +#define CC_STRINGIFY_BYTE_239 "\xef" +#define CC_STRINGIFY_BYTE_240 "\xf0" +#define CC_STRINGIFY_BYTE_241 "\xf1" +#define CC_STRINGIFY_BYTE_242 "\xf2" +#define CC_STRINGIFY_BYTE_243 "\xf3" +#define CC_STRINGIFY_BYTE_244 "\xf4" +#define CC_STRINGIFY_BYTE_245 "\xf5" +#define CC_STRINGIFY_BYTE_246 "\xf6" +#define CC_STRINGIFY_BYTE_247 "\xf7" +#define CC_STRINGIFY_BYTE_248 "\xf8" +#define CC_STRINGIFY_BYTE_249 "\xf9" +#define CC_STRINGIFY_BYTE_250 "\xfa" +#define CC_STRINGIFY_BYTE_251 "\xfb" +#define CC_STRINGIFY_BYTE_252 "\xfc" +#define CC_STRINGIFY_BYTE_253 "\xfd" +#define CC_STRINGIFY_BYTE_254 "\xfe" +#define CC_STRINGIFY_BYTE_255 "\xff" +#define CC_STRINGIFY_BYTE(x) CC_STRINGIFY_BYTE_##x + + +//// + + +enum +{ + CC_TYPE_UINT8, + CC_TYPE_INT8, + CC_TYPE_UINT16, + CC_TYPE_INT16, + CC_TYPE_UINT32, + CC_TYPE_INT32, + CC_TYPE_UINT64, + CC_TYPE_INT64, + CC_TYPE_FLOAT, + CC_TYPE_DOUBLE, + + CC_TYPE_COUNT +}; + +extern const size_t ccTypeSize[CC_TYPE_COUNT]; + + +//// + + +uint32_t ccHash32Data( void *data, int size ); +uint32_t ccHash32Int32( uint32_t data ); +uint32_t ccHash32Int64( uint64_t data ); +uint32_t ccHash32Array32( uint32_t *data, int count ); +uint32_t ccHash32Array64( uint64_t *data, int count ); + +static inline CC_ALWAYSINLINE uint32_t ccHash32Int16Inline( uint32_t i ) +{ + uint32_t hash; + hash = ( i << 16 ) ^ i; + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline CC_ALWAYSINLINE uint32_t ccHash32Int32Inline( uint32_t i ) +{ + uint32_t hash; + hash = i & 0xFFFF; + hash = ( ( hash << 16 ) ^ hash ) ^ ( ( i & 0xFFFF0000 ) >> 5 ); + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline CC_ALWAYSINLINE uint32_t ccHash32Int64Inline( uint64_t i ) +{ + uint32_t hash; + hash = (uint32_t)(i & 0xFFFF); + hash = ( ( hash << 16 ) ^ hash ) ^ ( ( (uint32_t)( i >> 16 ) & 0xFFFF ) << 11 ); + hash += ( hash >> 11 ) + ( (uint32_t)( i >> 32 ) & 0xFFFF ); + hash = ( ( hash << 16 ) ^ hash ) ^ (uint32_t)( ( i >> 37 ) & 0x7FFF800 ); + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline CC_ALWAYSINLINE uint32_t ccHash32Data3Inline( uint8_t *data ) +{ + uint32_t hash; + hash = 0; + hash += ( (uint32_t)data[1] << 8 ) | (uint32_t)data[0]; + hash ^= hash << 16; + hash ^= (uint32_t)data[2] << 18; + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline CC_ALWAYSINLINE uint32_t ccHash32Data4Inline( uint8_t *data ) +{ + uint32_t hash; + hash = 0; + hash += ( (uint32_t)data[1] << 8 ) | (uint32_t)data[0]; + hash = ( hash << 16 ) ^ ( ( ( (uint32_t)data[3] << 19 ) | ( (uint32_t)data[2] << 11 ) ) ^ hash ); + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline CC_ALWAYSINLINE uint32_t ccHash32Data5Inline( uint8_t *data ) +{ + uint32_t hash; + hash = 0; + hash += ( (uint32_t)data[1] << 8 ) | (uint32_t)data[0]; + hash = ( hash << 16 ) ^ ( ( ( (uint32_t)data[3] << 19 ) | ( (uint32_t)data[2] << 11 ) ) ^ hash ); + hash += hash >> 11; + hash += (uint32_t)data[4]; + hash ^= hash << 10; + hash += hash >> 1; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline CC_ALWAYSINLINE uint32_t ccHash32Data6Inline( uint8_t *data ) +{ + uint32_t hash; + hash = 0; + hash += ( (uint32_t)data[1] << 8 ) | (uint32_t)data[0]; + hash = ( hash << 16 ) ^ ( ( ( (uint32_t)data[3] << 19 ) | ( (uint32_t)data[2] << 11 ) ) ^ hash ); + hash += hash >> 11; + hash += ( (uint32_t)data[5] << 8 ) | (uint32_t)data[4]; + hash ^= hash << 11; + hash += hash >> 17; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline CC_ALWAYSINLINE uint32_t ccHash32Data7Inline( uint8_t *data ) +{ + uint32_t hash; + hash = 0; + hash += ( (uint32_t)data[1] << 8 ) | (uint32_t)data[0]; + hash = ( hash << 16 ) ^ ( ( ( (uint32_t)data[3] << 19 ) | ( (uint32_t)data[2] << 11 ) ) ^ hash ); + hash += hash >> 11; + data = ADDRESS( data, 4 ); + hash += ( (uint32_t)data[5] << 8 ) | (uint32_t)data[4]; + hash ^= hash << 16; + hash ^= (uint32_t)data[6] << 18; + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline CC_ALWAYSINLINE uint32_t ccHash32Data8Inline( uint8_t *data ) +{ + uint32_t hash; + hash = 0; + hash += ( (uint32_t)data[1] << 8 ) | (uint32_t)data[0]; + hash = ( hash << 16 ) ^ ( ( ( (uint32_t)data[3] << 19 ) | ( (uint32_t)data[2] << 11 ) ) ^ hash ); + hash += hash >> 11; + hash += ( (uint32_t)data[5] << 8 ) | (uint32_t)data[4]; + hash = ( hash << 16 ) ^ ( ( ( (uint32_t)data[7] << 19 ) | ( (uint32_t)data[6] << 11 ) ) ^ hash ); + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + + +//// + + +typedef struct +{ + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; +} ccQuickRandState32; + +static inline CC_ALWAYSINLINE uint32_t ccQuickRand32( ccQuickRandState32 *randstate ) +{ + uint32_t e; + e = randstate->a - ( ( randstate->b << 27 ) | ( randstate->b >> (32-27) ) ); + randstate->a = randstate->b ^ ( ( randstate->c << 17 ) | ( randstate->c >> (32-17) ) ); + randstate->b = randstate->c + randstate->d; + randstate->c = randstate->d + e; + randstate->d = e + randstate->a; + return randstate->d; +} + +static inline CC_ALWAYSINLINE void ccQuickRand32Seed( ccQuickRandState32 *randstate, uint32_t seed ) +{ + uint32_t i; + randstate->a = 0xf1ea5eed; + randstate->b = seed; + randstate->c = seed; + randstate->d = seed; + for( i = 0 ; i < 20 ; i++ ) + ccQuickRand32( randstate ); + return; +} + +static inline CC_ALWAYSINLINE void ccQuickRand32SeedFast( ccQuickRandState32 *randstate, uint32_t seed0, uint32_t seed1, uint32_t seed2 ) +{ + uint32_t i; + randstate->a = 0xf1ea5eed; + randstate->b = seed0; + randstate->c = seed1; + randstate->d = seed2; + for( i = 0 ; i < 4 ; i++ ) + ccQuickRand32( randstate ); + return; +} + + +typedef struct +{ + uint64_t a; + uint64_t b; + uint64_t c; + uint64_t d; +} ccQuickRandState64; + +static inline CC_ALWAYSINLINE uint64_t ccQuickRand64( ccQuickRandState64 *randstate ) +{ + uint64_t e; + e = randstate->a - ( ( randstate->b << 7 ) | ( randstate->b >> (64-7) ) ); + randstate->a = randstate->b ^ ( ( randstate->c << 13 ) | ( randstate->c >> (64-13) ) ); + randstate->b = randstate->c + ( ( randstate->d << 37 ) | ( randstate->d >> (64-37) ) ); + randstate->c = randstate->d + e; + randstate->d = e + randstate->a; + return randstate->d; +} + +static inline CC_ALWAYSINLINE void ccQuickRand64Seed( ccQuickRandState64 *randstate, uint64_t seed ) +{ + uint64_t i; + randstate->a = 0xf1ea5eed; + randstate->b = seed; + randstate->c = seed; + randstate->d = seed; + for( i = 0 ; i < 20 ; i++ ) + ccQuickRand64( randstate ); + return; +} + + +//// + + +int ccMemCmp( void *s0, void *s1, int size ); +int ccMemCmp32( uint32_t *s0, uint32_t *s1, int count ); +int ccMemCmp64( uint64_t *s0, uint64_t *s1, int count ); +int ccMemCmpRetSize( void *s0, void *s1, int size ); + +static inline CC_ALWAYSINLINE int ccMemCmpInline( void *s0, void *s1, int size ) +{ + int i; + uint8_t *t0, *t1; + t0 = s0; + t1 = s1; + for( i = 0 ; i < size ; i++ ) + { + if( t0[i] != t1[i] ) + return 0; + } + return 1; +} + +static inline CC_ALWAYSINLINE int ccMemCmpSizeInline( void *s0, void *s1, int size ) +{ + int i; + uint8_t *t0, *t1; + t0 = s0; + t1 = s1; + for( i = 0 ; i < size ; i++ ) + { + if( t0[i] != t1[i] ) + break; + } + return i; +} + + +//// + + +uint8_t ccLog2Int8( uint8_t i ); +uint16_t ccLog2Int16( uint16_t i ); +uint32_t ccLog2Int32( uint32_t i ); +uint64_t ccLog2Int64( uint64_t i ); +#if CPUCONF_LONG_SIZE == 8 + #define ccLog2IntL(v) ccLog2Int64(v) +#else + #define ccLog2IntL(v) ccLog2Int32(v) +#endif + + +//// + + +static inline CC_ALWAYSINLINE int8_t ccPowInt8( int8_t base, int exp ) +{ + int result; + result = 1; + while( exp ) + { + if( exp & 1 ) + result *= base; + exp >>= 1; + base *= base; + } + return (int8_t)result; +} + +static inline CC_ALWAYSINLINE int16_t ccPowInt16( int16_t base, int exp ) +{ + int result; + result = 1; + while( exp ) + { + if( exp & 1 ) + result *= base; + exp >>= 1; + base *= base; + } + return (int16_t)result; +} + +static inline CC_ALWAYSINLINE int32_t ccPowInt32( int32_t base, int exp ) +{ + int result; + result = 1; + while( exp ) + { + if( exp & 1 ) + result *= base; + exp >>= 1; + base *= base; + } + return result; +} + +static inline CC_ALWAYSINLINE int64_t ccPowInt64( int64_t base, int exp ) +{ + int result; + result = 1; + while( exp ) + { + if( exp & 1 ) + result *= base; + exp >>= 1; + base *= base; + } + return result; +} + + +//// + + +static inline CC_ALWAYSINLINE uint8_t ccMergeIntMask8( uint8_t i0, uint8_t i1, uint8_t mask ) +{ + return (uint8_t)(i0 ^ ( ( i0 ^ i1 ) & mask )); +} + +static inline CC_ALWAYSINLINE uint16_t ccMergeIntMask16( uint16_t i0, uint16_t i1, uint16_t mask ) +{ + return (uint16_t)(i0 ^ ( ( i0 ^ i1 ) & mask )); +} + +static inline CC_ALWAYSINLINE uint32_t ccMergeIntMask32( uint32_t i0, uint32_t i1, uint32_t mask ) +{ + return i0 ^ ( ( i0 ^ i1 ) & mask ); +} + +static inline CC_ALWAYSINLINE uint64_t ccMergeIntMask64( uint64_t i0, uint64_t i1, uint64_t mask ) +{ + return i0 ^ ( ( i0 ^ i1 ) & mask ); +} + +#if CPUCONF_LONG_SIZE == 8 + #define ccMergeIntMaskL(v) ccMergeIntMask64(v) +#else + #define ccMergeIntMaskL(v) ccMergeIntMask32(v) +#endif + + +//// + + +static inline CC_ALWAYSINLINE int ccCountBits64( uint64_t i ) +{ + int r; + for( r = 0 ; i ; r++ ) + i &= i - 1; + return r; +} + + +static inline CC_ALWAYSINLINE int ccCountBits32( uint32_t v ) +{ + int c; + v = v - ( ( v >> 1 ) & 0x55555555 ); + v = ( v & 0x33333333 ) + ( ( v >> 2 ) & 0x33333333 ); + c = ( ( ( v + ( v >> 4 ) ) & 0xF0F0F0F ) * 0x1010101 ) >> 24; + return c; +} + + +//// + + +static inline CC_ALWAYSINLINE int ccTrailingCount32( uint32_t v ) +{ + int c; + if( v & 0x1 ) + c = 0; + else + { + c = 1; + if( !( v & 0xffff ) ) + { + v >>= 16; + c += 16; + } + if( !( v & 0xff ) ) + { + v >>= 8; + c += 8; + } + if( !( v & 0xf ) ) + { + v >>= 4; + c += 4; + } + if( !( v & 0x3 ) ) + { + v >>= 2; + c += 2; + } + c -= v & 0x1; + } + return c; +} + + +static inline CC_ALWAYSINLINE int ccTrailingCount64( uint64_t v ) +{ + int c; + if( v & 0x1 ) + c = 0; + else + { + c = 1; + if( !( v & 0xffffffff ) ) + { + v >>= 32; + c += 32; + } + if( !( v & 0xffff ) ) + { + v >>= 16; + c += 16; + } + if( !( v & 0xff ) ) + { + v >>= 8; + c += 8; + } + if( !( v & 0xf ) ) + { + v >>= 4; + c += 4; + } + if( !( v & 0x3 ) ) + { + v >>= 2; + c += 2; + } + c -= v & 0x1; + } + return c; +} + + +//// + + +static inline CC_ALWAYSINLINE uint32_t ccReverseBits32( uint32_t value ) +{ + uint32_t result; + int shift; + + result = value; + shift = 32-1; + for( value >>= 1 ; value ; value >>= 1 ) + { + result <<= 1; + result |= value & 1; + shift--; + } + result <<= shift; + + return result; +} + +static inline CC_ALWAYSINLINE uint64_t ccReverseBits64( uint64_t value ) +{ + uint64_t result; + int shift; + + result = value; + shift = 64-1; + for( value >>= 1 ; value ; value >>= 1 ) + { + result <<= 1; + result |= value & 1; + shift--; + } + result <<= shift; + + return result; +} + +static inline CC_ALWAYSINLINE uint32_t ccReverseBitsVar32( uint32_t value, int numbits ) +{ + uint32_t result; + int shift; + + value &= ( ((uint32_t)1) << numbits ) - 1; + result = value; + shift = 32-1; + for( value >>= 1 ; value ; value >>= 1 ) + { + result <<= 1; + result |= value & 1; + shift--; + } + result <<= shift; + result >>= 32 - numbits; + + return result; +} + +static inline CC_ALWAYSINLINE uint64_t ccReverseBitsVar64( uint64_t value, int numbits ) +{ + uint64_t result; + int shift; + + value &= ( ((uint64_t)1) << numbits ) - 1; + result = value; + shift = 64-1; + for( value >>= 1 ; value ; value >>= 1 ) + { + result <<= 1; + result |= value & 1; + shift--; + } + result <<= shift; + result >>= 64 - numbits; + + return result; +} + + +//// + + +static inline CC_ALWAYSINLINE uint8_t ccIsPow2Int8( uint8_t v ) +{ + return ( ( v & ( v - 1 ) ) == 0 ); +} + +static inline CC_ALWAYSINLINE uint16_t ccIsPow2Int16( uint16_t v ) +{ + return ( ( v & ( v - 1 ) ) == 0 ); +} + +static inline CC_ALWAYSINLINE uint32_t ccIsPow2Int32( uint32_t v ) +{ + return ( ( v & ( v - 1 ) ) == 0 ); +} + +static inline CC_ALWAYSINLINE uint64_t ccIsPow2Int64( uint64_t v ) +{ + return ( ( v & ( v - 1 ) ) == 0 ); +} + + +static inline CC_ALWAYSINLINE uint8_t ccPow2Round8( uint8_t v ) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v++; + return v; +} + +static inline CC_ALWAYSINLINE uint16_t ccPow2Round16( uint16_t v ) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v++; + return v; +} + +static inline CC_ALWAYSINLINE uint32_t ccPow2Round32( uint32_t v ) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +static inline CC_ALWAYSINLINE uint64_t ccPow2Round64( uint64_t v ) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v++; + return v; +} + +#if CPUCONF_LONG_SIZE == 8 + #define ccPow2RoundL(v) ccPow2Round64(v) +#else + #define ccPow2RoundL(v) ccPow2Round32(v) +#endif + + +//// + + +static inline uint32_t ccDivMod10in32( uint32_t in, uint32_t *mod ) +{ + uint32_t x, q, div; + x = ( in | 1 ) - ( in >> 2 ); + q = ( x >> 4 ) + x; + x = q; + q = ( q >> 8 ) + x; + q = ( q >> 16 ) + ( x >> 8 ) + x; + q = ( q >> 8 ) + x; + div = ( q >> 3 ); + *mod = in - ( ( q & ~0x7 ) + ( div << 1 ) ); + return div; +} + + +//// + + +static inline CC_ALWAYSINLINE uint32_t ccTestNullByte32( uint32_t v ) +{ + return ( v - 0x01010101 ) & ~v & 0x80808080; +} + +static inline CC_ALWAYSINLINE uint64_t ccTestNullByte64( uint64_t v ) +{ + return ( v - 0x0101010101010101ULL ) & ~v & 0x8080808080808080ULL; +} + +static inline CC_ALWAYSINLINE uint32_t ccSignBit32( uint32_t v ) +{ + return v >> 31; +} + +static inline CC_ALWAYSINLINE uint64_t ccSignBit64( uint64_t v ) +{ + return v >> 63; +} + +static inline CC_ALWAYSINLINE uint32_t ccAbs32( int32_t v ) +{ + int32_t mask; + mask = v >> 31; + return ( v ^ mask ) - mask; +} + +static inline CC_ALWAYSINLINE uint64_t ccAbs64( int64_t v ) +{ + int64_t mask; + mask = (int32_t)(v >> 63); + return ( v ^ mask ) - mask; +} + + +//// + + +static inline CC_ALWAYSINLINE int32_t ccMortonNumber32( int32_t x, int32_t y ) +{ + int i; + uint32_t z; + z = 0; + for( i = 0 ; i < 16 ; i++ ) + { + z |= ( x & ( ((uint32_t)1) << i ) ) << i; + z |= ( y & ( ((uint32_t)1) << i ) ) << ( i + 1 ); + } + return z; +} + +static inline CC_ALWAYSINLINE int64_t ccMortonNumber64( int32_t x, int32_t y ) +{ + int i; + uint64_t z; + z = 0; + for( i = 0 ; i < 16 ; i++ ) + { + z |= ( x & ( ((uint64_t)1) << i ) ) << i; + z |= ( y & ( ((uint64_t)1) << i ) ) << ( i + 1 ); + } + return z; +} + + +//// + + +#define CC_FLT_INT_MAPPING + +#if CPUCONF_FLOAT_SIZE == 4 +typedef uint32_t ccuintf; +#elif CPUCONF_FLOAT_SIZE == 8 +typedef uint64_t ccuintf; +#else + #undef CC_FLT_INT_MAPPING +#endif + +#if CPUCONF_DOUBLE_SIZE == 4 +typedef uint32_t ccuintd; +#elif CPUCONF_DOUBLE_SIZE == 8 +typedef uint64_t ccuintd; +#else + #undef CC_FLT_INT_MAPPING +#endif + + +#ifdef CC_FLT_INT_MAPPING + +static inline CC_ALWAYSINLINE ccuintf ccFloatToUint( float f ) +{ + void *p = &f; + return *((ccuintf *)p); +} + +static inline CC_ALWAYSINLINE float ccUintToFloat( ccuintf f ) +{ + void *p = &f; + return *((float *)p); +} + + +static inline CC_ALWAYSINLINE ccuintd ccDoubleToUint( double d ) +{ + void *p = &d; + return *((ccuintd *)p); +} + +static inline CC_ALWAYSINLINE double ccUintToDouble( ccuintd d ) +{ + void *p = &d; + return *((double *)p); +} + +#endif + + +//// + + +#define CC_LOG2_E 1.4426950408889634073599246810018921 + +static inline CC_ALWAYSINLINE float ccFastExpFloat( float x ) +{ + union + { + uint32_t i; + float f; + } u; + if( x > 88.0 ) + return expf( x ); + else if( x < -80.0 ) + return 0.0; + u.i = (int32_t)( x * ( (float)0x800000 * (float)CC_LOG2_E ) ) + ( 0x3f800000 - 486408 ); + return u.f; +} + +static inline CC_ALWAYSINLINE float ccFastExpFloatNearZero( float x ) +{ + union + { + uint32_t i; + float f; + } u; + if( x > 88.0 ) + return expf( x ); + else if( x < -80.0 ) + return 0.0; + u.i = (int32_t)( x * ( (float)0x800000 * (float)CC_LOG2_E ) ) + 0x3f800000; + return u.f; +} + +static inline CC_ALWAYSINLINE double ccFastExpDouble( double x ) +{ +#if CPUCONF_WORD_SIZE >= 64 + union + { + uint64_t i; + double d; + } u; + if( x > 88.0 ) + return exp( x ); + else if( x < -80.0 ) + return 0.0; + u.i = (int64_t)( x * ( (double)0x10000000000000 * CC_LOG2_E ) ) + ( (uint64_t)0x3ff0000000000000 - (uint64_t)261138306564096 ); + return u.d; +#else + union + { + uint32_t i[2]; + double d; + } u; + if( x > 88.0 ) + return expf( (float)x ); + else if( x < -80.0 ) + return 0.0; + #ifdef CPUCONF_LITTLE_ENDIAN + u.i[1] = (int32_t)( x * ( (double)0x100000 * CC_LOG2_E ) ) + ( 0x3ff00000 - 60801 ); + u.i[0] = 0; + #else + u.i[0] = (int32_t)( x * ( (double)0x100000 * CC_LOG2_E ) ) + ( 0x3ff00000 - 60801 ); + u.i[1] = 0; + #endif + return u.d; +#endif +} + +static inline CC_ALWAYSINLINE double ccFastExpDoubleNearZero( double x ) +{ +#if CPUCONF_WORD_SIZE >= 64 + union + { + uint64_t i; + double d; + } u; + if( x > 88.0 ) + return exp( x ); + else if( x < -80.0 ) + return 0.0; + u.i = (int64_t)( x * ( (double)0x10000000000000 * CC_LOG2_E ) ) + (uint64_t)0x3ff0000000000000; + return u.d; +#else + union + { + uint32_t i[2]; + double d; + } u; + if( x > 88.0 ) + return expf( (float)x ); + else if( x < -80.0 ) + return 0.0; + #ifdef CPUCONF_LITTLE_ENDIAN + u.i[1] = (int32_t)( x * ( (double)0x100000 * CC_LOG2_E ) ) + 0x3ff00000; + u.i[0] = 0; + #else + u.i[0] = (int32_t)( x * ( (double)0x100000 * CC_LOG2_E ) ) + 0x3ff00000; + u.i[1] = 0; + #endif + return u.d; +#endif +} + + +//// + + +static inline CC_ALWAYSINLINE float ccFastLog2Float( float x ) +{ + int base; + union + { + uint32_t i; + float f; + } u; + u.f = x; + base = ( ( u.i >> 23 ) & 0xff ) - 0x80; + u.i &= ~( (uint32_t)0xff << 23 ); + u.i += (uint32_t)0x7f << 23; + return (float)base + ( u.f * ( 2.0f + u.f * ( -1.0f/3.0f ) ) ) - ( 2.0f/3.0f ); +} + +static inline CC_ALWAYSINLINE float ccFastLog2Double( double x ) +{ +#if CPUCONF_WORD_SIZE >= 64 + int base; + union + { + uint64_t i; + double f; + } u; + u.f = x; + base = (int)(( ( u.i >> 52 ) & 0x7ff ) - 0x400); + u.i &= ~( (uint64_t)0x7ff << 52 ); + u.i += (uint64_t)0x3ff << 52; +#else + int base; + union + { + uint32_t i[2]; + double f; + } u; + u.f = x; + base = ( ( u.i[1] >> 20 ) & 0x7ff ) - 0x400; + u.i[1] &= ~( (uint32_t)0x7ff << 20 ); + u.i[1] += (uint32_t)0x3ff << 20; +#endif + return (double)base + ( u.f * ( 2.0 + u.f * ( -1.0/3.0 ) ) ) - ( 2.0/3.0 ); +} + + +//// + + +/* Only valid between -M_PI and M_PI */ +static inline CC_ALWAYSINLINE float ccFastSinFloat( float x ) +{ + float s; + s = (float)(( 1.27323954474 * x ) + ( -0.405284734569 * x * fabsf( x ) )); + return s; +} + +/* Only valid between -M_PI and M_PI */ +static inline CC_ALWAYSINLINE double ccFastSinDouble( double x ) +{ + double s; + s = ( 1.27323954474 * x ) + ( -0.405284734569 * x * fabs( x ) ); + return s; +} + + +//// + + +#define CC_INT16_BSWAP(i) (__extension__({uint16_t bsw=(i);((bsw&0xff)<<8)|(bsw>>8);})) +#define CC_INT32_BSWAP(i) (__extension__({uint32_t bsw=(i);(bsw<<24)|((bsw&0xff00)<<8)|((bsw>>8)&0xff00)|(bsw>>24);})) +#define CC_INT64_BSWAP(i) (__extension__({uint64_t bsw=(i);(bsw>>56)|((bsw&0x00ff000000000000LL)>>40)|((bsw&0x0000ff0000000000LL)>>24)|((bsw&0x000000ff00000000LL)>>8)|((bsw&0x00000000ff000000LL)<<8)|((bsw&0x0000000000ff0000LL)<<24)|((bsw&0x000000000000ff00LL)<<40)|(bsw<<56);})) + + +static inline CC_ALWAYSINLINE uint16_t ccByteSwap16( uint16_t i ) +{ + return (uint16_t)(CC_INT16_BSWAP( i )); +} + +#if defined(__GNUC__) && defined(__i386__) + +static inline CC_ALWAYSINLINE uint32_t ccByteSwap32( uint32_t i ) +{ + __asm__( "bswap %0" : "=r" (i) : "0" (i) ); + return i; +} + +static inline CC_ALWAYSINLINE uint64_t ccByteSwap64( uint64_t i ) +{ + union { + uint32_t s[2]; + uint64_t i; + } u; + u.i = i; + __asm__( "bswapl %0 ; bswapl %1 ; xchgl %0,%1" : "=r" (u.s[0]), "=r" (u.s[1]) : "0" (u.s[0]), "1" (u.s[1]) ); + return u.i; +} + +#elif defined(__GNUC__) && defined(__x86_64__) + +static inline CC_ALWAYSINLINE uint32_t ccByteSwap32( uint32_t i ) +{ + __asm__( "bswapl %0" : "=r" (i) : "0" (i) ); + return i; +} + +static inline CC_ALWAYSINLINE uint64_t ccByteSwap64( uint64_t i ) +{ + __asm__( "bswapq %0" : "=r" (i) : "0" (i) ); + return i; +} + +#else + +static inline CC_ALWAYSINLINE uint32_t ccByteSwap32( uint32_t i ) +{ + return CC_INT32_BSWAP( i ); +} + +static inline CC_ALWAYSINLINE uint64_t ccByteSwap64( uint64_t i ) +{ + return CC_INT64_BSWAP( i ); +} + +#endif + +static inline CC_ALWAYSINLINE float ccByteSwapf( float f ) +{ + uint32_t i; + void *p; + p = &f; + i = ccByteSwap32( *((uint32_t *)p) ); + p = &i; + return *((float *)p); +} + +static inline CC_ALWAYSINLINE double ccByteSwapd( double f ) +{ + uint64_t i; + void *p; + p = &f; + i = ccByteSwap64( *((uint64_t *)p) ); + p = &i; + return *((double *)p); +} + + +//// + + +static inline CC_ALWAYSINLINE uint32_t ccAlignInt32( uint32_t i ) +{ + i--; + i |= i >> 1; + i |= i >> 2; + i |= i >> 4; + i |= i >> 8; + i |= i >> 16; + return i + 1; +} + +static inline CC_ALWAYSINLINE uint64_t ccAlignInt64( uint64_t i ) +{ + i--; + i |= i >> 1; + i |= i >> 2; + i |= i >> 4; + i |= i >> 8; + i |= i >> 16; + i |= i >> 32; + return i + 1; +} + +static inline CC_ALWAYSINLINE uintptr_t ccAlignIntPtr( uintptr_t i ) +{ + i--; + i |= i >> 1; + i |= i >> 2; + i |= i >> 4; + i |= i >> 8; + i |= i >> 16; +#if CPUCONF_INTPTR_BITS > 32 + i |= i >> 32; +#endif + return i + 1; +} + + +//// + + +static inline CC_ALWAYSINLINE uint8_t ccRotateLeft8( uint8_t x, int bits ) +{ + return ( x << bits ) | ( x >> ( 8 - bits ) ); +} + +static inline CC_ALWAYSINLINE uint16_t ccRotateLeft16( uint16_t x, int bits ) +{ + return ( x << bits ) | ( x >> ( 16 - bits ) ); +} + +static inline CC_ALWAYSINLINE uint32_t ccRotateLeft32( uint32_t x, int bits ) +{ + return ( x << bits ) | ( x >> ( 32 - bits ) ); +} + +static inline CC_ALWAYSINLINE uint64_t ccRotateLeft64( uint64_t x, int bits ) +{ + return ( x << bits ) | ( x >> ( 64 - bits ) ); +} + + +static inline CC_ALWAYSINLINE uint8_t ccRotateRight8( uint8_t x, int bits ) +{ + return ( x >> bits ) | ( x << ( 8 - bits ) ); +} + +static inline CC_ALWAYSINLINE uint16_t ccRotateRight16( uint16_t x, int bits ) +{ + return ( x >> bits ) | ( x << ( 16 - bits ) ); +} + +static inline CC_ALWAYSINLINE uint32_t ccRotateRight32( uint32_t x, int bits ) +{ + return ( x >> bits ) | ( x << ( 32 - bits ) ); +} + +static inline CC_ALWAYSINLINE uint64_t ccRotateRight64( uint64_t x, int bits ) +{ + return ( x >> bits ) | ( x << ( 64 - bits ) ); +} + + +//// + + +static inline CC_ALWAYSINLINE void ccQuickCipherPerm32( uint32_t *data, int datacount, uint32_t c0, uint32_t c1 ) +{ + int i; + uint32_t c0accum; + c0accum = c0; + for( i = 0 ; i < datacount ; i++, c0accum += c0 ) + data[i] = ( data[i] + c0accum ) ^ c1; + return; +} + +static inline CC_ALWAYSINLINE void ccQuickCipherPerm32Inv( uint32_t *data, int datacount, uint32_t c0, uint32_t c1 ) +{ + int i; + uint32_t c0accum; + c0accum = c0; + for( i = 0 ; i < datacount ; i++, c0accum += c0 ) + data[i] = ( data[i] ^ c1 ) - c0accum; + return; +} + +static inline CC_ALWAYSINLINE void ccQuickCipherRot32( uint32_t *data, int datacount, uint32_t c0, int rotbits ) +{ + int i; + uint32_t c0accum; + c0accum = c0; + for( i = 0 ; i < datacount ; i++, c0accum += c0 ) + data[i] = ccRotateLeft32( data[i] + c0accum, rotbits ); + return; +} + +static inline CC_ALWAYSINLINE void ccQuickCipherRot32Inv( uint32_t *data, int datacount, uint32_t c0, int rotbits ) +{ + int i; + uint32_t c0accum; + c0accum = c0; + for( i = 0 ; i < datacount ; i++, c0accum += c0 ) + data[i] = ccRotateRight32( data[i], rotbits ) - c0accum; + return; +} + +static inline CC_ALWAYSINLINE void ccQuickCipherMix32( uint32_t *data, int datacount, uint32_t mix, uint32_t c0, uint32_t c1 ) +{ + int i; + uint32_t block, newblock, carry, mixinv; + mixinv = ~mix; + block = data[datacount-1]; + carry = block & mix; + for( i = 0 ; i < datacount ; i++ ) + { + block = data[i]; + newblock = ( block & mixinv ) | carry; + carry = block & mix; + data[i] = ( newblock + c0 ) ^ c1; + } + return; +} + +static inline CC_ALWAYSINLINE void ccQuickCipherMix32Inv( uint32_t *data, int datacount, uint32_t mix, uint32_t c0, uint32_t c1 ) +{ + int i; + uint32_t block, newblock, carry, mixinv; + mixinv = ~mix; + block = ( data[0] ^ c1 ) - c0; + carry = block & mix; + for( i = datacount-1 ; i >= 0 ; i-- ) + { + block = ( data[i] ^ c1 ) - c0; + newblock = ( block & mixinv ) | carry; + carry = block & mix; + data[i] = newblock; + } + return; +} + +static inline CC_ALWAYSINLINE void ccQuickCipherPerm64( uint64_t *data, int datacount, uint64_t c0, uint64_t c1 ) +{ + int i; + uint64_t c0accum; + c0accum = c0; + for( i = 0 ; i < datacount ; i++, c0accum += c0 ) + data[i] = ( data[i] + c0accum ) ^ c1; + return; +} + +static inline CC_ALWAYSINLINE void ccQuickCipherPerm64Inv( uint64_t *data, int datacount, uint64_t c0, uint64_t c1 ) +{ + int i; + uint64_t c0accum; + c0accum = c0; + for( i = 0 ; i < datacount ; i++, c0accum += c0 ) + data[i] = ( data[i] ^ c1 ) - c0accum; + return; +} + +static inline CC_ALWAYSINLINE void ccQuickCipherRot64( uint64_t *data, int datacount, uint64_t c0, int rotbits ) +{ + int i; + uint64_t c0accum; + c0accum = c0; + for( i = 0 ; i < datacount ; i++, c0accum += c0 ) + data[i] = ccRotateLeft64( data[i] + c0accum, rotbits ); + return; +} + +static inline CC_ALWAYSINLINE void ccQuickCipherRot64Inv( uint64_t *data, int datacount, uint64_t c0, int rotbits ) +{ + int i; + uint64_t c0accum; + c0accum = c0; + for( i = 0 ; i < datacount ; i++, c0accum += c0 ) + data[i] = ccRotateRight64( data[i], rotbits ) - c0accum; + return; +} + +static inline CC_ALWAYSINLINE void ccQuickCipherMix64( uint64_t *data, int datacount, uint64_t mix, uint64_t c0, uint64_t c1 ) +{ + int i; + uint64_t block, newblock, carry, mixinv; + mixinv = ~mix; + block = data[datacount-1]; + carry = block & mix; + for( i = 0 ; i < datacount ; i++ ) + { + block = data[i]; + newblock = ( block & mixinv ) | carry; + carry = block & mix; + data[i] = ( newblock + c0 ) ^ c1; + } + return; +} + +static inline CC_ALWAYSINLINE void ccQuickCipherMix64Inv( uint64_t *data, int datacount, uint64_t mix, uint64_t c0, uint64_t c1 ) +{ + int i; + uint64_t block, newblock, carry, mixinv; + mixinv = ~mix; + block = ( data[0] ^ c1 ) - c0; + carry = block & mix; + for( i = datacount-1 ; i >= 0 ; i-- ) + { + block = ( data[i] ^ c1 ) - c0; + newblock = ( block & mixinv ) | carry; + carry = block & mix; + data[i] = newblock; + } + return; +} + + +//// + + +#define CC_INT32_MAX ((((uint32_t)1)<<31)-1) + +static inline CC_ALWAYSINLINE int32_t ccFloatToInt32Sat( float f ) +{ + if( f >= (float)CC_INT32_MAX ) + return CC_INT32_MAX; + else if( f <= -(float)CC_INT32_MAX ) + return -CC_INT32_MAX; + else + return (int32_t)f; +} + + +static inline CC_ALWAYSINLINE int32_t ccDoubleToInt32Sat( double f ) +{ + if( f >= (double)CC_INT32_MAX ) + return CC_INT32_MAX; + else if( f <= -(double)CC_INT32_MAX ) + return -CC_INT32_MAX; + else + return (int32_t)f; +} + + +#define CC_INT64_MAX ((((uint64_t)1)<<63)-1) + +static inline CC_ALWAYSINLINE int64_t ccFloatToInt64Sat( float f ) +{ + if( f >= (float)CC_INT64_MAX ) + return CC_INT64_MAX; + else if( f <= -(float)CC_INT64_MAX ) + return -CC_INT64_MAX; + else + return (int64_t)f; +} + + +static inline CC_ALWAYSINLINE int64_t ccDoubleToInt64Sat( double f ) +{ + if( f >= (double)CC_INT64_MAX ) + return CC_INT64_MAX; + else if( f <= -(double)CC_INT64_MAX ) + return -CC_INT64_MAX; + else + return (int64_t)f; +} + + + +static inline CC_ALWAYSINLINE float ccFastNextAfterPositivef( float f ) +{ + ccuintf i; + i = ccFloatToUint( f ); + i++; + f = ccUintToFloat( i ); + return f; +} + + +//// + +//// + + +void ccQuickSort( void **table, int count, int (*sortfunc)( void *t0, void *t1 ), uint32_t randmask ); +void ccQuickSortContext( void **table, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context, uint32_t randmask ); + +int ccMergeSort( void **src, void **tmp, int count, int (*sortfunc)( void *t0, void *t1 ) ); +int ccMergeSortContext( void **src, void **tmp, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context ); + +void ccHybridSort( void **table, void **tmp, int count, int (*sortfunc)( void *t0, void *t1 ), uint32_t randmask ); +void ccHybridSortContext( void **table, void **tmp, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context, uint32_t randmask ); + + +//// + + +void ccDebugLog( char *filename, char *string, ... ); + + +//// + + +typedef struct +{ + size_t allocsize; + size_t offset; + char *data; +} ccGrowth; + +void ccGrowthInit( ccGrowth *growth, int defaultsize ); +int ccGrowthPrintf( ccGrowth *growth, char *format, ... ); +int ccGrowthData( ccGrowth *growth, void *data, size_t size ); +int ccGrowthSeek( ccGrowth *growth, int offset ); +void ccGrowthFree( ccGrowth *growth ); +void ccGrowthElapsedTimeString( ccGrowth *growth, int64_t timecount, int maxfieldcount ); + + +//// + + +void *ccFileLoad( const char *path, size_t maxsize, size_t *retsize ); +size_t ccFileLoadDirect( const char *path, void *data, size_t minsize, size_t maxsize ); +int ccFileStore( const char *path, void *data, size_t datasize, int fsyncflag ); +int ccFileExists( char *path ); +int ccFileStat( char *path, size_t *retfilesize, time_t *retfiletime ); +int ccRenameFile( char *oldpath, char *newpath ); + + +//// + + +typedef struct _ccDir ccDir; + +ccDir *ccOpenDir( char *path ); +char *ccReadDir( ccDir *dir ); +void ccCloseDir( ccDir *dir ); + + +//// + + +int64_t ccGetFreeDiskSpace( char *dirpath ); + + +//// + + +int ccGetTimeOfDay( struct timeval *tv ); + +void ccSleep( int milliseconds ); + +static inline CC_ALWAYSINLINE uint64_t ccGetMillisecondsTime() +{ + struct timeval lntime; + ccGetTimeOfDay( &lntime ); + return ( (uint64_t)lntime.tv_sec * 1000 ) + ( (uint64_t)lntime.tv_usec / 1000 ); +} + +static inline CC_ALWAYSINLINE uint64_t ccGetMicrosecondsTime() +{ + struct timeval lntime; + ccGetTimeOfDay( &lntime ); + return ( (uint64_t)lntime.tv_sec * 1000000 ) + (uint64_t)lntime.tv_usec; +} + +static inline CC_ALWAYSINLINE uint64_t ccGetNanosecondsTime() +{ + struct timeval lntime; + ccGetTimeOfDay( &lntime ); + return ( (uint64_t)lntime.tv_sec * 1000000000 ) + ( (uint64_t)lntime.tv_usec * 1000 ); +} + + +//// + + +/* Returned string must be free()d */ +char *ccGetSystemName(); + + +#endif + diff --git a/ecere/src/gfx/newFonts/cc/cchybridsort.h b/ecere/src/gfx/newFonts/cc/cchybridsort.h new file mode 100644 index 0000000000..9be169669a --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/cchybridsort.h @@ -0,0 +1,302 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +/* + +Templates C style! + +#include this whole file with the following definitions set: + +#define HSORT_MAIN MyInlinedSortFunction +#define HSORT_CMP MyComparisonFunction +#define HSORT_TYPE int + +*/ + + +#ifndef CC_HSORT_INLINE + + #define CC_HSORT_SWAP(a,b) (__extension__({temp=table[b];table[b]=table[a];table[a]=temp;})) + #define CC_HSORT_STACK_DEPTH (512) + #define CC_HSORT_MIN_QSORT_COUNT (5) + +typedef struct +{ + void *table; + int count; + int depth; +} ccHybridSortStack; + + #define CC_HSORT_INLINE + #define CC_HSORT_STACK_DEPTH (512) + +#endif + + +#ifndef HSORT_COPY + #define HSORT_COPY(d,s) (*(d)=*(s)) + #define CC_HSORT_COPY +#endif + + +#ifdef HSORT_CONTEXT + #define HSORT_CONTEXT_PARAM , HSORT_CONTEXT context + #define HSORT_CONTEXT_PASS context, + #define HSORT_CONTEXT_PASSLAST , context +#else + #define HSORT_CONTEXT_PARAM + #define HSORT_CONTEXT_PASS + #define HSORT_CONTEXT_PASSLAST +#endif + + +/* Build merge sort pipeline */ +#define HSORT_MERGE_FUNCX(n) n##Merge +#define HSORT_MERGE_FUNC(n) HSORT_MERGE_FUNCX(n) +#define MSORT_MAIN HSORT_MERGE_FUNC(HSORT_MAIN) +#define MSORT_CMP HSORT_CMP +#define MSORT_TYPE HSORT_TYPE +#ifdef HSORT_CONTEXT + #define MSORT_CONTEXT HSORT_CONTEXT +#endif +#include "ccmergesort.h" +#undef MSORT_MAIN +#undef MSORT_CMP +#undef MSORT_TYPE +#ifdef HSORT_CONTEXT + #undef MSORT_CONTEXT +#endif + + +#define HSORT_PART_FUNCX(n) n##Part +#define HSORT_PART_FUNC(n) HSORT_PART_FUNCX(n) + +static void HSORT_PART_FUNC(HSORT_MAIN)( HSORT_TYPE *table, int count HSORT_CONTEXT_PARAM ) +{ + HSORT_TYPE temp; + if( count == 4 ) + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[1] ) ) + CC_HSORT_SWAP( 1, 0 ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[2], &table[3] ) ) + CC_HSORT_SWAP( 3, 2 ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[2] ) ) + { + temp = table[2]; + table[2] = table[1]; + table[1] = table[0]; + table[0] = temp; + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[2], &table[3] ) ) + { + CC_HSORT_SWAP( 3, 2 ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[1], &table[2] ) ) + CC_HSORT_SWAP( 2, 1 ); + } + } + else + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[1], &table[2] ) ) + { + CC_HSORT_SWAP( 2, 1 ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[2], &table[3] ) ) + CC_HSORT_SWAP( 3, 2 ); + } + } + } + else if( count == 3 ) + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[1] ) ) + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[1], &table[2] ) ) + { + /* [1]>[0], [2]>[1] = [2]>[1]>[0] */ + CC_HSORT_SWAP( 2, 0 ); + } + else + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[2] ) ) + { + /* [1]>[0], [2]<[1], [2]>[0] = [1]>[2]>[0] */ + temp = table[0]; + table[0] = table[1]; + table[1] = table[2]; + table[2] = temp; + } + else + { + /* [1]>[0], [2]<[1], [2]<[0] = [1]>[0]>[2] */ + CC_HSORT_SWAP( 1, 0 ); + } + } + } + else + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[1], &table[2] ) ) + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[2] ) ) + { + /* [1]<[0], [2]>[1], [2]>[0] = [2]>[0]>[1] */ + temp = table[2]; + table[2] = table[1]; + table[1] = table[0]; + table[0] = temp; + } + else + { + /* [1]<[0], [2]>[1], [2]<[0] = [0]>[2]>[1] */ + CC_HSORT_SWAP( 1, 2 ); + } + } + else + { + /* [1]<[0], [2]<[1] = [0]>[1]>[2] */ + } + } + } + else if( count == 2 ) + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[1] ) ) + CC_HSORT_SWAP( 1, 0 ); + } + return; +} + + +static void HSORT_MAIN( HSORT_TYPE *table, HSORT_TYPE *tmp, int count HSORT_CONTEXT_PARAM, uint32_t randmask ) +{ + int depth, depthmax, tmpcountalloc; + ssize_t i, pivotindex, leftcount, rightcount, highindex, pivotoffset; + HSORT_TYPE temp; + HSORT_TYPE *mergesrc; + HSORT_TYPE pivot; + ccHybridSortStack stack[CC_HSORT_STACK_DEPTH]; + ccHybridSortStack *sp; +#ifdef HSORT_PIVOT_STORE + HSORT_PIVOT_STORE pivotstore; +#endif + + if( count <= 1 ) + return; + + depth = 0; + depthmax = 1 + ccLog2Int32( count ); + tmpcountalloc = count; + + sp = stack; + for( ; ; ) + { + if( count < CC_HSORT_MIN_QSORT_COUNT ) + { + HSORT_PART_FUNC(HSORT_MAIN)( table, count HSORT_CONTEXT_PASSLAST ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + depth = sp->depth; + continue; + } + + /* Pathological case, switch to merge sort */ + if( depth >= depthmax ) + { + if( !( tmp ) ) + { + tmp = malloc( tmpcountalloc * sizeof(HSORT_TYPE) ); + tmpcountalloc = 0; + } + mergesrc = HSORT_MERGE_FUNC(HSORT_MAIN)( table, tmp, count HSORT_CONTEXT_PASSLAST ); + if( mergesrc != table ) + memcpy( table, mergesrc, count * sizeof(HSORT_TYPE) ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + depth = sp->depth; + continue; + } + + /* Select pivot */ + randmask += count; + pivotindex = 1 + ( randmask % ( count-2 ) ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[pivotindex] ) ) + CC_HSORT_SWAP( pivotindex, 0 ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[pivotindex], &table[count-1] ) ) + { + CC_HSORT_SWAP( count-1, pivotindex ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[pivotindex] ) ) + CC_HSORT_SWAP( pivotindex, 0 ); + } + + /* Quick sort on both sides of the pivot */ + pivot = table[pivotindex]; + highindex = count - 2; + pivotoffset = highindex; + CC_HSORT_SWAP( pivotoffset, pivotindex ); + pivotindex = 1; +#ifdef HSORT_PIVOT_STORE + HSORT_PIVOT_INIT( HSORT_CONTEXT_PASS &pivotstore, &pivot ); +#endif + for( i = highindex ; --i ; ) + { + /* Optional optimization for constant pivot */ +#ifdef HSORT_PIVOT_STORE + if( HSORT_PIVOT_CMP( HSORT_CONTEXT_PASS &pivotstore, &pivot, &table[pivotindex] ) ) +#else + if( HSORT_CMP( HSORT_CONTEXT_PASS &pivot, &table[pivotindex] ) ) +#endif + pivotindex++; + else + { + highindex--; + CC_HSORT_SWAP( highindex, pivotindex ); + } + } + CC_HSORT_SWAP( pivotindex, pivotoffset ); + + /* Count of entries on both sides of the pivot */ + leftcount = pivotindex; + pivotindex++; + rightcount = count - pivotindex; + + /* Fast sort small chunks, iterate */ + if( leftcount < rightcount ) + { + depth++; + sp->table = &table[pivotindex]; + sp->count = (int)rightcount; + sp->depth = depth; + sp++; + count = (int)leftcount; + } + else + { + depth++; + sp->table = table; + sp->count = (int)leftcount; + sp->depth = depth; + sp++; + table += pivotindex; + count = (int)rightcount; + } + } + + if( !( tmpcountalloc ) ) + free( tmp ); + + return; +} + + +#ifdef CC_HSORT_COPY + #undef HSORT_COPY + #undef CC_HSORT_COPY +#endif + +#undef HSORT_CONTEXT_PARAM +#undef HSORT_CONTEXT_PASS +#undef HSORT_CONTEXT_PASSLAST + diff --git a/ecere/src/gfx/newFonts/cc/ccmergesort.h b/ecere/src/gfx/newFonts/cc/ccmergesort.h new file mode 100644 index 0000000000..4c95358a7b --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/ccmergesort.h @@ -0,0 +1,215 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +/* + +Templates C style! + +#include this whole file with the following definitions set: + +#define MSORT_MAIN MyInlinedSortFunction +#define MSORT_CMP MyComparisonFunction +#define MSORT_TYPE int + +*/ + + +#ifndef CC_MSORT_INLINE + +typedef struct +{ + void *src; + void *dst; + int count; + int mergeflag; + int depthbit; +} ccMergeSortStack; + + #define CC_MSORT_INLINE + #define CC_MSORT_STACK_DEPTH (512) + +#endif + + +#ifndef MSORT_COPY + #define MSORT_COPY(d,s) (*(d)=*(s)) + #define CC_MSORT_COPY +#endif + + +#ifdef MSORT_CONTEXT + #define MSORT_CONTEXT_PARAM , MSORT_CONTEXT context + #define MSORT_CONTEXT_PASS context, + #define MSORT_CONTEXT_PASSLAST , context +#else + #define MSORT_CONTEXT_PARAM + #define MSORT_CONTEXT_PASS + #define MSORT_CONTEXT_PASSLAST +#endif + + +static MSORT_TYPE *MSORT_MAIN( MSORT_TYPE *src, MSORT_TYPE *tmp, int count MSORT_CONTEXT_PARAM ) +{ + int swapflag, depthbit, maxdepthbit; + ssize_t leftcount, rightcount; + MSORT_TYPE *dst; + MSORT_TYPE *sl; + MSORT_TYPE *sr; + MSORT_TYPE *dstend; + MSORT_TYPE *dstbase; + MSORT_TYPE *swap; + MSORT_TYPE temp; + ccMergeSortStack stack[CC_MSORT_STACK_DEPTH]; + ccMergeSortStack *sp; + + if( count <= 1 ) + return src; + + dst = tmp; + sp = stack; + swapflag = 0; + depthbit = 0; + + leftcount = count; + for( maxdepthbit = 1 ; ; maxdepthbit ^= 1 ) + { + leftcount = leftcount - ( leftcount >> 1 ); + if( leftcount <= 4 ) + break; + } + + for( ; ; ) + { + if( count <= 4 ) + { + if( !( depthbit ^ maxdepthbit ) ) + { + if( ( count == 4 ) && MSORT_CMP( MSORT_CONTEXT_PASS &src[2], &src[3] ) ) + { + MSORT_COPY( &temp, &src[2] ); + MSORT_COPY( &src[2], &src[3] ); + MSORT_COPY( &src[3], &temp ); + } + if( MSORT_CMP( MSORT_CONTEXT_PASS &src[0], &src[1] ) ) + { + MSORT_COPY( &temp, &src[0] ); + MSORT_COPY( &src[0], &src[1] ); + MSORT_COPY( &src[1], &temp ); + } + swapflag = 0; + } + else + { + if( count == 4 ) + { + if( MSORT_CMP( MSORT_CONTEXT_PASS &src[2], &src[3] ) ) + { + MSORT_COPY( &dst[2], &src[3] ); + MSORT_COPY( &dst[3], &src[2] ); + } + else + { + MSORT_COPY( &dst[2], &src[2] ); + MSORT_COPY( &dst[3], &src[3] ); + } + } + else if( count == 3 ) + MSORT_COPY( &dst[2], &src[2] ); + if( MSORT_CMP( MSORT_CONTEXT_PASS &src[0], &src[1] ) ) + { + MSORT_COPY( &dst[0], &src[1] ); + MSORT_COPY( &dst[1], &src[0] ); + } + else + { + MSORT_COPY( &dst[0], &src[0] ); + MSORT_COPY( &dst[1], &src[1] ); + } + swap = src; + src = dst; + dst = swap; + swapflag = 1; + } + } + else + { + rightcount = count >> 1; + leftcount = count - rightcount; + sp->src = src; + sp->dst = dst; + sp->count = count; + sp->mergeflag = 1; + sp->depthbit = depthbit; + depthbit ^= 1; + sp++; + sp->src = src + leftcount; + sp->dst = dst + leftcount; + sp->count = (int)rightcount; + sp->mergeflag = 0; + sp->depthbit = depthbit; + sp++; + count = (int)leftcount; + continue; + } + + for( ; ; ) + { + rightcount = count >> 1; + leftcount = count - rightcount; + sl = src; + sr = src + leftcount; + dstbase = dst; + for( ; ; ) + { + if( MSORT_CMP( MSORT_CONTEXT_PASS sl, sr ) ) + { + MSORT_COPY( dst++, sr++ ); + if( --rightcount ) + continue; + for( dstend = &dst[leftcount] ; dst < dstend ; ) + MSORT_COPY( dst++, sl++ ); + break; + } + else + { + MSORT_COPY( dst++, sl++ ); + if( --leftcount ) + continue; + for( dstend = &dst[rightcount] ; dst < dstend ; ) + MSORT_COPY( dst++, sr++ ); + break; + } + } + if( sp == stack ) + return dstbase; + sp--; + src = sp->src; + dst = sp->dst; + count = sp->count; + depthbit = sp->depthbit; + if( !( sp->mergeflag ) ) + break; + swapflag ^= 1; + if( swapflag ) + { + src = sp->dst; + dst = sp->src; + } + } + } + + return 0; +} + + +#ifdef CC_MSORT_COPY + #undef MSORT_COPY + #undef CC_MSORT_COPY +#endif + +#undef MSORT_CONTEXT_PARAM +#undef MSORT_CONTEXT_PASS +#undef MSORT_CONTEXT_PASSLAST + diff --git a/ecere/src/gfx/newFonts/cc/ccstr.c b/ecere/src/gfx/newFonts/cc/ccstr.c new file mode 100644 index 0000000000..20e58e4445 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/ccstr.c @@ -0,0 +1,1466 @@ +/* ***************************************************************************** + * + * Copyright (c) 2007-2016 Alexis Naveros. + * Ecere Corporation has unlimited/unrestricted rights. + * ***************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) && !defined(__ANDROID__) +asm(".symver pow,pow@GLIBC_2.2.5"); +asm(".symver log,log@GLIBC_2.2.5"); +#endif + +#include "cc.h" +#include "ccstr.h" + + +//// + + +#define CC_CHAR_IS_CONTROL(c) ((c)<' ') +#define CC_CHAR_IS_DELIMITER(c) ((c)<=' ') + + +void ccStrLowCase( char *str, int length ) +{ + int i; + for( i = 0 ; i < length ; i++ ) + { + if( ( str[i] >= 'A' ) && ( str[i] <= 'Z' ) ) + str[i] += 'a' - 'A'; + } + return; +} + + +void ccStrLowCopy( char *dst, char *src, int length ) +{ + int i; + for( i = 0 ; i < length ; i++ ) + { + dst[i] = src[i]; + if( ( src[i] >= 'A' ) && ( src[i] <= 'Z' ) ) + dst[i] = src[i] + 'a' - 'A'; + } + dst[i] = 0; + return; +} + + +int ccStrCmpEqual( char *s0, char *s1 ) +{ + int i; + if( !( s0 ) ) + return ( s1 ? 0 : 1 ); + else if( !( s1 ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( s0[i] != s1[i] ) + return 0; + if( !( s0[i] ) ) + break; + } + return 1; +} + + +int ccStrCmpEqualTest( char *s0, char *s1 ) +{ + int i; + if( !( s0 ) ) + return ( s1 ? 0 : 1 ); + else if( !( s1 ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( s0[i] != s1[i] ) + return 0; + if( !( s0[i] ) ) + break; + } + return 1; +} + + +int ccStrCmpStdTest( char *s0, char *s1 ) +{ + int i; + if( !( s0 ) ) + return ( s1 ? -1 : 0 ); + else if( !( s1 ) ) + return 1; + for( i = 0 ; ; i++ ) + { + if( s0[i] != s1[i] ) + return( s0[i] > s1[i] ? 1 : -1 ); + if( !( s0[i] ) ) + break; + } + return 0; +} + + +int ccStrCpyStr( char *buffer, int buffersize, char *str ) +{ + int i; + char c; + if( !( buffersize ) ) + return 0; + buffersize--; + for( i = 0 ; i < buffersize ; i++ ) + { + c = str[i]; + if( !c ) + break; + buffer[i] = c; + } + buffer[i] = 0; + return i + 1; +} + + +char *ccStrCmpWord( char *str, char *word ) +{ + int i; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( !( word[i] ) ) + return str + i; + if( str[i] != word[i] ) + break; + } + return 0; +} + + +char *ccStrCmpWordIgnoreCase( char *str, char *word ) +{ + int i; + char c0, c1; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( !( word[i] ) ) + return str + i; + c0 = str[i]; + c1 = word[i]; + if( ( c0 >= 'A' ) && ( c0 <= 'Z' ) ) + c0 += 'a' - 'A'; + if( ( c1 >= 'A' ) && ( c1 <= 'Z' ) ) + c1 += 'a' - 'A'; + if( c0 != c1 ) + break; + } + return 0; +} + + +char *ccStrCmpSeq( char *str, char *seq, int seqlength ) +{ + int i; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( i >= seqlength ) + return str + i; + if( str[i] != seq[i] ) + break; + } + return 0; +} + + +char *ccStrCmpSeqIgnoreCase( char *str, char *seq, int seqlength ) +{ + int i; + char c0, c1; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( i >= seqlength ) + return str + i; + c0 = seq[i]; + c1 = str[i]; + if( ( c0 >= 'A' ) && ( c0 <= 'Z' ) ) + c0 += 'a' - 'A'; + if( ( c1 >= 'A' ) && ( c1 <= 'Z' ) ) + c1 += 'a' - 'A'; + if( c0 != c1 ) + break; + } + return 0; +} + + +char *ccStrMatchSeq( char *str, char *seq, int seqlength ) +{ + int i; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( i >= seqlength ) + { + if( str[i] ) + break; + return str + i; + } + if( str[i] != seq[i] ) + break; + } + return 0; +} + + +char *ccSeqCmpSeq( char *s1, char *s2, int s1length, int s2length ) +{ + int i; + if( s1length != s2length ) + return 0; + for( i = 0 ; i < s1length ; i++ ) + { + if( s1[i] != s2[i] ) + break; + } + return s1 + i; +} + + +int ccSeqCmpSeqStdTest( char *s1, char *s2, int s1length, int s2length ) +{ + int i; + if( !( s1length ) ) + return ( s2length ? -1 : 0 ); + else if( !( s2length ) ) + return 1; + for( i = 0 ; ; i++ ) + { + if( i >= s1length ) + { + if( i >= s2length ) + break; + return -1; + } + else if( i >= s2length ) + return 1; + if( s1[i] != s2[i] ) + return( s1[i] > s2[i] ? 1 : -1 ); + } + return 0; +} + + +int ccStrWordCmpWord( char *s1, char *s2 ) +{ + int i; + for( i = 0 ; ; i++ ) + { + if( s1[i] != s2[i] ) + break; + if( CC_CHAR_IS_DELIMITER( s1[i] ) ) + { + if( CC_CHAR_IS_DELIMITER( s2[i] ) ) + return 1; + else + return 0; + } + } + if( ( CC_CHAR_IS_DELIMITER( s1[i] ) ) && ( CC_CHAR_IS_DELIMITER( s2[i] ) ) ) + return 1; + return 0; +} + + +char *ccStrLowCmpWord( char *str, char *word ) +{ + int i; + char c1, c2; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( !( word[i] ) ) + { + if( CC_CHAR_IS_DELIMITER( str[i] ) ) + return str + i; + else + return 0; + } + c1 = str[i]; + if( ( c1 >= 'A' ) && ( c1 <= 'Z' ) ) + c1 += 'a' - 'A'; + c2 = word[i]; + if( ( c2 >= 'A' ) && ( c2 <= 'Z' ) ) + c2 += 'a' - 'A'; + if( c1 != c2 ) + break; + } + return 0; +} + + +char *ccStrLowCmpSeq( char *str, char *seq, int seqlength ) +{ + int i; + char c1, c2; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( i >= seqlength ) + { + if( CC_CHAR_IS_DELIMITER( str[i] ) ) + return str + i; + else + return 0; + } + c1 = str[i]; + if( ( c1 >= 'A' ) && ( c1 <= 'Z' ) ) + c1 += 'a' - 'A'; + c2 = seq[i]; + if( ( c2 >= 'A' ) && ( c2 <= 'Z' ) ) + c2 += 'a' - 'A'; + if( c1 != c2 ) + break; + } + return 0; +} + + +char *ccStrFindStr( char *str0, char *str1 ) +{ + int i; + if( !( str0 ) ) + return 0; + for( ; *str0 ; str0++ ) + { + for( i = 0 ; ; i++ ) + { + if( !( str1[i] ) ) + return str0; + if( str0[i] != str1[i] ) + break; + } + } + return 0; +} + + +char *ccStrFindStrSkip( char *str0, char *str1 ) +{ + int i; + if( !( str0 ) ) + return 0; + for( ; *str0 ; str0++ ) + { + for( i = 0 ; ; i++ ) + { + if( !( str1[i] ) ) + return str0 + i; + if( str0[i] != str1[i] ) + break; + } + } + return 0; +} + + +char *ccStrFindSeq( char *str, char *seq, int seqlength ) +{ + int i; + if( !( str ) ) + return 0; + for( ; *str ; str++ ) + { + for( i = 0 ; ; i++ ) + { + if( i >= seqlength ) + return str; + if( str[i] != seq[i] ) + break; + } + } + return 0; +} + + +char *ccStrFindWord( char *str, char *word, int wordlength ) +{ + int i, wordflag; + wordflag = 0; + if( !( str ) ) + return 0; + for( ; *str ; str++ ) + { + if( CC_CHAR_IS_DELIMITER( *str ) ) + { + wordflag = 0; + continue; + } + else if( wordflag ) + continue; + for( i = 0 ; ; i++ ) + { + if( i >= wordlength ) + { + if( CC_CHAR_IS_DELIMITER( str[i] ) ) + return str; + return 0; + } + if( str[i] != word[i] ) + { + wordflag = 1; + str += i; + break; + } + } + } + return 0; +} + + +int ccStrWordLength( char *str ) +{ + int i; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( CC_CHAR_IS_DELIMITER( str[i] ) ) + break; + } + return i; +} + + +int ccStrFindChar( char *str, char c ) +{ + int i; + if( !( str ) ) + return -1; + for( i = 0 ; str[i] ; i++ ) + { + if( str[i] == c ) + return i; + } + return -1; +} + + +int ccSeqFindChar( char *seq, int seqlen, char c ) +{ + int i; + for( i = 0 ; i < seqlen ; i++ ) + { + if( seq[i] == c ) + return i; + } + return -1; +} + + +int ccStrFindCharLast( char *str, char c ) +{ + int i, last; + if( !( str ) ) + return -1; + last = -1; + for( i = 0 ; str[i] ; i++ ) + { + if( str[i] == c ) + last = i; + } + return last; +} + + +int ccSeqFindCharLast( char *seq, int seqlen, char c ) +{ + int i, last; + last = -1; + for( i = 0 ; i < seqlen ; i++ ) + { + if( seq[i] == c ) + last = i; + } + return last; +} + + +char *ccSeqFindStr( char *seq, int seqlen, char *str ) +{ + int i; + for( ; seqlen ; seqlen--, seq++ ) + { + for( i = 0 ; i < seqlen ; i++ ) + { + if( !str[i] ) + return seq; + if( seq[i] != str[i] ) + break; + } + } + return 0; +} + + +char *ccSeqFindStrSkip( char *seq, int seqlen, char *str ) +{ + int i; + for( ; seqlen ; seqlen--, seq++ ) + { + for( i = 0 ; i < seqlen ; i++ ) + { + if( !str[i] ) + return seq + i; + if( seq[i] != str[i] ) + break; + } + } + return 0; +} + + +char *ccSeqFindStrIgnoreCaseSkip( char *seq, int seqlen, char *str ) +{ + int i; + char c0, c1; + for( ; seqlen ; seqlen--, seq++ ) + { + for( i = 0 ; i < seqlen ; i++ ) + { + if( !str[i] ) + return seq + i; + c0 = seq[i]; + c1 = str[i]; + if( ( c0 >= 'A' ) && ( c0 <= 'Z' ) ) + c0 += 'a' - 'A'; + if( ( c1 >= 'A' ) && ( c1 <= 'Z' ) ) + c1 += 'a' - 'A'; + if( c0 != c1 ) + break; + } + } + return 0; +} + + +char *ccStrParam( char *str, int *retparamlen, int *retskiplen ) +{ + int i; + if( !( str ) ) + return 0; + if( str[0] == '"' ) + { + for( i = 1 ; ; i++ ) + { + if( str[i] == '"' ) + break; + if( CC_CHAR_IS_CONTROL( str[i] ) ) + return 0; + } + if( !( CC_CHAR_IS_DELIMITER( str[i+1] ) ) ) + return 0; + *retparamlen = i-1; + *retskiplen = i+1; + return str+1; + } + if( CC_CHAR_IS_DELIMITER( str[0] ) ) + return 0; + for( i = 1 ; ; i++ ) + { + if( CC_CHAR_IS_DELIMITER( str[i] ) ) + break; + } + *retparamlen = i; + *retskiplen = i; + return str; +} + + +int ccParseParameters( char *str, char **argv, int argcountmax ) +{ + int argc, paramlen, skiplen; + char *param; + + if( !( str ) ) + return 0; + for( argc = 0 ; argc < argcountmax ; ) + { + if( !( str = ccStrNextWordSameLine( str ) ) ) + break; + param = ccStrParam( str, ¶mlen, &skiplen ); + if( argv ) + argv[argc] = param; + argc++; + if( !( param ) ) + break; + if( !( param[paramlen] ) ) + break; + str += skiplen + 1; + } + + return argc; +} + + +int ccParseParametersCut( char *str, char **argv, int argcountmax ) +{ + int argc, paramlen, skiplen; + char *param; + + if( !( str ) ) + return 0; + for( argc = 0 ; argc < argcountmax ; ) + { + if( !( str = ccStrNextWordSameLine( str ) ) ) + break; + param = ccStrParam( str, ¶mlen, &skiplen ); + if( argv ) + argv[argc] = param; + argc++; + if( !( param ) ) + break; + if( !( param[paramlen] ) ) + break; + param[paramlen] = 0; + str += skiplen + 1; + } + + return argc; +} + + +char *ccStrNextWord( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( *str == 0 ) + return 0; + if( !( CC_CHAR_IS_DELIMITER( *str ) ) ) + break; + } + return str; +} + + +char *ccStrSkipWord( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( !( *str ) ) + return 0; + if( ( *str == ' ' ) || ( *str == '\t' ) || ( *str == '\n' ) || ( *str == '\r' ) ) + break; + } + return str; +} + + +char *ccStrEndWord( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( !( *str ) || ( *str == ' ' ) || ( *str == '\t' ) || ( *str == '\n' ) || ( *str == '\r' ) ) + break; + } + return str; +} + + +char *ccStrNextWordSameLine( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( *str == 0 ) + return 0; + if( *str == '\n' ) + return 0; + if( !( CC_CHAR_IS_DELIMITER( *str ) ) ) + break; + } + return str; +} + + +char *ccStrNextParam( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( ( *str == 0 ) || ( *str != '\n' ) ) + return 0; + if( !( CC_CHAR_IS_DELIMITER( *str ) ) ) + break; + } + return str; +} + + +char *ccStrNextLine( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( *str == 0 ) + return str; + if( *str == '\n' ) + break; + } + return str + 1; +} + + +char *ccStrPassLine( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( ( *str == 0 ) || ( *str == '\n' ) ) + break; + if( !( CC_CHAR_IS_DELIMITER( *str ) ) ) + return 0; + } + return str; +} + +char *ccStrAllocPrintf( char *format, ... ) +{ + char *str; + int strsize, allocsize; + va_list ap; + + allocsize = 512; + str = malloc( allocsize ); + for( ; ; ) + { + va_start( ap, format ); + strsize = vsnprintf( str, allocsize, format, ap ); +#if CC_WINDOWS + if( strsize == -1 ) + strsize = allocsize << 1; +#endif + va_end( ap ); + if( strsize < allocsize ) + break; + allocsize = strsize + 2; + str = realloc( str, allocsize ); + } + + return str; +} + + +char *ccStrDup( const char *str ) +{ + int len; + char *newstr; + if( !( str ) ) + return 0; + len = strlen( str ); + if( !( len ) ) + return 0; + newstr = malloc( ( len + 1 ) * sizeof(char) ); + memcpy( newstr, str, len + 1 ); + return newstr; +} + + +int ccUnicodeToUtf8( char *s, uint32_t unicode ) +{ + int retval; + retval = 0; + if( unicode < 0x80 ) + { + *s++ = unicode; + retval = 1; + } + else if( unicode < 0x800 ) + { + *s++ = 192 + ( unicode / 64 ); + *s++ = 128 + ( unicode % 64 ); + retval = 2; + } + else if( ( unicode - 0xd800u ) < 0x800 ) + retval = 0; + else if( unicode < 0x10000 ) + { + *s++ = 224 + ( unicode / 4096 ); + *s++ = 128 + ( ( unicode / 64 ) % 64 ); + *s++ = 128 + ( unicode % 64 ); + retval = 3; + } + else if( unicode < 0x110000 ) + { + *s++ = 240 + ( unicode / 262144 ); + *s++ = 128 + ( ( unicode / 4096 ) % 64 ); + *s++ = 128 + ( ( unicode / 64 ) % 64 ); + *s++ = 128 + ( unicode % 64 ); + retval = 4; + } + return retval; +} + + +static const char ccStrPrintDecimalTable[201] = +{ + "00010203040506070809" + "10111213141516171819" + "20212223242526272829" + "30313233343536373839" + "40414243444546474849" + "50515253545556575859" + "60616263646566676869" + "70717273747576777879" + "80818283848586878889" + "90919293949596979899" +}; + +static inline CC_ALWAYSINLINE int ccStrPrintLength32( uint32_t n ) +{ + int size; + if( n >= 10000 ) + { + if( n >= 10000000 ) + { + if( n >= 1000000000 ) + size = 10; + else if( n >= 100000000 ) + size = 9; + else + size = 8; + } + else if( n >= 1000000 ) + size = 7; + else if( n >= 100000 ) + size = 6; + else + size = 5; + } + else + { + if( n >= 100 ) + { + if( n >= 1000 ) + size = 4; + else + size = 3; + } + else if( n >= 10 ) + size = 2; + else + size = 1; + } + return size; +} + +static inline CC_ALWAYSINLINE int ccStrPrintLength64( uint64_t n ) +{ + int size; + if( n >= 10000 ) + { + if( n >= 10000000 ) + { + if( n >= 10000000000LL ) + { + if( n >= 10000000000000LL ) + { + if( n >= 10000000000000000LL ) + { + if( n >= 10000000000000000000ULL ) + size = 20; + else if( n >= 1000000000000000000LL ) + size = 19; + else if( n >= 100000000000000000LL ) + size = 18; + else + size = 17; + } + else if( n >= 1000000000000000LL ) + size = 16; + else if( n >= 100000000000000LL ) + size = 15; + else + size = 14; + } + else if( n >= 1000000000000LL ) + size = 13; + else if( n >= 100000000000LL ) + size = 12; + else + size = 11; + } + else if( n >= 1000000000 ) + size = 10; + else if( n >= 100000000 ) + size = 9; + else + size = 8; + } + else + { + if( n >= 1000000 ) + size = 7; + else if( n >= 100000 ) + size = 6; + else + size = 5; + } + } + else if( n >= 100 ) + { + if( n >= 1000 ) + size = 4; + else + size = 3; + } + else if( n >= 10 ) + size = 2; + else + size = 1; + return size; +} + +int ccStrPrintInt32( char *str, int32_t n ) +{ + int sign, size, retsize, pos; + uint32_t val32; + const char *src; + + if( n == 0 ) + { + str[0] = '0'; + str[1] = 0; + return 1; + } + + sign = -( n < 0 ); + val32 = ( n ^ sign ) - sign; + size = ccStrPrintLength32( val32 ); + + if( sign ) + { + size++; + str[0] = '-'; + } + retsize = size; + str[size] = 0; + str += size - 1; + + while( val32 >= 100 ) + { + pos = val32 % 100; + val32 /= 100; + src = &ccStrPrintDecimalTable[ pos << 1 ]; + str[-1] = src[0]; + str[0] = src[1]; + str -= 2; + } + while( val32 > 0 ) + { + *str-- = '0' + ( val32 % 10 ); + val32 /= 10; + } + + return retsize; +} + +int ccStrPrintUint32( char *str, uint32_t n ) +{ + int size, retsize, pos; + uint32_t val32; + const char *src; + + if( n == 0 ) + { + str[0] = '0'; + str[1] = 0; + return 1; + } + + val32 = n; + size = ccStrPrintLength32( val32 ); + retsize = size; + str[size] = 0; + str += size - 1; + + while( val32 >= 100 ) + { + pos = val32 % 100; + val32 /= 100; + src = &ccStrPrintDecimalTable[ pos << 1 ]; + str[-1] = src[0]; + str[0] = src[1]; + str -= 2; + } + while( val32 > 0 ) + { + *str-- = '0' + ( val32 % 10 ); + val32 /= 10; + } + + return retsize; +} + +int ccStrPrintInt64( char *str, int64_t n ) +{ + int sign, size, retsize, pos; + uint64_t val64; + const char *src; + + if( n == 0 ) + { + str[0] = '0'; + str[1] = 0; + return 1; + } + + sign = -( n < 0 ); + val64 = ( n ^ sign ) - sign; + size = ccStrPrintLength64( val64 ); + + if( sign ) + { + size++; + str[0] = '-'; + } + retsize = size; + str[size] = 0; + str += size - 1; + + while( val64 >= 100 ) + { + pos = val64 % 100; + val64 /= 100; + src = &ccStrPrintDecimalTable[ pos << 1 ]; + str[-1] = src[0]; + str[0] = src[1]; + str -= 2; + } + while( val64 > 0 ) + { + *str-- = '0' + ( val64 % 10 ); + val64 /= 10; + } + + return retsize; +} + +int ccStrPrintUint64( char *str, uint64_t n ) +{ + int size, retsize, pos; + uint64_t val64; + const char *src; + + if( n == 0 ) + { + str[0] = '0'; + str[1] = 0; + return 1; + } + + val64 = n; + size = ccStrPrintLength64( val64 ); + + retsize = size; + str[size] = 0; + str += size - 1; + + while( val64 >= 100 ) + { + pos = val64 % 100; + val64 /= 100; + src = &ccStrPrintDecimalTable[ pos << 1 ]; + str[-1] = src[0]; + str[0] = src[1]; + str -= 2; + } + while( val64 > 0 ) + { + *str-- = '0' + ( val64 % 10 ); + val64 /= 10; + } + + return retsize; +} + + +//// + + +#define CC_STR_PRINT_DOUBLE_MAX_DECIMAL (17) + +static const double ccStrPrintBiasTable[CC_STR_PRINT_DOUBLE_MAX_DECIMAL] = +{ 0.5, 0.05, 0.005, 0.0005, 0.00005, 0.000005, 0.0000005, 0.00000005, 0.000000005, 0.0000000005, 0.00000000005, 0.000000000005, 0.0000000000005, 0.00000000000005, 0.000000000000005, 0.0000000000000005, 0.00000000000000005 }; + +int ccStrPrintDouble( char *str, int bufsize, int decimals, double value ) +{ + int size, offset, index; + int32_t frac, accumsub; + double muldec; + uint32_t u32; + uint64_t u64; + + size = 0; + if( value < 0.0 ) + { + size = 1; + *str++ = '-'; + bufsize--; + value = -value; + } + + /* Add bias matching the count of desired decimals in order to round the right way */ + if( decimals >= CC_STR_PRINT_DOUBLE_MAX_DECIMAL ) + decimals = CC_STR_PRINT_DOUBLE_MAX_DECIMAL; + value += ccStrPrintBiasTable[decimals]; + + if( value < 4294967296.0 ) + { + if( bufsize < CC_STR_PRINT_BUFSIZE_UINT32 ) + goto error; + u32 = (int32_t)value; + offset = ccStrPrintUint32( str, u32 ); + size += offset; + bufsize -= size; + value -= (double)u32; + } + else if( value < 18446744073709551616.0 ) + { + if( bufsize < CC_STR_PRINT_BUFSIZE_UINT64 ) + goto error; + u64 = (int64_t)value; + offset = ccStrPrintUint64( str, u64 ); + size += offset; + bufsize -= size; + value -= (double)u64; + } + else + goto error; + + decimals = CC_MIN( decimals, bufsize - 2 ); + if( !( bufsize ) || !( decimals ) ) + return size; + + str[offset] = '.'; + muldec = 10.0; + accumsub = 0; + str += offset + 1; + size++; + + for( index = 0 ; index < decimals ; index++ ) + { + frac = (int32_t)( value * muldec ) - accumsub; + str[index] = '0' + (char)frac; + accumsub += frac; + accumsub = ( accumsub << 3 ) + ( accumsub << 1 ); + if( muldec < 10000000 ) + muldec *= 10.0; + else + { + value *= 10000000.0; + value -= (int32_t)value; + muldec = 10.0; + accumsub = 0; + } + } + str[ decimals ] = 0; + size += decimals; + + return size; + + error: + if( bufsize < 4 ) + *str = 0; + else + { + str[0] = 'E'; + str[1] = 'R'; + str[2] = 'R'; + str[3] = 0; + } + return 0; +} + + +//// + + +int ccStrParseInt32( char *str, int32_t *retint ) +{ + int retval; + int64_t i; + retval = ccStrParseInt64( str, &i ); + if( ( i >= ((((int64_t)1)<<31)-1) ) || ( i < -(((int64_t)1)<<31) ) ) + return 0; + *retint = (int32_t)i; + return retval; +} + + +int ccSeqParseInt32( char *seq, int seqlength, int32_t *retint ) +{ + int retval; + int64_t i; + retval = ccSeqParseInt64( seq, seqlength, &i ); + if( ( i >= ((((int64_t)1)<<31)-1) ) || ( i < -(((int64_t)1)<<31) ) ) + return 0; + *retint = (int32_t)i; + return retval; +} + + +int ccStrParseInt64( char *str, int64_t *retint ) +{ + int negflag; + char c; + int64_t workint; + + *retint = 0; + if( !( str ) ) + return 0; + negflag = 0; + if( *str == '-' ) + negflag = 1; + str += negflag; + + workint = 0; + for( ; ; str++ ) + { + c = *str; + if( ( c >= '0' ) && ( c <= '9' ) ) + { + if( workint >= (int64_t)0xcccccccccccccccLL ) + return 0; + workint = ( workint * 10 ) + ( c - '0' ); + } + else if( CC_CHAR_IS_DELIMITER( c ) ) + break; + else + return 0; + } + + if( negflag ) + workint = -workint; + *retint = workint; + return 1; +} + + +int ccSeqParseInt64( char *seq, int seqlength, int64_t *retint ) +{ + int i, negflag; + char c; + int64_t workint; + + *retint = 0; + if( !( seqlength ) ) + return 0; + negflag = 0; + i = 0; + if( *seq == '-' ) + { + negflag = 1; + i = 1; + } + + workint = 0; + for( ; i < seqlength ; i++ ) + { + c = seq[i]; + if( ( c >= '0' ) && ( c <= '9' ) ) + { + if( workint >= (int64_t)0xcccccccccccccccLL ) + return 0; + workint = ( workint * 10 ) + ( c - '0' ); + } + else if( CC_CHAR_IS_DELIMITER( c ) ) + break; + else + return 0; + } + + if( negflag ) + workint = -workint; + *retint = workint; + return 1; +} + + +//// + + +int ccStrParseFloat( char *str, float *retfloat ) +{ + int retval; + double d; + retval = ccStrParseDouble( str, &d ); + *retfloat = (float)d; + return retval; +} + + +int ccSeqParseFloat( char *seq, int seqlength, float *retfloat ) +{ + int retval; + double d; + retval = ccSeqParseDouble( seq, seqlength, &d ); + *retfloat = (float)d; + return retval; +} + + +int ccStrParseDouble( char *str, double *retdouble ) +{ + int negflag; + char c; + double accum; + double decfactor; + + *retdouble = 0.0; + if( !( str ) ) + return 0; + negflag = ( *str == '-' ); + str += negflag; + + accum = 0.0; + for( ; ; str++ ) + { + c = *str; + if( ( c >= '0' ) && ( c <= '9' ) ) + accum = ( accum * 10.0 ) + (double)( c - '0' ); + else if( CC_CHAR_IS_DELIMITER( c ) ) + goto done; + else if( c == '.' ) + break; + else + return 0; + } + + str++; + decfactor = 0.1; + for( ; ; str++ ) + { + c = *str; + if( ( c >= '0' ) && ( c <= '9' ) ) + { + accum += (double)( c - '0' ) * decfactor; + decfactor *= 0.1; + } + else if( CC_CHAR_IS_DELIMITER( c ) ) + break; + else + return 0; + } + + done: + + if( negflag ) + accum = -accum; + *retdouble = (double)accum; + return 1; +} + + +int ccSeqParseDouble( char *seq, int seqlength, double *retdouble ) +{ + int i, negflag; + char c; + double accum; + double decfactor; + + *retdouble = 0.0; + i = 0; + if( !( seq ) ) + return 0; + negflag = ( seq[i] == '-' ); + i += negflag; + + accum = 0.0; + for( ; i < seqlength ; i++ ) + { + c = seq[i]; + if( ( c >= '0' ) && ( c <= '9' ) ) + accum = ( accum * 10.0 ) + (double)( c - '0' ); + else if( CC_CHAR_IS_DELIMITER( c ) ) + goto done; + else if( c == '.' ) + break; + else + return 0; + } + + i++; + decfactor = 0.1; + for( ; i < seqlength ; i++ ) + { + c = seq[i]; + if( ( c >= '0' ) && ( c <= '9' ) ) + { + accum += (double)( c - '0' ) * decfactor; + decfactor *= 0.1; + } + else if( CC_CHAR_IS_DELIMITER( c ) ) + break; + else + return 0; + } + + done: + + if( negflag ) + accum = -accum; + *retdouble = (double)accum; + return 1; +} + + +int ccStrParseHex( char *str, int hexchars ) +{ + int index, value, charvalue; + value = 0; + for( index = 0 ; index < hexchars ; index++ ) + { + value <<= 4; + charvalue = ccCharHexBase( str[index] ); + if( charvalue == -1 ) + return -1; + value += charvalue; + } + return value; +} + + +//// + diff --git a/ecere/src/gfx/newFonts/cc/ccstr.h b/ecere/src/gfx/newFonts/cc/ccstr.h new file mode 100644 index 0000000000..63f1de466c --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/ccstr.h @@ -0,0 +1,161 @@ +/* ----------------------------------------------------------------------------- + * + * Copyright (c) 2011 Alexis Naveros. + * Ecere Corporation has unlimited/unrestricted rights. + * ----------------------------------------------------------------------------- + */ + + +#ifndef CCSTR_H +#define CCSTR_H + +#include "cc.h" + +//// + + +static inline CC_ALWAYSINLINE int ccStrCmpEqualInline( char *s0, char *s1 ) +{ + int i; + for( i = 0 ; ; i++ ) + { + if( s0[i] != s1[i] ) + return 0; + if( !( s0[i] ) ) + break; + } + return 1; +} + +static inline CC_ALWAYSINLINE int ccIsAlphaNum( char c ) +{ + if( ( c >= 'a' ) && ( c <= 'z' ) ) + return 1; + if( ( c >= 'A' ) && ( c <= 'Z' ) ) + return 1; + if( ( c >= '0' ) && ( c <= '9' ) ) + return 1; + return 0; +} + +static inline CC_ALWAYSINLINE int ccIsAlphaNumExtended( unsigned char c ) +{ + if( ( c >= 'a' ) && ( c <= 'z' ) ) + return 1; + if( ( c >= 'A' ) && ( c <= 'Z' ) ) + return 1; + if( ( c >= '0' ) && ( c <= '9' ) ) + return 1; + if( c >= 128 ) + return 1; + return 0; +} + +static inline CC_ALWAYSINLINE int ccCharHexBase( char c ) +{ + int hex; + if( ( c >= '0' ) && ( c <= '9' ) ) + hex = c - '0'; + else if( ( c >= 'A' ) && ( c <= 'F' ) ) + hex = c - ('A'-10); + else if( ( c >= 'a' ) && ( c <= 'f' ) ) + hex = c - ('a'-10); + else + hex = -1; + return hex; +} + +static inline CC_ALWAYSINLINE size_t ccStrlen( char *str ) +{ + return ( str ? strlen( str ) : 0 ); +} + +void ccStrLowCase( char *str, int length ); +void ccStrLowCopy( char *dst, char *src, int length ); +int ccStrCmpEqual( char *s0, char *s1 ); +int ccStrCmpEqualTest( char *s0, char *s1 ); +int ccStrCmpStdTest( char *s0, char *s1 ); +int ccStrCpyStr( char *buffer, int buffersize, char *str ); +char *ccStrCmpWord( char *str, char *word ); +char *ccStrCmpWordIgnoreCase( char *str, char *word ); +char *ccStrCmpSeq( char *str, char *seq, int seqlength ); +char *ccStrCmpSeqIgnoreCase( char *str, char *seq, int seqlength ); +char *ccStrMatchSeq( char *str, char *seq, int seqlength ); +char *ccSeqCmpSeq( char *s1, char *s2, int s1length, int s2length ); +int ccSeqCmpSeqStdTest( char *s1, char *s2, int s1length, int s2length ); +int ccStrWordCmpWord( char *s1, char *s2 ); +char *ccStrLowCmpWord( char *str, char *word ); +char *ccStrLowCmpSeq( char *str, char *seq, int seqlength ); +char *ccStrFindStr( char *str0, char *str1 ); +char *ccStrFindStrSkip( char *str0, char *str1 ); +char *ccStrFindSeq( char *str, char *seq, int seqlength ); +char *ccStrFindWord( char *str, char *word, int wordlength ); +int ccStrWordLength( char *str ); +int ccStrFindChar( char *str, char c ); +int ccSeqFindChar( char *seq, int seqlen, char c ); +int ccStrFindCharLast( char *str, char c ); +int ccSeqFindCharLast( char *seq, int seqlen, char c ); +char *ccSeqFindStr( char *seq, int seqlen, char *str ); +char *ccSeqFindStrSkip( char *seq, int seqlen, char *str ); +char *ccSeqFindStrIgnoreCaseSkip( char *seq, int seqlen, char *str ); +char *ccStrParam( char *str, int *retparamlen, int *retskiplen ); +int ccParseParameters( char *str, char **argv, int argcountmax ); +int ccParseParametersCut( char *str, char **argv, int argcountmax ); +char *ccStrNextWord( char *str ); +char *ccStrSkipWord( char *str ); +char *ccStrEndWord( char *str ); +char *ccStrNextWordSameLine( char *str ); +char *ccStrNextParam( char *str ); +char *ccStrNextLine( char *str ); +char *ccStrPassLine( char *str ); + + +char *ccStrAllocPrintf( char *format, ... ); +char *ccStrDup( const char *str ); +int ccUnicodeToUtf8( char *s, uint32_t unicode ); +/* Returns 1 when data is insufficient, send more bytes ; state must be initialized to zero */ +uint32_t ccUtf8ToUnicode( uint32_t byte, uint32_t *state, uint32_t *retunicode ); + + +//// + + +/* 5 times faster than snprintf() and such */ +int ccStrPrintInt32( char *str, int32_t n ); +int ccStrPrintUint32( char *str, uint32_t n ); +int ccStrPrintInt64( char *str, int64_t n ); +int ccStrPrintUint64( char *str, uint64_t n ); + +/* Buffer size required by ccStrPrint* integer functions */ +#define CC_STR_PRINT_BUFSIZE_INT32 (12) +#define CC_STR_PRINT_BUFSIZE_UINT32 (11) +#define CC_STR_PRINT_BUFSIZE_INT64 (21) +#define CC_STR_PRINT_BUFSIZE_UINT64 (20) + +/* 20 times faster than snprintf() and such ~ integer range under uint64_t, up to 16 decimals, rounding of decimal at end of mantissa range can be off */ +int ccStrPrintDouble( char *str, int bufsize, int decimals, double value ); + + +//// + + +/* Much faster than sscanf() and such */ +int ccStrParseInt32( char *str, int32_t *retint ); +int ccSeqParseInt32( char *seq, int seqlength, int32_t *retint ); +int ccStrParseInt64( char *str, int64_t *retint ); +int ccSeqParseInt64( char *seq, int seqlength, int64_t *retint ); + +/* Fast and generally accurate except for the last 1-2 bits of double mantissa ~ 3x-10x faster than sscanf() and such */ +int ccStrParseFloat( char *str, float *retfloat ); +int ccSeqParseFloat( char *seq, int seqlength, float *retfloat ); +int ccStrParseDouble( char *str, double *retdouble ); +int ccSeqParseDouble( char *seq, int seqlength, double *retdouble ); + +int ccStrParseHex( char *str, int hexchars ); + + +//// + + +#endif + diff --git a/ecere/src/gfx/newFonts/cc/cpuconfig.h b/ecere/src/gfx/newFonts/cc/cpuconfig.h new file mode 100644 index 0000000000..9062462b84 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/cpuconfig.h @@ -0,0 +1,168 @@ +#ifndef __CPUCONFIG_H__ +#define __CPUCONFIG_H__ + +#if defined(__LUMIN__) +#define CPU_DISABLE_SSE 1 +#endif + +#if CPU_DISABLE_SSE && !defined(CPU_ENABLE_SSE) +#define CPU_ENABLE_SSE 0 +#endif + +/* Automatically generated CPU information header */ + +#define CPUCONF_CHAR_SIZE (1) +#define CPUCONF_SHORT_SIZE (2) +#define CPUCONF_INT_SIZE (4) +#define CPUCONF_LONG_LONG_SIZE (8) +#define CPUCONF_FLOAT_SIZE (4) +#define CPUCONF_DOUBLE_SIZE (8) +#define CPUCONF_LONG_DOUBLE_SIZE (16) + +#define CPUCONF_CHAR_BITS (8) +#define CPUCONF_SHORT_BITS (16) +#define CPUCONF_INT_BITS (32) +#define CPUCONF_LONG_LONG_BITS (64) +#define CPUCONF_FLOAT_BITS (32) +#define CPUCONF_DOUBLE_BITS (64) +#define CPUCONF_LONG_DOUBLE_BITS (128) + +#define CPUCONF_CHAR_SIZESHIFT (0) +#define CPUCONF_SHORT_SIZESHIFT (1) +#define CPUCONF_INT_SIZESHIFT (2) +#define CPUCONF_LONG_LONG_SIZESHIFT (3) + +#define CPUCONF_FLOAT_SIZESHIFT (2) +#define CPUCONF_DOUBLE_SIZESHIFT (3) +#define CPUCONF_LONG_DOUBLE_SIZESHIFT (4) + +#define CPUCONF_CHAR_BITSHIFT (3) +#define CPUCONF_SHORT_BITSHIFT (4) +#define CPUCONF_INT_BITSHIFT (5) +#define CPUCONF_LONG_LONG_BITSHIFT (6) +#define CPUCONF_FLOAT_BITSHIFT (5) +#define CPUCONF_DOUBLE_BITSHIFT (6) +#define CPUCONF_LONG_DOUBLE_BITSHIFT (7) + +#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \ + defined(__BIG_ENDIAN__) || \ + defined(__ARMEB__) || \ + defined(__THUMBEB__) || \ + defined(__AARCH64EB__) || \ + defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) +#define CPUCONF_BIG_ENDIAN +#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \ + defined(__LITTLE_ENDIAN__) || \ + defined(__ARMEL__) || \ + defined(__THUMBEL__) || \ + defined(__AARCH64EL__) || \ + defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) +#define CPUCONF_LITTLE_ENDIAN +#else + #define CPUCONF_LITTLE_ENDIAN + //#error "Unknown endianness" +#endif + +#if (defined(__WORDSIZE) && __WORDSIZE == 8) || defined(__x86_64__) || defined(_M_X64) || defined(_WIN64) || defined(__LP64__) || defined(__LLP64__) + +#if defined(__x86_64__) || defined(_M_X64) + #define CPUCONF_ARCH_AMD64 +#endif + + #define CPUCONF_INTPTR_SIZE (8) + #define CPUCONF_POINTER_SIZE (8) + #define CPUCONF_INTPTR_BITS (64) + #define CPUCONF_POINTER_BITS (64) + #define CPUCONF_INTPTR_SIZESHIFT (3) + #define CPUCONF_POINTER_SIZESHIFT (3) + #define CPUCONF_INTPTR_BITSHIFT (6) + #define CPUCONF_POINTER_BITSHIFT (6) + +#if defined(__WIN32__) + #define CPUCONF_LONG_SIZE (4) +#else + #define CPUCONF_LONG_SIZE (8) +#endif + #define CPUCONF_LONG_BITSHIFT ((sizeof(long) == 4) ? 5 : 6) + #define CPUCONF_LONG_SIZESHIFT ((sizeof(long) == 4) ? 2 : 3) + #define CPUCONF_LONG_BITS (sizeof(long) * 8) + + #define CPUCONF_WORD_SIZE (64) + +#else + +#if !defined(__EMSCRIPTEN__) && (defined(__i386) || defined(_M_IX86)) + #define CPUCONF_ARCH_IA32 +#endif + + #define CPUCONF_INTPTR_SIZE (4) + #define CPUCONF_POINTER_SIZE (4) + #define CPUCONF_INTPTR_BITS (32) + #define CPUCONF_POINTER_BITS (32) + #define CPUCONF_INTPTR_SIZESHIFT (2) + #define CPUCONF_POINTER_SIZESHIFT (2) + #define CPUCONF_INTPTR_BITSHIFT (5) + #define CPUCONF_POINTER_BITSHIFT (5) + + #define CPUCONF_LONG_SIZE (4) + #define CPUCONF_LONG_BITSHIFT (5) + #define CPUCONF_LONG_SIZESHIFT (2) + #define CPUCONF_LONG_BITS (32) + + #define CPUCONF_WORD_SIZE (32) + +#endif +#define CPUCONF_VENDOR_INTEL + +#define CPUCONF_IDENTIFIER "Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz" +//#define CPUCONF_CLASS_COREI7-AVX2 +#define CPUCONF_SOCKET_LOGICAL_CORES (16) +#define CPUCONF_SOCKET_PHYSICAL_CORES (8) +#define CPUCONF_TOTAL_CORE_COUNT (8) +#define CPUCONF_SYSTEM_MEMORY (17072009216LL) + +#define CPUCONF_CACHE_LINE_SIZE (64) +#define CPUCONF_CACHE_L1CODE_SIZE (32768) +#define CPUCONF_CACHE_L1CODE_LINE (64) +#define CPUCONF_CACHE_L1CODE_ASSOCIATIVITY (8) +#define CPUCONF_CACHE_L1CODE_SHARED (2) +#define CPUCONF_CACHE_L1DATA_SIZE (32768) +#define CPUCONF_CACHE_L1DATA_LINE (64) +#define CPUCONF_CACHE_L1DATA_ASSOCIATIVITY (8) +#define CPUCONF_CACHE_L1DATA_SHARED (2) +#define CPUCONF_CACHE_L1_UNIFIED_FLAG (0) +#define CPUCONF_CACHE_L2_SIZE (262144) +#define CPUCONF_CACHE_L2_LINE (64) +#define CPUCONF_CACHE_L2_ASSOCIATIVITY (8) +#define CPUCONF_CACHE_L2_SHARED (2) +#define CPUCONF_CACHE_L3_SIZE (8388608) +#define CPUCONF_CACHE_L3_LINE (64) +#define CPUCONF_CACHE_L3_ASSOCIATIVITY (16) +#define CPUCONF_CACHE_L3_SHARED (16) +#define CPUCONF_CAP_GPREGS (16) +#define CPUCONF_CAP_FPREGS (16) + +#define CPUCONF_CAP_CMOV +#define CPUCONF_CAP_CLFLUSH +#define CPUCONF_CAP_TSC +#define CPUCONF_CAP_MMX +#define CPUCONF_CAP_SSE +#define CPUCONF_CAP_SSE2 +#define CPUCONF_CAP_SSE3 +#define CPUCONF_CAP_SSSE3 +#define CPUCONF_CAP_SSE4_1 +#define CPUCONF_CAP_SSE4_2 +#define CPUCONF_CAP_AVX +#define CPUCONF_CAP_AVX2 +#define CPUCONF_CAP_AES +#define CPUCONF_CAP_PCLMUL +#define CPUCONF_CAP_CMPXCHG16B +#define CPUCONF_CAP_MOVBE +#define CPUCONF_CAP_RDTSCP +#define CPUCONF_CAP_CONSTANTTSC +#define CPUCONF_CAP_HYPERTHREADING +#define CPUCONF_CAP_MWAIT +#define CPUCONF_CAP_THERMALSENSOR +#define CPUCONF_CAP_CLOCKMODULATION + +#endif diff --git a/ecere/src/gfx/newFonts/cc/mm.c b/ecere/src/gfx/newFonts/cc/mm.c new file mode 100644 index 0000000000..c0df4ab164 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mm.c @@ -0,0 +1,3467 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2016 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +/** + * @file + * + * Global memory management routines. + * + * This file includes all the generic memory management routines used + * throughout the code : linked lists, balanced trees, block memory allocation, + * growing memory allocation, page-based pointer directories, aligned memory + * allocation, memory volume management, memory leak or buffer overrun + * tracking, etc. + */ + + +#if !defined(__WIN32__) +#define MM_THREADING (1) +#endif + +#ifdef MM_THREADING + #define _GNU_SOURCE + #include + #include +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "cpuconfig.h" +#include "cc.h" +#include "mm.h" + + +#if defined(MM_UNIX) + #include + #include + #include + #include + #include +/* + #include +*/ +#elif defined(MM_WINDOWS) + #include +#endif + +#if _POSIX_MAPPED_FILES > 0 + #include + #define MM_ZONE_SUPPORT 1 +#endif + +#if defined(MM_LINUX) + #include +#if !defined(__ANDROID__) + #include +#endif +#endif + +#if defined(MM_WIN32) + #include + #include +#endif + +#if MM_OSX + #ifndef MAP_ANONYMOUS + #define MAP_ANONYMOUS MAP_ANON + #endif +#endif + + +#ifndef ADDRESS +#define ADDRESS(p,o) ((void *)(((char *)p)+(o))) +#endif + +#ifndef ADDRESSDIFF +#define ADDRESSDIFF(a,b) (((char *)a)-((char *)b)) +#endif + + +#if MM_DEBUG + #define MM_PASSPARAMS , file, line +#else + #define MM_PASSPARAMS +#endif + + +//// + + +int mmInitStatus = 0; + +mmContext mmcontext; + +#ifdef MT_MUTEX_INITIALIZER +static mtMutex mmGlobalMutex = MT_MUTEX_INITIALIZER; +#else +static mtMutex mmGlobalMutex; +#endif + +#ifdef MM_NUMA + +int mmNumaInit() +{ + int nodeindex, cpuindex, cpurange; + int nodecpucount; + struct bitmask *cpus; + if( numa_available() == -1 ) + return 0; + mmcontext.numaflag = 1; + cpurange = numa_num_configured_cpus(); + if( cpurange >= MM_CPU_COUNT_MAXIMUM ) + { + fprintf( stderr, "CPU count %d exceeds %d, increase MM_CPU_COUNT_MAXIMUM in mm.h and try again.\n", cpurange, MM_CPU_COUNT_MAXIMUM ); + exit( 1 ); + } + mmcontext.nodecount = numa_num_configured_nodes(); + if( mmcontext.nodecount >= MM_NODE_COUNT_MAXIMUM ) + { + fprintf( stderr, "Node count %d exceeds %d, increase MM_NODE_COUNT_MAXIMUM in mm.h and try again.\n", mmcontext.nodecount, MM_NODE_COUNT_MAXIMUM ); + exit( 1 ); + } + mmcontext.cpucount = 0; + mmcontext.sysmemory = 0; + for( nodeindex = 0 ; nodeindex < mmcontext.nodecount ; nodeindex++ ) + { + mmcontext.nodesize[nodeindex] = numa_node_size64( nodeindex, 0 ); + mmcontext.sysmemory += mmcontext.nodesize[nodeindex]; + mmcontext.nodecpucount[nodeindex] = 0; + if( !( numa_bitmask_isbitset( numa_all_nodes_ptr, nodeindex ) ) ) + continue; + nodecpucount = 0; + cpus = numa_bitmask_alloc( cpurange ); + if( numa_node_to_cpus( nodeindex, cpus ) >= 0 ) + { + for( cpuindex = 0 ; cpuindex < cpurange ; cpuindex++ ) + { + if( numa_bitmask_isbitset( cpus, cpuindex ) ) + { + mmcontext.cpunode[ cpuindex ] = nodeindex; + nodecpucount++; + } + } + } + numa_bitmask_free( cpus ); + mmcontext.nodecpucount[nodeindex] = nodecpucount; + mmcontext.cpucount += nodecpucount; + } + return 1; +} + +#endif + +void numa_warn( int num, char *fmt, ... ) +{ + return; +} + +void mmInit() +{ + int64_t sysmemory; + if( mmInitStatus ) + return; + mmcontext.numaflag = 0; +#ifdef MM_NUMA + mmNumaInit(); +#endif + if( !( mmcontext.numaflag ) ) + { + mmcontext.nodecount = 1; + sysmemory = -1; + #if defined(MM_LINUX) && !defined(__ANDROID__) + mmcontext.cpucount = get_nprocs(); + mmcontext.pagesize = sysconf(_SC_PAGESIZE); + #elif defined(MM_UNIX) + mmcontext.cpucount = sysconf( _SC_NPROCESSORS_CONF ); + mmcontext.pagesize = sysconf(_SC_PAGESIZE); + #elif defined(MM_WIN32) + SYSTEM_INFO sysinfo; + GetSystemInfo( &sysinfo ); + mmcontext.cpucount = sysinfo.dwNumberOfProcessors; + mmcontext.pagesize = sysinfo.dwPageSize; + #else + mmcontext.cpucount = 1; + #endif + #if defined(MM_UNIX) && defined(_SC_PHYS_PAGES) + sysmemory = sysconf( _SC_PHYS_PAGES ); + if( sysmemory > 0 ) + sysmemory *= mmcontext.pagesize; + #endif + mmcontext.sysmemory = sysmemory; + mmcontext.nodesize[0] = mmcontext.sysmemory; + mmcontext.nodecpucount[0] = mmcontext.cpucount; + } + mtMutexInit( &mmGlobalMutex ); + mmInitStatus = 1; +/* + { + int nodeindex, cpuindex; + printf( "NUMA nodes : %d\n", mmcontext.nodecount ); + for( nodeindex = 0 ; nodeindex < mmcontext.nodecount ; nodeindex++ ) + printf( " NUMA node %d, size %lld, CPU count %d\n", nodeindex, (long long)mmcontext.nodesize[nodeindex], mmcontext.nodecpucount[nodeindex] ); + printf( "CPUs : %d\n", mmcontext.cpucount ); + for( cpuindex = 0 ; cpuindex < mmcontext.cpucount ; cpuindex++ ) + printf( " CPU %d on node %d\n", cpuindex, mmcontext.cpunode[ cpuindex ] ); + } +*/ + return; +} + + +void mmEnd() +{ + mtMutexDestroy( &mmGlobalMutex ); + return; +} + + +//// + + +void mmThreadBindToNode( int nodeindex ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + { + numa_run_on_node( nodeindex ); + return; + } +#endif +#if defined(MM_LINUX) + int cpuindex; + cpu_set_t cpuset; + CPU_ZERO( &cpuset ); + for( cpuindex = 0 ; cpuindex < mmcontext.cpucount ; cpuindex++ ) + CPU_SET( cpuindex, &cpuset ); + sched_setaffinity( 0, sizeof(cpu_set_t), &cpuset ); +#endif + return; +} + +void mmThreadBindToCpu( int cpuindex ) +{ +#if defined(MM_LINUX) + cpu_set_t cpuset; + CPU_ZERO( &cpuset ); + CPU_SET( cpuindex, &cpuset ); + sched_setaffinity( 0, sizeof(cpu_set_t), &cpuset ); +#elif defined(MM_WIN32) + HANDLE *handle; + handle = GetCurrentThread(); + SetThreadAffinityMask( handle, 1 << cpuindex ); +#endif + return; +} + +int mmCpuGetNode( int cpuindex ) +{ + return mmcontext.cpunode[ cpuindex ]; +} + + +//// + + +void *mmNodeAlloc( int nodeindex, size_t size ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + return numa_alloc_onnode( size, nodeindex ); +#endif + return malloc( size ); +} + +void mmNodeFree( int nodeindex, void *v, size_t size ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + { + numa_free( v, size ); + return; + } +#endif + free( v ); + return; +} + +void mmNodeMap( int nodeindex, void *start, size_t bytes ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + numa_tonode_memory( start, bytes, nodeindex ); +#endif + return; +} + +static void *mmNodeRelayAlloc( void *head, size_t bytes MM_PARAMS ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + return numa_alloc_onnode( bytes, (int)((intptr_t)head) ); +#endif + return malloc( bytes ); +} + +static void mmNodeRelayFree( void *head, void *v, size_t bytes MM_PARAMS ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + { + numa_free( v, bytes ); + return; + } +#endif + free( v ); + return; +} + + +void *mmNodeAlignAlloc( int nodeindex, size_t bytes, intptr_t align ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + return numa_alloc_onnode( bytes, nodeindex ); +#endif + return mmAlignAlloc( bytes, align ); +} + +void mmNodeAlignFree( int nodeindex, void *v, size_t bytes ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + { + numa_free( v, bytes ); + return; + } +#endif + mmAlignFree( v ); + return; +} + + + + +//// + + +#ifndef MM_INLINE_LIST_FUNCTIONS + +/** + * Add the item to a linked list. + * + * The head of the linked list should be defined as a void pointer. The list + * parameter can be a pointer to it, or a pointer to the mmListNode.next + * variable of the preceeding item. The offset parameter should be the offset + * of the mmListNode structure within the structure of the item. It can be + * easily obtained with the offsetof(,) macro. + */ +void mmListAdd( void **list, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + node->prev = list; + node->next = *list; + if( *list ) + { + next = ADDRESS( *list, offset ); + next->prev = &(node->next); + } + *list = item; + return; +} + +/** + * Remove the item from a linked list. + * + * The offset parameter should be the offset of the mmListNode structure + * within the structure of the item. It can be easily obtained with the + * offsetof(,) macro. + */ +void mmListRemove( void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + *(node->prev) = (void *)node->next; + if( node->next ) + { + next = ADDRESS( node->next, offset ); + next->prev = node->prev; + } + return; +} + + + +void mmListMergeList( void **listdst, void **listsrc, intptr_t offset ) +{ + void *item; + mmListNode *node; + if( !( *listsrc ) ) + return; + for( item = *listdst ; item ; item = node->next ) + { + node = ADDRESS( item, offset ); + listdst = &node->next; + } + item = *listsrc; + node = ADDRESS( item, offset ); + node->prev = listdst; + *listdst = item; + *listsrc = 0; + return; +} + + +void mmListMoveList( void **listdst, void **listsrc, intptr_t offset ) +{ + void *item; + mmListNode *node; + if( !( *listsrc ) ) + { + *listdst = 0; + return; + } + item = *listsrc; + node = ADDRESS( item, offset ); + node->prev = listdst; + *listdst = item; + *listsrc = 0; + return; +} + + + +/** + * Initialize a dual-direction linked list. + * + * The head of the linked list should be defined as a mmListDualHead + * structure, with the head parameter being a pointer to it. + */ +void mmListDualInit( mmListDualHead *head ) +{ + head->first = 0; + head->last = &head->first; + return; +} + + +/** + * Add the item to the beginning of a dual-direction linked list. + * + * The head of the linked list should be defined as a mmListDualHead structure, + * with the head parameter being a pointer to it. The offset parameter should + * be the offset of the mmListNode structure within the structure of the item. + * It can be easily obtained with the offsetof(,) macro. + */ +void mmListDualAddFirst( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + node->prev = &head->first; + node->next = head->first; + if( node->next ) + { + next = ADDRESS( node->next, offset ); + next->prev = &(node->next); + } + else + head->last = &(node->next); + head->first = item; + return; +} + + +/** + * Add the item to the end of a dual-direction linked list. + * + * The head of the linked list should be defined as a mmListDualHead structure, + * with the head parameter being a pointer to it. The offset parameter should + * be the offset of the mmListNode structure within the structure of the item. + * It can be easily obtained with the offsetof(,) macro. + */ +void mmListDualAddLast( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node; + void **prev; + prev = head->last; + *prev = item; + node = ADDRESS( item, offset ); + node->prev = head->last; + head->last = &(node->next); + node->next = 0; + return; +} + + +void mmListDualInsertAfter( mmListDualHead *head, void **prevnext, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + node->prev = prevnext; + node->next = *prevnext; + if( *prevnext ) + { + next = ADDRESS( *prevnext, offset ); + next->prev = &(node->next); + } + else + head->last = &(node->next); + *prevnext = item; + return; +} + + +/** + * Remove the item from a dual-direction linked list. + * + * The head of the linked list should be defined as a mmListDualHead structure, + * with the head parameter being a pointer to it. The offset parameter should + * be the offset of the mmListNode structure within the structure of the item. + * It can be easily obtained with the offsetof(,) macro. + */ +void mmListDualRemove( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + *(node->prev) = (void *)node->next; + if( node->next ) + { + next = ADDRESS( node->next, offset ); + next->prev = node->prev; + } + else + head->last = node->prev; + return; +} + + +void *mmListDualLast( mmListDualHead *head, intptr_t offset ) +{ + if( !( head->first ) ) + return 0; + return ADDRESS( head->last, -( offset + offsetof(mmListNode,next) ) ); +} + + +void *mmListDualPrevious( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node; + if( item == head->first ) + return 0; + node = ADDRESS( item, offset ); + return ADDRESS( node->prev, -( offset + offsetof(mmListNode,next) ) ); +} + + +#endif + + +void mmListLoopInit( mmListLoopHead *head ) +{ + head->first = 0; + head->last = 0; + return; +} + + +void mmListLoopAddFirst( mmListLoopHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *prev, *next; + node = ADDRESS( item, offset ); + if( !( head->first ) ) + { + head->first = item; + head->last = item; + node->prev = item; + node->next = item; + return; + } + node->prev = head->last; + node->next = head->first; + next = ADDRESS( head->first, offset ); + next->prev = item; + prev = ADDRESS( head->last, offset ); + prev->next = item; + head->first = item; + return; +} + + +void mmListLoopAddLast( mmListLoopHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *prev, *next; + node = ADDRESS( item, offset ); + if( !( head->first ) ) + { + head->first = item; + head->last = item; + node->prev = item; + node->next = item; + return; + } + prev = ADDRESS( head->last, offset ); + prev->next = item; + next = ADDRESS( head->first, offset ); + next->prev = item; + node->prev = head->last; + node->next = head->first; + head->last = item; + return; +} + + +void mmListLoopInsert( mmListLoopHead *head, void *previtem, void *item, intptr_t offset ) +{ + mmListNode *prev, *node, *next; + node = ADDRESS( item, offset ); + if( !( head->first ) ) + { + head->first = item; + head->last = item; + node->prev = item; + node->next = item; + return; + } + node->prev = previtem; + prev = ADDRESS( previtem, offset ); + node->next = prev->next; + prev->next = item; + next = ADDRESS( node->next, offset ); + next->prev = item; + if( head->last == previtem ) + head->last = item; + return; +} + + +void mmListLoopRemove( mmListLoopHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *prev, *next; + node = ADDRESS( item, offset ); + prev = ADDRESS( node->prev, offset ); + prev->next = node->next; + if( head->first == item ) + { + head->first = node->next; + if( head->first == item ) + head->first = 0; + } + next = ADDRESS( node->next, offset ); + next->prev = node->prev; + if( head->last == item ) + { + head->last = node->prev; + if( head->last == item ) + head->last = 0; + } + return; +} + + + +//// + + + +/** + * Private function to balance a branch of the tree after an insertion. + * + * The details of the implementation are left as an exercise to the reader. + */ +static void mmBTreeInsertBalance( void *item, intptr_t offset, void **root ) +{ + void *parent, *relative, *ancestor, *link; + mmBTreeNode *node, *pnode, *rnode, *anode, *lnode, *tnode; + + node = ADDRESS( item, offset ); + parent = node->parent; + + if( !( parent ) ) + { + node->flags |= MM_BTREE_FLAGS_STEP; + *root = item; + return; + } + + pnode = ADDRESS( parent, offset ); + if( pnode->flags & MM_BTREE_FLAGS_STEP ) + return; + + ancestor = pnode->parent; + anode = ADDRESS( ancestor, offset ); + + relative = anode->child[ ( pnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) ^ 1 ]; + if( ( relative ) && !( ( rnode = ADDRESS( relative, offset ) )->flags & MM_BTREE_FLAGS_STEP ) ) + { + anode->flags &= ~MM_BTREE_FLAGS_STEP; + pnode->flags |= MM_BTREE_FLAGS_STEP; + rnode->flags |= MM_BTREE_FLAGS_STEP; + mmBTreeInsertBalance( ancestor, offset, root ); + return; + } + + if( ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) != ( pnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) ) + { + if( ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) == MM_BTREE_FLAGS_RIGHT ) + { + node->flags = ( anode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) | MM_BTREE_FLAGS_STEP; + link = anode->parent; + + anode->parent = item; + anode->flags = MM_BTREE_FLAGS_RIGHT; + anode->child[0] = node->child[1]; + if( anode->child[0] ) + { + tnode = ADDRESS( anode->child[0], offset ); + tnode->flags &= ~MM_BTREE_FLAGS_RIGHT; + tnode->parent = ancestor; + } + + pnode->parent = item; + pnode->child[1] = node->child[0]; + if( pnode->child[1] ) + { + tnode = ADDRESS( pnode->child[1], offset ); + tnode->flags |= MM_BTREE_FLAGS_RIGHT; + tnode->parent = parent; + } + + if( relative ) + ( (mmBTreeNode *)ADDRESS( relative, offset ) )->flags |= MM_BTREE_FLAGS_STEP; + + node->child[0] = parent; + node->child[1] = ancestor; + node->parent = link; + if( link ) + { + lnode = ADDRESS( link, offset ); + lnode->child[ node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = item; + return; + } + *root = item; + return; + } + else + { + node->flags = ( anode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) | MM_BTREE_FLAGS_STEP; + link = anode->parent; + + anode->parent = item; + anode->flags = 0; + anode->child[1] = node->child[0]; + if( anode->child[1] ) + { + tnode = ADDRESS( anode->child[1], offset ); + tnode->flags |= MM_BTREE_FLAGS_RIGHT; + tnode->parent = ancestor; + } + + pnode->parent = item; + pnode->child[0] = node->child[1]; + if( pnode->child[0] ) + { + tnode = ADDRESS( pnode->child[0], offset ); + tnode->flags &= ~MM_BTREE_FLAGS_RIGHT; + tnode->parent = parent; + } + + if( relative ) + ( (mmBTreeNode *)ADDRESS( relative, offset ) )->flags |= MM_BTREE_FLAGS_STEP; + + node->child[0] = ancestor; + node->child[1] = parent; + node->parent = link; + if( link ) + { + lnode = ADDRESS( link, offset ); + lnode->child[ node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = item; + return; + } + *root = item; + return; + } + } + + if( ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) == MM_BTREE_FLAGS_RIGHT ) + { + pnode->flags = ( anode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) | MM_BTREE_FLAGS_STEP; + link = anode->parent; + + anode->parent = parent; + anode->flags = 0; + anode->child[1] = pnode->child[0]; + if( anode->child[1] ) + { + tnode = ADDRESS( anode->child[1], offset ); + tnode->flags |= MM_BTREE_FLAGS_RIGHT; + tnode->parent = ancestor; + } + + pnode->child[0] = ancestor; + pnode->child[1] = item; + pnode->parent = link; + if( link ) + { + lnode = ADDRESS( link, offset ); + lnode->child[ pnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = parent; + return; + } + *root = parent; + return; + } + else + { + pnode->flags = ( anode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) | MM_BTREE_FLAGS_STEP; + link = anode->parent; + + anode->parent = parent; + anode->flags = MM_BTREE_FLAGS_RIGHT; + anode->child[0] = pnode->child[1]; + if( anode->child[0] ) + { + tnode = ADDRESS( anode->child[0], offset ); + tnode->flags &= ~MM_BTREE_FLAGS_RIGHT; + tnode->parent = ancestor; + } + + pnode->child[0] = item; + pnode->child[1] = ancestor; + pnode->parent = link; + if( link ) + { + lnode = ADDRESS( link, offset ); + lnode->child[ pnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = parent; + return; + } + *root = parent; + return; + } + + return; +} + + +/** + * Insert an item within the balanced tree + * + * Inserts the item specified at the position specified by the parent pointer + * and the itemflag, which can be either MM_BTREE_FLAGS_LEFT or + * MM_BTREE_FLAGS_RIGHT to indicate on which side of the parent to insert. The + * parent pointer can be null to indicate the top of the tree. The offset + * parameter should be the offset of the mmBTreeNode structure within the + * structure of the item. The root parameter is a pointer to the root of the + * tree. + */ +void mmBTreeInsert( void *item, void *parent, int itemflag, intptr_t offset, void **root ) +{ + mmBTreeNode *node, *pnode; + + node = ADDRESS( item, offset ); + node->parent = parent; + node->child[0] = 0; + node->child[1] = 0; + node->flags = itemflag; + if( parent ) + { + pnode = ADDRESS( parent, offset ); + pnode->child[itemflag] = item; + } + + mmBTreeInsertBalance( item, offset, root ); + + return; +} + + +/** + * Find lowest left gap to use as pivot for removal. + */ +static void *mmBTreeRemoveSeek( void *item, intptr_t offset ) +{ + mmBTreeNode *node; + + node = ADDRESS( item, offset ); + item = node->child[1]; + for( ; ; ) + { + node = ADDRESS( item, offset ); + if( !( node->child[0] ) ) + break; + item = node->child[0]; + } + + return item; +} + + +static void mmBTreeRemoveBalanceLeft( void *item, intptr_t offset, void **root ); +static void mmBTreeRemoveBalanceRight( void *item, intptr_t offset, void **root ); + + +/** + * Private function to balance a left branch of the tree after a removal. + * + * The details of the implementation are left as an exercise to the reader. + */ +static void mmBTreeRemoveBalanceLeft( void *item, intptr_t offset, void **root ) +{ + int mask; + void **plink; + void *litem, *llitem, *lritem, *lrritem, *lrlitem; + mmBTreeNode *node, *lnode, *llnode, *lrnode, *lrrnode, *lrlnode, *tempnode; + + node = ADDRESS( item, offset ); + litem = node->child[0]; + lnode = ADDRESS( litem, offset ); + lritem = lnode->child[1]; + lrnode = ADDRESS( lritem, offset ); + + plink = root; + if( node->parent ) + plink = &( (mmBTreeNode *)ADDRESS( node->parent, offset ) )->child[ node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ]; + + if( !( lnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + lrlitem = lrnode->child[0]; + lrlnode = ADDRESS( lrlitem, offset ); + lrritem = lrnode->child[1]; + lrrnode = ADDRESS( lrritem, offset ); + + if( !( lrlitem ) ) + { + if( lrritem ) + { + lnode->flags = node->flags; + lnode->parent = node->parent; + lnode->child[1] = lrritem; + *plink = litem; + + lrrnode->flags = MM_BTREE_FLAGS_RIGHT; + lrrnode->parent = litem; + lrrnode->child[0] = lritem; + lrrnode->child[1] = item; + + lrnode->flags = MM_BTREE_FLAGS_STEP; + lrnode->parent = lrritem; + lrnode->child[1] = 0; + + node->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + node->parent = lrritem; + node->child[0] = 0; + } + else + goto lshift; + } + else if( !( lrlnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + lrnode->flags = node->flags; + lrnode->parent = node->parent; + lrnode->child[0] = litem; + lrnode->child[1] = item; + *plink = lritem; + + node->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + node->parent = lritem; + node->child[0] = lrritem; + if( lrritem ) + { + lrrnode->flags &= ~MM_BTREE_FLAGS_RIGHT; + lrrnode->parent = item; + } + + lnode->flags = 0; + lnode->parent = lritem; + lnode->child[1] = lrlitem; + lrlnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + lrlnode->parent = litem; + } + else if( lrrnode->flags & MM_BTREE_FLAGS_STEP ) + { + lshift: + lnode->flags = node->flags; + lnode->parent = node->parent; + lnode->child[1] = item; + *plink = litem; + + node->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + node->parent = litem; + node->child[0] = lritem; + lrnode->flags = 0; + lrnode->parent = item; + } + else + { + lnode->flags = node->flags; + lnode->parent = node->parent; + lnode->child[1] = lrritem; + *plink = litem; + + node->flags = MM_BTREE_FLAGS_RIGHT; + node->parent = lrritem; + node->child[0] = lrrnode->child[1]; + if( node->child[0] ) + { + tempnode = ADDRESS( node->child[0], offset ); + tempnode->flags = MM_BTREE_FLAGS_STEP; + tempnode->parent = item; + } + + lrnode->flags = 0; + lrnode->parent = lrritem; + lrnode->child[1] = lrrnode->child[0]; + if( lrnode->child[1] ) + { + tempnode = ADDRESS( lrnode->child[1], offset ); + tempnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + tempnode->parent = lritem; + } + + lrrnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + lrrnode->parent = litem; + lrrnode->child[0] = lritem; + lrrnode->child[1] = item; + } + + return; + } + + mask = node->flags & MM_BTREE_FLAGS_STEP; + llitem = lnode->child[0]; + llnode = ADDRESS( llitem, offset ); + + if( ( lritem ) && !( lrnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + lrlitem = lrnode->child[0]; + lrritem = lrnode->child[1]; + + lrnode->flags = node->flags; + lrnode->parent = node->parent; + lrnode->child[0] = litem; + lrnode->child[1] = item; + *plink = lritem; + + node->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + node->parent = lritem; + node->child[0] = lrritem; + if( lrritem ) + { + lrrnode = ADDRESS( lrritem, offset ); + lrrnode->parent = item; + lrrnode->flags &= ~MM_BTREE_FLAGS_RIGHT; + } + + lnode->flags = MM_BTREE_FLAGS_STEP; + lnode->parent = lritem; + lnode->child[1] = lrlitem; + if( lrlitem ) + { + lrlnode = ADDRESS( lrlitem, offset ); + lrlnode->parent = litem; + lrlnode->flags |= MM_BTREE_FLAGS_RIGHT; + } + } + else if( ( llitem ) && !( llnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + lnode->flags = node->flags | MM_BTREE_FLAGS_STEP; + lnode->parent = node->parent; + lnode->child[1] = item; + *plink = litem; + + node->flags = MM_BTREE_FLAGS_RIGHT | mask; + node->parent = litem; + node->child[0] = lritem; + if( lritem ) + { + lrnode->parent = item; + lrnode->flags = MM_BTREE_FLAGS_STEP; + } + + llnode->flags = mask; + } + else if( !( mask ) ) + { + node->flags |= MM_BTREE_FLAGS_STEP; + lnode->flags = 0; + } + else + { + lnode->flags = 0; + if( node->parent ) + ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ? mmBTreeRemoveBalanceLeft : mmBTreeRemoveBalanceRight )( node->parent, offset, root ); + } + + return; +} + + +/** + * Private function to balance a right branch of the tree after a removal. + * + * The details of the implementation are left as an exercise to the reader. + */ +static void mmBTreeRemoveBalanceRight( void *item, intptr_t offset, void **root ) +{ + int mask; + void **plink; + void *ritem, *rritem, *rlitem, *rllitem, *rlritem; + mmBTreeNode *node, *rnode, *rrnode, *rlnode, *rllnode, *rlrnode, *tempnode; + + node = ADDRESS( item, offset ); + ritem = node->child[1]; + rnode = ADDRESS( ritem, offset ); + rlitem = rnode->child[0]; + rlnode = ADDRESS( rlitem, offset ); + + plink = root; + if( node->parent ) + plink = &( (mmBTreeNode *)ADDRESS( node->parent, offset ) )->child[ node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ]; + + if( !( rnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + rlritem = rlnode->child[1]; + rlrnode = ADDRESS( rlritem, offset ); + rllitem = rlnode->child[0]; + rllnode = ADDRESS( rllitem, offset ); + + if( !( rlritem ) ) + { + if( rllitem ) + { + rnode->flags = node->flags; + rnode->parent = node->parent; + rnode->child[0] = rllitem; + *plink = ritem; + + rllnode->flags = 0; + rllnode->parent = ritem; + rllnode->child[1] = rlitem; + rllnode->child[0] = item; + + rlnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + rlnode->parent = rllitem; + rlnode->child[0] = 0; + + node->flags = MM_BTREE_FLAGS_STEP; + node->parent = rllitem; + node->child[1] = 0; + } + else + goto rshift; + } + else if( !( rlrnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + rlnode->flags = node->flags; + rlnode->parent = node->parent; + rlnode->child[1] = ritem; + rlnode->child[0] = item; + *plink = rlitem; + + node->flags = MM_BTREE_FLAGS_STEP; + node->parent = rlitem; + node->child[1] = rllitem; + if( rllitem ) + { + rllnode->flags |= MM_BTREE_FLAGS_RIGHT; + rllnode->parent = item; + } + + rnode->flags = MM_BTREE_FLAGS_RIGHT; + rnode->parent = rlitem; + rnode->child[0] = rlritem; + rlrnode->flags = MM_BTREE_FLAGS_STEP; + rlrnode->parent = ritem; + } + else if( rllnode->flags & MM_BTREE_FLAGS_STEP ) + { + rshift: + rnode->flags = node->flags; + rnode->parent = node->parent; + rnode->child[0] = item; + *plink = ritem; + + node->flags = MM_BTREE_FLAGS_STEP; + node->parent = ritem; + node->child[1] = rlitem; + rlnode->flags = MM_BTREE_FLAGS_RIGHT; + rlnode->parent = item; + } + else + { + rnode->flags = node->flags; + rnode->parent = node->parent; + rnode->child[0] = rllitem; + *plink = ritem; + + node->flags = 0; + node->parent = rllitem; + node->child[1] = rllnode->child[0]; + if( node->child[1] ) + { + tempnode = ADDRESS( node->child[1], offset ); + tempnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + tempnode->parent = item; + } + + rlnode->flags = MM_BTREE_FLAGS_RIGHT; + rlnode->parent = rllitem; + rlnode->child[0] = rllnode->child[1]; + if( rlnode->child[0] ) + { + tempnode = ADDRESS( rlnode->child[0], offset ); + tempnode->flags = MM_BTREE_FLAGS_STEP; + tempnode->parent = rlitem; + } + + rllnode->flags = MM_BTREE_FLAGS_STEP; + rllnode->parent = ritem; + rllnode->child[1] = rlitem; + rllnode->child[0] = item; + } + + return; + } + + mask = node->flags & MM_BTREE_FLAGS_STEP; + rritem = rnode->child[1]; + rrnode = ADDRESS( rritem, offset ); + + if( ( rlitem ) && !( rlnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + rlritem = rlnode->child[1]; + rllitem = rlnode->child[0]; + + rlnode->flags = node->flags; + rlnode->parent = node->parent; + rlnode->child[1] = ritem; + rlnode->child[0] = item; + *plink = rlitem; + + node->flags = MM_BTREE_FLAGS_STEP; + node->parent = rlitem; + node->child[1] = rllitem; + if( rllitem ) + { + rllnode = ADDRESS( rllitem, offset ); + rllnode->parent = item; + rllnode->flags |= MM_BTREE_FLAGS_RIGHT; + } + + rnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + rnode->parent = rlitem; + rnode->child[0] = rlritem; + if( rlritem ) + { + rlrnode = ADDRESS( rlritem, offset ); + rlrnode->parent = ritem; + rlrnode->flags &= ~MM_BTREE_FLAGS_RIGHT; + } + } + else if( ( rritem ) && !( rrnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + rnode->flags = node->flags | MM_BTREE_FLAGS_STEP; + rnode->parent = node->parent; + rnode->child[0] = item; + *plink = ritem; + + node->flags = mask; + node->parent = ritem; + node->child[1] = rlitem; + if( rlitem ) + { + rlnode->parent = item; + rlnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + } + + rrnode->flags = MM_BTREE_FLAGS_RIGHT | mask; + } + else if( !( mask ) ) + { + node->flags |= MM_BTREE_FLAGS_STEP; + rnode->flags = MM_BTREE_FLAGS_RIGHT; + } + else + { + rnode->flags = MM_BTREE_FLAGS_RIGHT; + if( node->parent ) + ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ? mmBTreeRemoveBalanceLeft : mmBTreeRemoveBalanceRight )( node->parent, offset, root ); + } + + return; +} + + +/** + * Remove an item within the balanced tree + * + * Remove the item specified from the balanced tree. The offset parameter + * should be the offset of the mmBTreeNode structure within the structure + * of the item. The root parameter is a pointer to the root of the tree. + */ +void mmBTreeRemove( void *item, intptr_t offset, void **root ) +{ + void *target, *parent, *child; + mmBTreeNode *node, *tnode, *pnode, *cnode; + + node = ADDRESS( item, offset ); + if( !( node->child[0] ) || !( node->child[1] ) ) + target = item; + else + target = mmBTreeRemoveSeek( item, offset ); + + tnode = ADDRESS( target, offset ); + child = tnode->child[0]; + if( tnode->child[1] ) + child = tnode->child[1]; + cnode = ADDRESS( child, offset ); + + parent = tnode->parent; + pnode = ADDRESS( parent, offset ); + + if( !( tnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + if( child ) + { + cnode->parent = parent; + cnode->flags = ( tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) | MM_BTREE_FLAGS_STEP; + } + if( parent ) + pnode->child[ tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = child; + else + *root = child; + } + else if( ( child ) && !( cnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + cnode->parent = parent; + cnode->flags = ( tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) | MM_BTREE_FLAGS_STEP; + if( parent ) + pnode->child[ tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = child; + else + *root = child; + } + else + { + if( child ) + { + cnode->parent = parent; + cnode->flags = tnode->flags; + } + if( parent ) + { + pnode->child[ tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = child; + ( tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ? mmBTreeRemoveBalanceLeft : mmBTreeRemoveBalanceRight )( parent, offset, root ); + } + else + *root = child; + } + + if( item != target ) + { + memcpy( tnode, node, sizeof(mmBTreeNode) ); + if( tnode->parent ) + ( (mmBTreeNode *)ADDRESS( tnode->parent, offset ) )->child[ tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = target; + else + *root = target; + if( tnode->child[0] ) + ( (mmBTreeNode *)ADDRESS( tnode->child[0], offset ) )->parent = target; + if( tnode->child[1] ) + ( (mmBTreeNode *)ADDRESS( tnode->child[1], offset ) )->parent = target; + } + + return; +} + + +void *mmBtreeMostLeft( void *root, intptr_t offset ) +{ + mmBTreeNode *node; + if( !( root ) ) + return 0; + node = ADDRESS( root, offset ); + while( node->child[MM_BTREE_FLAGS_LEFT] ) + { + root = node->child[MM_BTREE_FLAGS_LEFT]; + node = ADDRESS( root, offset ); + } + return root; +} + + +void *mmBtreeMostRight( void *root, intptr_t offset ) +{ + mmBTreeNode *node; + if( !( root ) ) + return 0; + node = ADDRESS( root, offset ); + while( node->child[MM_BTREE_FLAGS_RIGHT] ) + { + root = node->child[MM_BTREE_FLAGS_RIGHT]; + node = ADDRESS( root, offset ); + } + return root; +} + + +void *mmBtreeNeighbourLeft( void *item, intptr_t offset ) +{ + mmBTreeNode *node; + node = ADDRESS( item, offset ); + if( node->child[MM_BTREE_FLAGS_LEFT] ) + { + item = node->child[MM_BTREE_FLAGS_LEFT]; + node = ADDRESS( item, offset ); + while( node->child[MM_BTREE_FLAGS_RIGHT] ) + { + item = node->child[MM_BTREE_FLAGS_RIGHT]; + node = ADDRESS( item, offset ); + } + return item; + } + while( node->parent ) + { + node = ADDRESS( item, offset ); + item = node->parent; + if( ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) == MM_BTREE_FLAGS_RIGHT ) + return item; + } + return 0; +} + + +void *mmBtreeNeighbourRight( void *item, intptr_t offset ) +{ + mmBTreeNode *node; + node = ADDRESS( item, offset ); + if( node->child[MM_BTREE_FLAGS_RIGHT] ) + { + item = node->child[MM_BTREE_FLAGS_RIGHT]; + node = ADDRESS( item, offset ); + while( node->child[MM_BTREE_FLAGS_LEFT] ) + { + item = node->child[MM_BTREE_FLAGS_LEFT]; + node = ADDRESS( item, offset ); + } + return item; + } + while( node->parent ) + { + node = ADDRESS( item, offset ); + item = node->parent; + if( ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) == MM_BTREE_FLAGS_LEFT ) + return item; + } + return 0; +} + + +int mmBtreeListOrdered( void *root, intptr_t offset, int (*callback)( void *item, void *v ), void *v ) +{ + mmBTreeNode *node; + node = ADDRESS( root, offset ); + if( !( root ) ) + return 1; + if( !( mmBtreeListOrdered( node->child[MM_BTREE_FLAGS_LEFT], offset, callback, v ) ) ) + return 0; + if( !( callback( root, v ) ) ) + return 0; + if( !( mmBtreeListOrdered( node->child[MM_BTREE_FLAGS_RIGHT], offset, callback, v ) ) ) + return 0; + return 1; +} + + +int mmBtreeListBalanced( void *root, intptr_t offset, int (*callback)( void *item, void *v ), void *v ) +{ + mmBTreeNode *node; + node = ADDRESS( root, offset ); + if( !( root ) ) + return 1; + if( !( callback( root, v ) ) ) + return 0; + if( !( mmBtreeListOrdered( node->child[MM_BTREE_FLAGS_LEFT], offset, callback, v ) ) ) + return 0; + if( !( mmBtreeListOrdered( node->child[MM_BTREE_FLAGS_RIGHT], offset, callback, v ) ) ) + return 0; + return 1; +} + + +intptr_t mmBtreeItemCount( void *root, intptr_t offset ) +{ + mmBTreeNode *node; + if( !( root ) ) + return 0; + node = ADDRESS( root, offset ); + return 1 + mmBtreeItemCount( node->child[0], offset ) + mmBtreeItemCount( node->child[1], offset ); +} + + + +//// + + + +/** + * Initialize the memory block head specified. + * + * The parameters chunksize and chunkperblock define the size of each memory + * chunk and the number of chunks per block. + */ +void MM_FUNC(BlockInit)( mmBlockHead *head, size_t chunksize, int chunkperblock, int keepfreecount, int alignment MM_PARAMS ) +{ + memset( head, 0, sizeof(mmBlockHead) ); + head->alignment = 0; + + head->chunksize = chunksize; + if( head->chunksize < sizeof(mmListNode) ) + head->chunksize = sizeof(mmListNode); + if( alignment >= 0x10 ) + { + head->alignment = alignment - 1; + head->chunksize = ( chunksize + head->alignment ) & ~head->alignment; + } + head->chunkfreecount = 0; + head->relayalloc = mmAlloc; + head->relayfree = mmFree; + head->relayvalue = 0; + head->chunkperblock = chunkperblock; + head->allocsize = sizeof(mmBlock) + head->chunksize * head->chunkperblock; + head->keepfreecount = keepfreecount + chunkperblock; + mtSpinInit( &head->spinlock ); + return; +} + + +void MM_FUNC(BlockNodeInit)( mmBlockHead *head, int nodeindex, size_t chunksize, int chunkperblock, int keepfreecount, int alignment MM_PARAMS ) +{ + MM_FUNC(BlockInit)( head, chunksize, chunkperblock, keepfreecount, alignment MM_PASSPARAMS ); + head->relayalloc = mmNodeRelayAlloc; + head->relayfree = mmNodeRelayFree; + head->relayvalue = (void *)((intptr_t)nodeindex); + return; +} + + +static void mmBlockTreeInsert( mmBlock *block, void **treeroot ) +{ + mmBlock *root = *treeroot; + if( !( root ) ) + { + mmBTreeInsert( block, 0, 0, offsetof(mmBlock,node), treeroot ); + return; + } + for( ; ; ) + { + if( block < root ) + { + if( root->node.child[0] ) + { + root = root->node.child[0]; + continue; + } + mmBTreeInsert( block, root, MM_BTREE_FLAGS_LEFT, offsetof(mmBlock,node), treeroot ); + break; + } + else + { + if( root->node.child[1] ) + { + root = root->node.child[1]; + continue; + } + mmBTreeInsert( block, root, MM_BTREE_FLAGS_RIGHT, offsetof(mmBlock,node), treeroot ); + break; + } + } + return; +} + + +static mmBlock *mmBlockResolveChunk( void *p, mmBlock *root ) +{ + mmBlock *best = 0; + for( ; root ; ) + { + if( p < (void *)root ) + root = root->node.child[0]; + else + { + best = root; + root = root->node.child[1]; + } + } + return best; +} + + +/** + * Allocates a chunk of memory from the block. + * + * The size of the memory chunk returned was defined during the initialisation + * of the specified memory block head. + */ +void *MM_FUNC(BlockAlloc)( mmBlockHead *head MM_PARAMS ) +{ + int a; + mmBlock *block; + void *chunk; + mtSpinLock( &head->spinlock ); + if( !( head->freelist ) ) + { + if( head->alignment ) + block = MM_FUNC(AlignRelayAlloc)( head->relayalloc, head->relayvalue, head->allocsize, head->alignment, sizeof(mmBlock) MM_PASSPARAMS ); + else + block = head->relayalloc( head->relayvalue, head->allocsize MM_PASSPARAMS ); + if( !( block ) ) + { + fprintf( stderr, "ERROR %s:%d\n", __FILE__, __LINE__ ); + return 0; + } + block->freecount = head->chunkperblock; + mmListAdd( &head->blocklist, block, offsetof(mmBlock,listnode) ); + chunk = ADDRESS( block, sizeof(mmBlock) ); + for( a = 0 ; a < head->chunkperblock ; a++, chunk = ADDRESS( chunk, head->chunksize ) ) + mmListAdd( &head->freelist, chunk, 0 ); + mmBlockTreeInsert( block, &head->treeroot ); + head->chunkfreecount += head->chunkperblock; + } + chunk = head->freelist; + block = mmBlockResolveChunk( chunk, head->treeroot ); + mmListRemove( chunk, 0 ); + block->freecount--; + head->chunkfreecount--; + mtSpinUnlock( &head->spinlock ); + return chunk; +} + + +/** + * Free a chunk of memory previously allocated by mmBlockAlloc(). + */ +void MM_FUNC(BlockFree)( mmBlockHead *head, void *v MM_PARAMS ) +{ + int a; + mmBlock *block; + void *chunk; + chunk = v; + mtSpinLock( &head->spinlock ); + block = mmBlockResolveChunk( chunk, head->treeroot ); + if(!block) return; + block->freecount++; + head->chunkfreecount++; + mmListAdd( &head->freelist, chunk, 0 ); + if( ( block->freecount == head->chunkperblock ) && ( head->chunkfreecount >= head->keepfreecount ) ) + { + mmListRemove( block, offsetof(mmBlock,listnode) ); + chunk = ADDRESS( block, sizeof(mmBlock) ); + for( a = 0 ; a < head->chunkperblock ; a++, chunk = ADDRESS( chunk, head->chunksize ) ) + mmListRemove( chunk, 0 ); + mmBTreeRemove( block, offsetof(mmBlock,node), &head->treeroot ); + if( head->alignment ) + MM_FUNC(AlignRelayFree)( head->relayfree, head->relayvalue, block, head->allocsize MM_PASSPARAMS ); + else + head->relayfree( head->relayvalue, block, head->allocsize MM_PASSPARAMS ); + head->chunkfreecount -= head->chunkperblock; + } + mtSpinUnlock( &head->spinlock ); + return; +} + + +/** + * Release a chunk of memory previously allocated by mmBlockAlloc(). + * + * Unlike mmBlockFree(), this function will collect but never free unused + * memory, improving performance if the memory pages are to be soon reused. + */ +void MM_FUNC(BlockRelease)( mmBlockHead *head, void *v MM_PARAMS ) +{ + mmBlock *block; + void *chunk; + chunk = v; + mtSpinLock( &head->spinlock ); + block = mmBlockResolveChunk( chunk, head->treeroot ); + block->freecount++; + head->chunkfreecount++; + mmListAdd( &head->freelist, chunk, 0 ); + mtSpinUnlock( &head->spinlock ); + return; +} + + +/** + * Free all memory allocated by a block head. + */ +void MM_FUNC(BlockFreeAll)( mmBlockHead *head MM_PARAMS ) +{ + mmBlock *block, *blocknext; + mtSpinLock( &head->spinlock ); + for( block = head->blocklist ; block ; block = blocknext ) + { + blocknext = block->listnode.next; + if( head->alignment ) + MM_FUNC(AlignRelayFree)( head->relayfree, head->relayvalue, block, head->allocsize MM_PASSPARAMS ); + else + head->relayfree( head->relayvalue, block, head->allocsize MM_PASSPARAMS ); + } + head->blocklist = 0; + head->freelist = 0; + head->treeroot = 0; + mtSpinUnlock( &head->spinlock ); + mtSpinDestroy( &head->spinlock ); + return; +} + + +void MM_FUNC(BlockProcessList)( mmBlockHead *head, void *userpointer, int (*processchunk)( void *chunk, void *userpointer ) MM_PARAMS ) +{ + int i, blockcount, blockrefsize, chunkperblock; + intptr_t **bitsref; + intptr_t *blockmask; + intptr_t blockindex, chunkindex; + size_t chunksize; + void *p, *chunk; + mmBlock *block; + mmListNode *list; + + mtSpinLock( &head->spinlock ); + + blockcount = 0; + for( block = head->blocklist ; block ; block = block->listnode.next ) + block->blockindex = blockcount++; + + chunksize = head->chunksize; + chunkperblock = head->chunkperblock; + blockrefsize = ( ( chunkperblock + CPUCONF_INTPTR_BITS - 1 ) >> CPUCONF_INTPTR_BITSHIFT ) * sizeof(intptr_t); + bitsref = malloc( blockcount * ( sizeof(intptr_t *) + blockrefsize ) ); + memset( bitsref, 0, blockcount * ( sizeof(intptr_t *) + blockrefsize ) ); + + p = ADDRESS( bitsref, blockcount * sizeof(intptr_t *) ); + for( i = 0 ; i < blockcount ; i++ ) + { + bitsref[i] = p; + p = ADDRESS( p, blockrefsize ); + } + for( list = head->freelist ; list ; list = list->next ) + { + block = mmBlockResolveChunk( list, head->treeroot ); + chunkindex = ADDRESSDIFF( list, ADDRESS( block, sizeof(mmBlock) ) ) / chunksize; + bitsref[ block->blockindex ][ chunkindex >> CPUCONF_INTPTR_BITSHIFT ] |= (intptr_t)1 << ( chunkindex & (CPUCONF_INTPTR_BITS-1) ); + } + + blockindex = 0; + for( block = head->blocklist ; block ; block = block->listnode.next ) + { + blockmask = bitsref[ blockindex ]; + chunk = ADDRESS( block, sizeof(mmBlock) ); + for( chunkindex = 0 ; chunkindex < chunkperblock ; chunkindex++, chunk = ADDRESS( chunk, chunksize ) ) + { + if( blockmask[ chunkindex >> CPUCONF_INTPTR_BITSHIFT ] & ( (intptr_t)1 << ( chunkindex & (CPUCONF_INTPTR_BITS-1) ) ) ) + continue; + if( processchunk( chunk, userpointer ) ) + goto end; + } + blockindex++; + } + + end: + free( bitsref ); + mtSpinUnlock( &head->spinlock ); + + return; +} + + +int MM_FUNC(BlockUseCount)( mmBlockHead *head MM_PARAMS ) +{ + int blockcount, chunkcount; + mmBlock *block; + mmListNode *list; + + mtSpinLock( &head->spinlock ); + + blockcount = 0; + for( block = head->blocklist ; block ; block = block->listnode.next ) + blockcount++; + chunkcount = blockcount * head->chunkperblock; + for( list = head->freelist ; list ; list = list->next ) + chunkcount--; + + mtSpinUnlock( &head->spinlock ); + + return chunkcount; +} + + +int MM_FUNC(BlockFreeCount)( mmBlockHead *head MM_PARAMS ) +{ + int chunkcount; + + mtSpinLock( &head->spinlock ); + chunkcount = head->chunkfreecount; + mtSpinUnlock( &head->spinlock ); + + return chunkcount; +} + + + +//// + + + +typedef struct +{ + intptr_t index; + mmBTreeNode node; +} mmIndex; + +void mmIndexInit( mmIndexHead *head, int indexesperblock ) +{ + mmBlockInit( &head->indexblock, sizeof(mmIndex), indexesperblock, indexesperblock, 0x10 ); + head->indextree = 0; + mtSpinInit( &head->spinlock ); + return; +} + +void mmIndexFreeAll( mmIndexHead *head ) +{ + mmBlockFreeAll( &head->indexblock ); + head->indextree = 0; + mtSpinDestroy( &head->spinlock ); + return; +} + +void mmIndexAdd( mmIndexHead *head, intptr_t index ) +{ + mmIndex *indexnode, *root; + mtSpinLock( &head->spinlock ); + indexnode = mmBlockAlloc( &head->indexblock ); + indexnode->index = index; + if( !( head->indextree ) ) + { + mmBTreeInsert( indexnode, 0, 0, offsetof(mmIndex,node), &head->indextree ); + mtSpinUnlock( &head->spinlock ); + return; + } + for( root = head->indextree ; ; ) + { + if( index < root->index ) + { + if( root->node.child[0] ) + { + root = root->node.child[0]; + continue; + } + mmBTreeInsert( indexnode, root, MM_BTREE_FLAGS_LEFT, offsetof(mmIndex,node), &head->indextree ); + break; + } + else + { + if( root->node.child[1] ) + { + root = root->node.child[1]; + continue; + } + mmBTreeInsert( indexnode, root, MM_BTREE_FLAGS_RIGHT, offsetof(mmIndex,node), &head->indextree ); + break; + } + } + mtSpinUnlock( &head->spinlock ); + return; +} + +intptr_t mmIndexGet( mmIndexHead *head ) +{ + intptr_t index; + mmIndex *indexnode; + mtSpinLock( &head->spinlock ); + if( !( head->indextree ) ) + index = head->indexlimit++; + else + { + for( indexnode = head->indextree ; indexnode->node.child[0] ; ) + indexnode = indexnode->node.child[0]; + index = indexnode->index; + mmBTreeRemove( indexnode, offsetof(mmIndex,node), &head->indextree ); + mmBlockFree( &head->indexblock, indexnode ); + } + mtSpinUnlock( &head->spinlock ); + return index; +} + +int mmIndexRemove( mmIndexHead *head, intptr_t index ) +{ + intptr_t limit; + mmIndex *indexnode; + mtSpinLock( &head->spinlock ); + if( index >= head->indexlimit ) + { + for( limit = head->indexlimit ; limit < index ; limit++ ) + mmIndexAdd( head, index ); + head->indexlimit = index + 1; + mtSpinUnlock( &head->spinlock ); + return 1; + } + for( indexnode = head->indextree ; ; ) + { + if( !( indexnode ) ) + { + mtSpinUnlock( &head->spinlock ); + return 0; + } + if( index == indexnode->index ) + break; + if( index < indexnode->index ) + indexnode = indexnode->node.child[0]; + else + indexnode = indexnode->node.child[1]; + } + mmBTreeRemove( indexnode, offsetof(mmIndex,node), &head->indextree ); + mmBlockFree( &head->indexblock, indexnode ); + mtSpinUnlock( &head->spinlock ); + return 1; +} + +size_t mmIndexCount( mmIndexHead *head ) +{ + intptr_t count; + mtSpinLock( &head->spinlock ); + count = mmBtreeItemCount( head->indextree, offsetof(mmIndex,node) ); + mtSpinUnlock( &head->spinlock ); + return count; +} + + + +//// + + + +#define MM_BITTABLE_SIZEBYTES(head) ((((head->mapsize)<bitshift))>>CPUCONF_CHAR_BITSHIFT) +#define MM_BITTABLE_MINALIGN (4096) + +void mmBitTableInit( mmBitTableHead *head, int bitsperentry, int chunksize, int initmask ) +{ + int i; + head->bitshift = ccLog2Int32( ccAlignInt32( bitsperentry ) ); + head->bitmask = ( 1 << bitsperentry ) - 1; + head->countalign = ccAlignIntPtr( chunksize ); + if( head->countalign < MM_BITTABLE_MINALIGN ) + head->countalign = MM_BITTABLE_MINALIGN; + head->indexshift = CPUCONF_INTPTR_BITSHIFT >> head->bitshift; + head->indexmask = ( 1 << head->indexshift ) - 1; + head->mapsize = 0; + head->map = 0; + initmask &= ( 1 << head->bitmask ) - 1; + head->initmask = 0; + for( i = 0 ; i < CPUCONF_INTPTR_BITS ; i += head->bitmask ) + head->initmask |= initmask << i; + mtSpinInit( &head->spinlock ); + return; +} + +void mmBitTableFreeAll( mmBitTableHead *head ) +{ + free( head->map ); + head->map = 0; + head->mapsize = 0; + mtSpinDestroy( &head->spinlock ); + return; +} + +void mmBitTableSet( mmBitTableHead *head, uintptr_t index, int flags, int editmask ) +{ + uintptr_t *map; + uintptr_t offset, shift, mapindex, mapindexend; + mtSpinLock( &head->spinlock ); + map = head->map; + if( index >= head->mapsize ) + { + mapindex = head->mapsize >> CPUCONF_INTPTR_BITSHIFT; + head->mapsize = ( index + head->countalign ) & ( head->countalign - 1 ); + head->map = realloc( head->map, MM_BITTABLE_SIZEBYTES( head ) ); + mapindexend = head->mapsize >> CPUCONF_INTPTR_BITSHIFT; + map = head->map; + for( ; mapindex < mapindexend ; mapindex++ ) + map[mapindex] = head->initmask; + } + offset = index >> head->indexshift; + shift = ( index & head->indexmask ) << head->bitshift; + map[ offset ] &= ~( editmask << shift ); + map[ offset ] |= ( ( flags & editmask ) << shift ); + mtSpinUnlock( &head->spinlock ); + return; +} + +uintptr_t mmBitTableGet( mmBitTableHead *head, uintptr_t index ) +{ + uintptr_t *map; + uintptr_t offset, shift, value; + mtSpinLock( &head->spinlock ); + if( index >= head->mapsize ) + { + mtSpinUnlock( &head->spinlock ); + return head->initmask; + } + map = head->map; + offset = index >> head->indexshift; + shift = ( index & head->indexmask ) << head->bitshift; + value = ( map[ offset ] >> shift ) & head->bitmask; + mtSpinUnlock( &head->spinlock ); + return value; +} + + + +//// + + + +#define MM_GROW_NODE_MEM(x,b) ADDRESS(x,sizeof(mmGrowNode)+b) + +/** + * Private function to add a new node to the packed growing memory allocator + */ +static int mmGrowAdd( mmGrow *mgrow, int size MM_PARAMS ) +{ + mmGrowNode *mnode; + if( !( mnode = mmAlloc( 0, sizeof(mmGrowNode) + size MM_PASSPARAMS ) ) ) + return 0; + mnode->size = size; + mnode->used = 0; + mnode->next = mgrow->first; + mgrow->first = mnode; + return 1; +} + + +/** + * Initialize a packed growing memory allocator. + * + * While no space is wasted between allocated chunks of memory, it is + * impossible to free individual allocations. A growing memory allocator + * must be freed entirely at once. + */ +int MM_FUNC(GrowInit)( mmGrow *mgrow, size_t nodesize MM_PARAMS ) +{ + mgrow->first = 0; + mgrow->nodesize = nodesize; + mtSpinInit( &mgrow->spinlock ); + return mmGrowAdd( mgrow, nodesize MM_PASSPARAMS ); +} + + +/** + * Free all chunks of a growing memory allocator. + */ +void MM_FUNC(GrowFreeAll)( mmGrow *mgrow MM_PARAMS ) +{ + mmGrowNode *mnode, *next; + mtSpinLock( &mgrow->spinlock ); + for( mnode = mgrow->first ; mnode ; mnode = next ) + { + next = mnode->next; + mmFree( 0, mnode, 0 MM_PASSPARAMS ); + } + mgrow->first = 0; + mtSpinUnlock( &mgrow->spinlock ); + mtSpinDestroy( &mgrow->spinlock ); + return; +} + + +/** + * Allocate a chunk of memory from a growing memory allocator. + * + * The memory can only be freed with mmGrowFreeAll() for the whole allocator. + * There are no memory alignment garantees for the allocations, yet as all + * memory is tightly packed, the alignment is directly dependant on the size + * of the previous allocations. + */ +void *MM_FUNC(GrowAlloc)( mmGrow *mgrow, size_t bytes MM_PARAMS ) +{ + size_t a, fbytes; + mmGrowNode *mnode; + mtSpinLock( &mgrow->spinlock ); + mnode = mgrow->first; + fbytes = mnode->size - mnode->used; + if( fbytes >= bytes ) + { + a = mnode->used; + mnode->used += bytes; + mtSpinUnlock( &mgrow->spinlock ); + return MM_GROW_NODE_MEM( mnode, a ); + } + mmGrowAdd( mgrow, ( bytes > mgrow->nodesize ) ? bytes : mgrow->nodesize MM_PASSPARAMS ); + mnode = mgrow->first; + mnode->used = bytes; + mtSpinUnlock( &mgrow->spinlock ); + return MM_GROW_NODE_MEM( mnode, 0 ); +} + + +/* Rewind last memory allocation by a count of bytes */ +void MM_FUNC(GrowRewindLast)( mmGrow *mgrow, size_t rewind MM_PARAMS ) +{ + mmGrowNode *mnode; + mtSpinLock( &mgrow->spinlock ); + mnode = mgrow->first; + mnode->used -= rewind; + mtSpinUnlock( &mgrow->spinlock ); + return; +} + + + +//// + + + +#if 0 + +/** + * Initialize a directory structure. + * + * The pageshift parameter indicates the power of two defining the count of + * entries per page. The pagecount parameter specifies the initial count of + * pages, which can grow as required. + */ +int MM_FUNC(DirInit)( mmDirectory *dir, intptr_t pageshift, intptr_t pagecount MM_PARAMS ) +{ + if( !( dir->table = mmAlloc( 0, pagecount * sizeof(void **) MM_PASSPARAMS ) ) ) + return 0; + memset( dir->table, 0, pagecount * sizeof(void **) ); + dir->pagecount = pagecount; + dir->pageshift = pageshift; + dir->pagesize = 1 << pageshift; + dir->pagemask = dir->pagesize - 1; + mtSpinInit( &dir->spinlock ); + return 1; +} + + +/** + * Private function to resize a directory page table. + */ +static inline void mmDirResizeTable( mmDirectory *dir, intptr_t newcount MM_PARAMS ) +{ + intptr_t page; + for( page = newcount ; page < dir->pagecount ; page++ ) + { + if( dir->table[ page ] ) + mmFree( 0, dir->table[ page ], 0 MM_PASSPARAMS ); + } + if( !( dir->table = mmRealloc( 0, dir->table, newcount * sizeof(void **) MM_PASSPARAMS ) ) ) + return; + if( newcount > dir->pagecount ) + memset( ADDRESS( dir->table, dir->pagecount * sizeof(void **) ), 0, ( newcount - dir->pagecount ) * sizeof(void **) ); + dir->pagecount = newcount; + return; +} + + +void MM_FUNC(DirSize)( mmDirectory *dir, intptr_t size MM_PARAMS ) +{ + intptr_t page, pagecount; + pagecount = ( size >> dir->pageshift ) + 1; + size &= dir->pagemask; + mtSpinLock( &dir->spinlock ); + if( pagecount != dir->pagecount ) + mmDirResizeTable( dir, pagecount MM_PASSPARAMS ); + for( page = 0 ; page < pagecount ; page++ ) + { + if( dir->table[ page ] ) + continue; + if( !( dir->table[ page ] = mmAlloc( 0, dir->pagesize * sizeof(void *) MM_PASSPARAMS ) ) ) + return; + } + mtSpinUnlock( &dir->spinlock ); + return; +} + + +/** + * Sets the directory's item specified by the index to the pointer of the entry + * parameter. + * + * If the directory is too small to accept the new entry, the internal table of + * pages grow in size. The MM_DIR_ENTRY(dir,index) macro can be used to access + * directory entries, both for reading and writing, only if the entry is known + * to have been previously set. + */ +void MM_FUNC(DirSet)( mmDirectory *dir, intptr_t index, void *entry MM_PARAMS ) +{ + intptr_t page; + page = index >> dir->pageshift; + index &= dir->pagemask; + mtSpinLock( &dir->spinlock ); + if( page >= dir->pagecount ) + mmDirResizeTable( dir, ( dir->pagecount ? dir->pagecount << 1 : 1 ) MM_PASSPARAMS ); + if( !( dir->table[ page ] ) ) + { + if( !( dir->table[ page ] = mmAlloc( 0, dir->pagesize * sizeof(void *) MM_PASSPARAMS ) ) ) + return; + } + dir->table[ page ][ index ] = entry; + mtSpinUnlock( &dir->spinlock ); + return; +} + + +void *MM_FUNC(DirGet)( mmDirectory *dir, intptr_t index MM_PARAMS ) +{ + intptr_t page; + void *entry; + page = index >> dir->pageshift; + index &= dir->pagemask; + mtSpinLock( &dir->spinlock ); + if( (uintptr_t)page >= dir->pagecount ) + return 0; + if( !( dir->table[ page ] ) ) + return 0; + entry = dir->table[ page ][ index ]; + mtSpinUnlock( &dir->spinlock ); + return entry; +} + + +void MM_FUNC(DirSetFast)( mmDirectory *dir, intptr_t index, void *entry MM_PARAMS ) +{ + mtSpinLock( &dir->spinlock ); + dir->table[ index >> dir->pageshift ][ index & dir->pagemask ] = entry; + mtSpinUnlock( &dir->spinlock ); + return; +} + +void *MM_FUNC(DirGetFast)( mmDirectory *dir, intptr_t index MM_PARAMS ) +{ + void *entry; + mtSpinLock( &dir->spinlock ); + entry = dir->table[ index >> dir->pageshift ][ index & dir->pagemask ]; + mtSpinUnlock( &dir->spinlock ); + return entry; +} + + +/** + * Free a directory structure. + */ +void MM_FUNC(DirFree)( mmDirectory *dir MM_PARAMS ) +{ + intptr_t page; + if( !( dir->table ) ) + return; + mtSpinLock( &dir->spinlock ); + for( page = dir->pagecount-1 ; page >= 0 ; page-- ) + { + if( dir->table[ page ] ) + mmFree( 0, dir->table[ page ], 0 MM_PASSPARAMS ); + } + dir->pagecount = 0; + mmFree( 0, dir->table, 0 MM_PASSPARAMS ); + dir->table = 0; + mtSpinUnlock( &dir->spinlock ); + mtSpinDestroy( &dir->spinlock ); + return; +} + +#endif + + + +//// + + + +typedef struct +{ + int padding; +} mmAlign; + + +/** + * Allocate a chunk of memory with the alignment specified. + * + * The alignment parameter must be a power of two. The allocated memory must be + * freed with the mmAlignFree() function. + */ +void *MM_FUNC(AlignAlloc)( size_t bytes, intptr_t align MM_PARAMS ) +{ + intptr_t i; + void *v; + mmAlign *malign; + align--; + if( !( v = mmAlloc( 0, bytes + align + sizeof(mmAlign) MM_PASSPARAMS ) ) ) + return 0; + i = ( (intptr_t)v + align + sizeof(mmAlign) ) & ~align; + malign = ADDRESS( (void *)i, -(int)sizeof(mmAlign) ); + malign->padding = ADDRESSDIFF( i, v ); + return (void *)i; +} + +/** + * Free a chunk of memory that was allocated by mmAlignAlloc(). + */ +void MM_FUNC(AlignFree)( void *v MM_PARAMS ) +{ + mmAlign *malign; + malign = ADDRESS( v, -(int)sizeof(mmAlign) ); + mmFree( 0, ADDRESS( v, -malign->padding ), 0 MM_PASSPARAMS ); + return; +} + +void *MM_FUNC(AlignGrow)( void *v, size_t bytes, size_t copybytes, intptr_t align MM_PARAMS ) +{ + void *newv; + newv = MM_FUNC(AlignAlloc)( bytes, align MM_PASSPARAMS ); + memcpy( newv, v, copybytes ); + MM_FUNC(AlignFree)( v MM_PASSPARAMS ); + return newv; +} + + +void *MM_FUNC(AlignRelayAlloc)( void *(*relayalloc)( void *head, size_t bytes MM_PARAMS ), void *relayvalue, size_t bytes, intptr_t align, size_t displacement MM_PARAMS ) +{ + intptr_t i; + void *v; + mmAlign *malign; + align--; + if( !( v = relayalloc( relayvalue, bytes + align + sizeof(mmAlign) + displacement MM_PASSPARAMS ) ) ) + return 0; + i = ( (intptr_t)v + align + sizeof(mmAlign) + displacement ) & ~align; + i -= displacement; + malign = ADDRESS( (void *)i, -(int)sizeof(mmAlign) ); + malign->padding = ADDRESSDIFF( i, v ); + return (void *)i; +} + + +void MM_FUNC(AlignRelayFree)( void (*relayfree)( void *head, void *v, size_t bytes MM_PARAMS ), void *relayvalue, void *v, size_t bytes MM_PARAMS ) +{ + mmAlign *malign; + malign = ADDRESS( v, -(int)sizeof(mmAlign) ); + relayfree( relayvalue, ADDRESS( v, -malign->padding ), bytes MM_PASSPARAMS ); + return; +} + + + +//// + + + +typedef struct +{ + size_t bigsize; + mmListNode list; + int alignment; +} mmVolume; + +typedef struct +{ + int32_t nextoffset; + int32_t prevoffset; +} mmVolumeChunkHeader; + +typedef struct +{ + mmVolumeChunkHeader h; + mmBTreeNode node; +} mmVolumeChunk; + +#define MM_VOLUME_CHUNK_FLAGS_FREE (0x1) +#define MM_VOLUME_CHUNK_FLAGS_LAST (0x2) +#define MM_VOLUME_CHUNK_FLAGS_MASK (0x3) + +#define MM_VOLUME_CHUNK_GET_FLAGS(c) ((c)->h.nextoffset&MM_VOLUME_CHUNK_FLAGS_MASK) +#define MM_VOLUME_CHUNK_SET_FLAGS(c,f) (c)->h.nextoffset=(((c)->h.nextoffset&~MM_VOLUME_CHUNK_FLAGS_MASK)|(f)) +#define MM_VOLUME_CHUNK_GET_NEXTOFFSET(c) ((c)->h.nextoffset&~MM_VOLUME_CHUNK_FLAGS_MASK) +#define MM_VOLUME_CHUNK_SET_NEXTOFFSET(c,n) (c)->h.nextoffset=((c)->h.nextoffset&MM_VOLUME_CHUNK_FLAGS_MASK)|(n) +#define MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS(c,n,f) (c)->h.nextoffset=((n)|(f)) + +#define MM_VOLUME_BIGCHUNK_THRESHOLD (1<<30) + + +/** + * Initialize a memory volume. + * + * The parameter volumesize defines the default size of each chunk of memory + * internally allocated by the volume manager, chunks which will be sliced + * accordingly to memory allocation needs. The parameter minchunksize defines + * the minimum size a chunk of memory must have not to be fused. + */ +void MM_FUNC(VolumeInit)( mmVolumeHead *head, size_t volumesize, size_t minchunksize, size_t keepfreesize, size_t alignment MM_PARAMS ) +{ + head->alignment = 3; + if( alignment > 4 ) + head->alignment = alignment - 1; + head->volumesize = volumesize; + if( head->volumesize > (((size_t)1)<<31)-1 ) + head->volumesize = (((size_t)1)<<31)-1; + head->volumeblocksize = ( sizeof(mmVolume) + head->alignment ) & ~head->alignment; + head->minchunksize = ( ( minchunksize > sizeof(mmVolumeChunk) ? minchunksize : sizeof(mmVolumeChunk) ) + head->alignment ) & ~head->alignment; + head->volumechunksize = ( sizeof(mmVolumeChunkHeader) + head->alignment ) & ~head->alignment; + head->keepfreesize = keepfreesize; + head->totalfreesize = 0; + head->freeroot = 0; + head->volumelist = 0; + head->relayalloc = mmAlloc; + head->relayfree = mmFree; + head->relayvalue = 0; + mtSpinInit( &head->spinlock ); + return; +} + + +void MM_FUNC(VolumeNodeInit)( mmVolumeHead *head, int nodeindex, size_t volumesize, size_t minchunksize, size_t keepfreesize, size_t alignment MM_PARAMS ) +{ + MM_FUNC(VolumeInit)( head, volumesize, minchunksize, keepfreesize, alignment MM_PASSPARAMS ); + head->relayalloc = mmNodeRelayAlloc; + head->relayfree = mmNodeRelayFree; + head->relayvalue = (void *)((intptr_t)nodeindex); + return; +} + + +/** + * Private function to search for the best match given a desired memory chunk size. + */ +static mmVolumeChunk *mmVolumeFindFreeChunk( int32_t bytes, mmVolumeChunk *root ) +{ + int32_t chunksize; + mmVolumeChunk *best = 0; + for( ; root ; ) + { + chunksize = MM_VOLUME_CHUNK_GET_NEXTOFFSET( root ); + if( bytes <= chunksize ) + { + best = root; + if( bytes == chunksize ) + return best; + root = root->node.child[0]; + } + else + root = root->node.child[1]; + } + return best; +} + + +/** + * Private function to add a new free chunk within the balanced binary tree. + */ +static void mmVolumeAddFreeChunk( mmVolumeChunk *chunk, void **rootfree ) +{ + int32_t nextoffset = chunk->h.nextoffset; + mmVolumeChunk *root = *rootfree; + if( !( root ) ) + { + mmBTreeInsert( chunk, 0, 0, offsetof(mmVolumeChunk,node), rootfree ); + return; + } + for( ; ; ) + { + if( nextoffset < root->h.nextoffset ) + { + if( root->node.child[0] ) + { + root = root->node.child[0]; + continue; + } + mmBTreeInsert( chunk, root, MM_BTREE_FLAGS_LEFT, offsetof(mmVolumeChunk,node), rootfree ); + break; + } + else + { + if( root->node.child[1] ) + { + root = root->node.child[1]; + continue; + } + mmBTreeInsert( chunk, root, MM_BTREE_FLAGS_RIGHT, offsetof(mmVolumeChunk,node), rootfree ); + break; + } + } + return; +} + + + +void mmVolumeDebugList( mmVolumeHead *volumehead ) +{ + int errorcount; + int32_t prevoffset, nextoffset, chunkflags, volumesize, nextcheck; + mmVolume *volume; + mmVolumeChunk *chunk; + + printf( "\n==== VOLUME DEBUG BEGIN ====\n" ); + fflush( stdout ); + + errorcount = 0; + for( volume = volumehead->volumelist ; volume ; volume = volume->list.next ) + { + chunk = ADDRESS( volume, volumehead->volumeblocksize ); + + volumesize = 0; + nextcheck = 0; + + printf( "Begin volume block\n" ); + for( ; ; ) + { + chunkflags = MM_VOLUME_CHUNK_GET_FLAGS( chunk ); + nextoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + prevoffset = chunk->h.prevoffset; + + printf( " Chunk %p ( %p )\n", chunk, ADDRESS( chunk, volumehead->volumechunksize ) ); + printf( " Prevoffset : %d\n", prevoffset ); + printf( " Nextoffset : %d\n", nextoffset ); + printf( " Chunkflags : %d", chunkflags ); + if( chunkflags & MM_VOLUME_CHUNK_FLAGS_FREE ) + printf( " (free)" ); + if( chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ) + printf( " (last)" ); + printf( "\n" ); + fflush( stdout ); + + volumesize += nextoffset; + if( !( nextoffset ) || ( ( nextcheck ) && ( prevoffset != nextcheck ) ) ) + { + printf( " ERROR: Offset corruption!\n" ); + errorcount++; + break; + } + nextcheck = nextoffset; + + if( chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ) + break; + + chunk = ADDRESS( chunk, nextoffset ); + } + + printf( "End volume block, size : %d\n", volumesize ); + printf( "\n" ); + } + + printf( "==== VOLUME DEBUG END ====\n\n" ); + if( errorcount ) + { + printf( " ABORTING, ERRORS ENCOUNTERED : %d\n\n", errorcount ); + exit( 1 ); + } + fflush( stdout ); + + return; +} + + +int mmVolumeDebugGetTreeDepth( mmVolumeHead *head ) +{ + int treedepth; + mmVolumeChunk *root; + treedepth = 0; + for( root = head->freeroot ; root ; ) + { + if( root->node.child[0] ) + root = root->node.child[0]; + else + root = root->node.child[1]; + treedepth++; + } + return treedepth; +} + + + +/** + * Allocate memory from a volume. + * + * The function allocates a chunk of memory of arbitrary size from the + * specified volume manager. + */ +void *MM_FUNC(VolumeAlloc)( mmVolumeHead *head, size_t bytes MM_PARAMS ) +{ + int32_t chunkflags, nextoffset; + size_t allocsize, minsize, extrasize; + intptr_t vmem; + mmVolume *volume; + mmVolumeChunk *chunk, *newchunk, *nextchunk; + + mtSpinLock( &head->spinlock ); + + /* Big chunk handling */ + if( bytes >= MM_VOLUME_BIGCHUNK_THRESHOLD ) + { + allocsize = bytes + ( head->volumeblocksize + head->volumechunksize ); + + vmem = (intptr_t)head->relayalloc( head->relayvalue, allocsize MM_PASSPARAMS ); + volume = (void *)( ( vmem + head->alignment ) & ~head->alignment ); + volume->bigsize = allocsize; + volume->alignment = (intptr_t)volume - vmem; + mmListAdd( &head->volumelist, volume, offsetof(mmVolume,list) ); + + chunk = ADDRESS( volume, head->volumeblocksize ); + chunk->h.prevoffset = 0; + chunk->h.nextoffset = 0; + + mtSpinUnlock( &head->spinlock ); + return ADDRESS( chunk, head->volumechunksize ); + } + + /* Find best match among free chunks */ + bytes += head->volumechunksize; + if( bytes < head->minchunksize ) + bytes = head->minchunksize; + bytes = ( bytes + head->alignment ) & ~head->alignment; + chunk = mmVolumeFindFreeChunk( bytes, head->freeroot ); + + /* Allocate a new volume */ + if( !( chunk ) ) + { + allocsize = head->volumesize; + + /* Allocate new volume and add to list */ + minsize = bytes + ( head->volumeblocksize + head->volumechunksize ); + if( minsize > allocsize ) + allocsize = minsize; + + head->totalfreesize += allocsize - ( head->volumeblocksize + head->volumechunksize ); + + vmem = (intptr_t)head->relayalloc( head->relayvalue, allocsize MM_PASSPARAMS ); + volume = (void *)( ( vmem + head->alignment ) & ~head->alignment ); + volume->bigsize = 0; + volume->alignment = (intptr_t)volume - vmem; + mmListAdd( &head->volumelist, volume, offsetof(mmVolume,list) ); + + /* Add a free chunk to cover the whole volume */ + chunk = ADDRESS( volume, head->volumeblocksize ); + + chunk->h.prevoffset = 0; + allocsize -= head->volumeblocksize + volume->alignment; + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( chunk, allocsize, MM_VOLUME_CHUNK_FLAGS_FREE | MM_VOLUME_CHUNK_FLAGS_LAST ); + + mmVolumeAddFreeChunk( chunk, &head->freeroot ); + } + + /* Remove best match of free chunk from btree */ + mmBTreeRemove( chunk, offsetof(mmVolumeChunk,node), &head->freeroot ); + chunkflags = MM_VOLUME_CHUNK_GET_FLAGS( chunk ); + MM_VOLUME_CHUNK_SET_FLAGS( chunk, chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ); + + nextoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + head->totalfreesize -= nextoffset - head->volumechunksize; + extrasize = nextoffset - bytes; + if( extrasize >= head->minchunksize ) + { + /* Split and spawn a new free chunk */ + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( chunk, bytes, 0 ); + newchunk = ADDRESS( chunk, bytes ); + newchunk->h.prevoffset = bytes; + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( newchunk, extrasize, MM_VOLUME_CHUNK_FLAGS_FREE | ( chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ) ); + if( !( chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ) ) + { + nextchunk = ADDRESS( chunk, nextoffset ); + nextchunk->h.prevoffset = extrasize; + } + head->totalfreesize += extrasize - head->volumechunksize; + mmVolumeAddFreeChunk( newchunk, &head->freeroot ); + } + + mtSpinUnlock( &head->spinlock ); + return ADDRESS( chunk, head->volumechunksize ); +} + + +static mmVolumeChunk *mmVolumeMergeFree( mmVolumeHead *head, mmVolumeChunk *chunk ) +{ + int32_t nextoffset, chunkflags, next2offset; + mmVolumeChunk *prev, *next, *next2; + + prev = 0; + if( chunk->h.prevoffset ) + prev = ADDRESS( chunk, -chunk->h.prevoffset ); + chunkflags = MM_VOLUME_CHUNK_GET_FLAGS( chunk ); + nextoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + head->totalfreesize += nextoffset - head->volumechunksize; + next = 0; + if( !( chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ) ) + next = ADDRESS( chunk, nextoffset ); + if( ( prev ) && ( MM_VOLUME_CHUNK_GET_FLAGS( prev ) & MM_VOLUME_CHUNK_FLAGS_FREE ) ) + { + /* Merge free space with previous chunk */ + mmBTreeRemove( prev, offsetof(mmVolumeChunk,node), &head->freeroot ); + prev->h.nextoffset += nextoffset; + MM_VOLUME_CHUNK_SET_FLAGS( prev, chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ); + chunk = prev; + head->totalfreesize += head->volumechunksize; +/* + prev = 0; + if( chunk->h.prevoffset ) + prev = ADDRESS( chunk, -chunk->h.prevoffset ); +*/ + if( next ) + { + next->h.prevoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + if( MM_VOLUME_CHUNK_GET_FLAGS( next ) & MM_VOLUME_CHUNK_FLAGS_FREE ) + goto mergenext; + } + } + else if( ( next ) && ( MM_VOLUME_CHUNK_GET_FLAGS( next ) & MM_VOLUME_CHUNK_FLAGS_FREE ) ) + { + /* Merge free space with next chunk */ + mergenext: + mmBTreeRemove( next, offsetof(mmVolumeChunk,node), &head->freeroot ); + next2offset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( next ); + chunk->h.nextoffset += next2offset; + head->totalfreesize += head->volumechunksize; + if( MM_VOLUME_CHUNK_GET_FLAGS( next ) & MM_VOLUME_CHUNK_FLAGS_LAST ) + MM_VOLUME_CHUNK_SET_FLAGS( chunk, MM_VOLUME_CHUNK_FLAGS_FREE | MM_VOLUME_CHUNK_FLAGS_LAST ); + else + { + MM_VOLUME_CHUNK_SET_FLAGS( chunk, MM_VOLUME_CHUNK_FLAGS_FREE ); + next2 = ADDRESS( next, next2offset ); + next2->h.prevoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + } + } + else + { + /* Solitary free chunk */ + MM_VOLUME_CHUNK_SET_FLAGS( chunk, chunkflags | MM_VOLUME_CHUNK_FLAGS_FREE ); + } + + return chunk; +} + + +void MM_FUNC(VolumeRelease)( mmVolumeHead *head, void *v MM_PARAMS ) +{ + mmVolumeChunk *chunk; + mmVolume *volume; + + chunk = ADDRESS( v, -head->volumechunksize ); + mtSpinLock( &head->spinlock ); + + /* Big chunk handling */ + if( !( chunk->h.nextoffset ) ) + { + volume = ADDRESS( chunk, -head->volumeblocksize ); + mmListRemove( volume, offsetof(mmVolume,list) ); + head->relayfree( head->relayvalue, ADDRESS( volume, -volume->alignment ), volume->bigsize + sizeof(mmVolumeChunkHeader) + sizeof(mmVolumeChunk) + volume->alignment MM_PASSPARAMS ); + mtSpinUnlock( &head->spinlock ); + return; + } + + chunk = mmVolumeMergeFree( head, chunk ); + + /* Register the new free chunk */ + mmVolumeAddFreeChunk( chunk, &head->freeroot ); + + mtSpinUnlock( &head->spinlock ); + return; +} + + +/** + * Free a chunk of memory that was allocated by mmVolumeAlloc(). + * + * The flags parameter may contain the flag MM_VOLUME_FLAGS_RELEASE, which + * instructs the volume manager not to free unused memory, improving + * performance if the blocks are to be soon reused. + */ +void MM_FUNC(VolumeFree)( mmVolumeHead *head, void *v MM_PARAMS ) +{ + size_t totalfreesize; + mmVolumeChunk *chunk; + mmVolume *volume; + + chunk = ADDRESS( v, -head->volumechunksize ); + mtSpinLock( &head->spinlock ); + + /* Big chunk handling */ + if( !( chunk->h.nextoffset ) ) + { + volume = ADDRESS( chunk, -head->volumeblocksize ); + mmListRemove( volume, offsetof(mmVolume,list) ); + head->relayfree( head->relayvalue, ADDRESS( volume, -volume->alignment ), volume->bigsize + sizeof(mmVolumeChunkHeader) + sizeof(mmVolumeChunk) + volume->alignment MM_PASSPARAMS ); + mtSpinUnlock( &head->spinlock ); + return; + } + + chunk = mmVolumeMergeFree( head, chunk ); + + totalfreesize = head->totalfreesize - ( MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ) - head->volumechunksize ); + + /* If our free chunk is alone in there, free the whole volume */ + if( ( MM_VOLUME_CHUNK_GET_FLAGS( chunk ) & MM_VOLUME_CHUNK_FLAGS_LAST ) && !( chunk->h.prevoffset ) && ( totalfreesize >= head->keepfreesize ) ) + { + head->totalfreesize = totalfreesize; + volume = ADDRESS( chunk, -head->volumeblocksize ); + mmListRemove( volume, offsetof(mmVolume,list) ); + head->relayfree( head->relayvalue, ADDRESS( volume, -volume->alignment ), MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ) + sizeof(mmVolumeChunkHeader) + sizeof(mmVolumeChunk) + volume->alignment MM_PASSPARAMS ); + mtSpinUnlock( &head->spinlock ); + return; + } + + /* Register the new free chunk */ + mmVolumeAddFreeChunk( chunk, &head->freeroot ); + + mtSpinUnlock( &head->spinlock ); + return; +} + + + + +/** + * Shrink a chunk of memory that was allocated by mmVolumeAlloc(). + * + * The bytes parameter defines the new size of the chunk of memory. The + * specified pointer remains valid as the chunk is never relocated elsewhere. + * This function can not be used to attempt to increase the size of a chunk. + */ +void MM_FUNC(VolumeShrink)( mmVolumeHead *head, void *v, size_t bytes MM_PARAMS ) +{ + int32_t nextoffset, newoffset, chunkflags, nextflags, next2offset; + ssize_t freesize; + mmVolumeChunk *chunk, *next, *next2, *newchunk; + + chunk = ADDRESS( v, -head->volumechunksize ); + mtSpinLock( &head->spinlock ); + next = 0; + chunkflags = MM_VOLUME_CHUNK_GET_FLAGS( chunk ); + nextoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + if( !( chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ) ) + { + next = ADDRESS( chunk, nextoffset ); + nextflags = MM_VOLUME_CHUNK_GET_FLAGS( next ); + next2offset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( next ); + } + + bytes += head->volumechunksize; + if( bytes < head->minchunksize ) + bytes = head->minchunksize; + bytes = ( bytes + head->alignment ) & ~head->alignment; + freesize = nextoffset - bytes; + if( ( freesize < (ssize_t)head->minchunksize ) && ( !( next ) || !( nextflags & MM_VOLUME_CHUNK_FLAGS_FREE ) ) ) + { + mtSpinUnlock( &head->spinlock ); + return; + } + newoffset = bytes; + + newchunk = ADDRESS( chunk, newoffset ); + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( chunk, newoffset, 0 ); + newchunk->h.prevoffset = newoffset; + head->totalfreesize += freesize - head->volumechunksize; + if( !( next ) ) + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( newchunk, freesize, MM_VOLUME_CHUNK_FLAGS_FREE | MM_VOLUME_CHUNK_FLAGS_LAST ); + else + { + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( newchunk, freesize, MM_VOLUME_CHUNK_FLAGS_FREE ); + if( !( nextflags & MM_VOLUME_CHUNK_FLAGS_FREE ) ) + next->h.prevoffset = freesize; + else + { + mmBTreeRemove( next, offsetof(mmVolumeChunk,node), &head->freeroot ); + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( newchunk, freesize + next2offset, nextflags ); + if( !( nextflags & MM_VOLUME_CHUNK_FLAGS_LAST ) ) + { + next2 = ADDRESS( next, next2offset ); + next2->h.prevoffset = freesize + next2offset; + } + } + } + mmVolumeAddFreeChunk( newchunk, &head->freeroot ); + + mtSpinUnlock( &head->spinlock ); + return; +} + + +size_t MM_FUNC(VolumeGetAllocSize)( mmVolumeHead *head, void *v MM_PARAMS ) +{ + mmVolumeChunk *chunk; + chunk = ADDRESS( v, -head->volumechunksize ); + return MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ) - head->volumechunksize; +} + + +/** + * Inspect a volume manager and free unused pages of memory. + * + * This function may only free memory if MM_VOLUME_FLAGS_RELEASE was used when + * freeing chunks of memory allocated by the manager. + */ +void MM_FUNC(VolumeClean)( mmVolumeHead *head MM_PARAMS ) +{ + mmVolume *volume, *next; + mmVolumeChunk *chunk; + mtSpinLock( &head->spinlock ); + for( volume = head->volumelist ; volume ; volume = next ) + { + next = volume->list.next; + chunk = ADDRESS( volume, head->volumeblocksize ); + if( ( MM_VOLUME_CHUNK_GET_FLAGS(chunk) & MM_VOLUME_CHUNK_FLAGS_LAST ) && !( chunk->h.prevoffset ) ) + { + head->totalfreesize -= MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + mmListRemove( volume, offsetof(mmVolume,list) ); + head->relayfree( head->relayvalue, ADDRESS( volume, -volume->alignment ), MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ) + sizeof(mmVolumeChunkHeader) + sizeof(mmVolumeChunk) + volume->alignment MM_PASSPARAMS ); + } + } + mtSpinUnlock( &head->spinlock ); + return; +} + + +/** + * Free all memory allocated by the memory volume manager. + */ +void MM_FUNC(VolumeFreeAll)( mmVolumeHead *head MM_PARAMS ) +{ + int32_t nextoffset; + size_t volumesize; + mmVolume *volume, *next; + mmVolumeChunk *chunk; + mtSpinLock( &head->spinlock ); + for( volume = head->volumelist ; volume ; volume = next ) + { + next = volume->list.next; + chunk = ADDRESS( volume, head->volumeblocksize ); + volumesize = volume->bigsize; + if( chunk->h.nextoffset ) + { + volumesize = 0; + for( ; ; ) + { + nextoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + volumesize += nextoffset; + if( MM_VOLUME_CHUNK_GET_FLAGS( chunk ) & MM_VOLUME_CHUNK_FLAGS_LAST ) + break; + chunk = ADDRESS( chunk, nextoffset ); + } + } + head->relayfree( head->relayvalue, ADDRESS( volume, -volume->alignment ), volumesize + sizeof(mmVolumeChunkHeader) + sizeof(mmVolumeChunk) + volume->alignment MM_PASSPARAMS ); + } + mtSpinUnlock( &head->spinlock ); + mtSpinDestroy( &head->spinlock ); + return; +} + + +void *MM_FUNC(VolumeRealloc)( mmVolumeHead *head, void *v, size_t bytes MM_PARAMS ) +{ + size_t chunksize; + void *newv; + mmVolumeChunk *chunk; + if( !( v ) ) + return MM_FUNC(VolumeAlloc)( head, bytes MM_PASSPARAMS ); + chunk = ADDRESS( v, -head->volumechunksize ); + chunksize = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ) - head->volumechunksize; + if( bytes < chunksize ) + { + MM_FUNC(VolumeShrink)( head, v, bytes MM_PASSPARAMS ); + return v; + } + newv = MM_FUNC(VolumeAlloc)( head, bytes MM_PASSPARAMS ); + memcpy( newv, v, chunksize ); + MM_FUNC(VolumeFree)( head, v MM_PASSPARAMS ); + return newv; +} + + + +//// + + +#ifdef MM_ZONE_SUPPORT + + +typedef struct +{ + int32_t flags; + void *next, *prev; +} mmZoneChunk; + +#define MM_ZONE_CHUNK_FLAGS_FREE (0x1) +#define MM_ZONE_CHUNK_FLAGS_LAST (0x2) + + +int MM_FUNC(ZoneInit)( mmZoneHead *head, size_t zonesize, intptr_t alignment MM_PARAMS ) +{ + mmZoneChunk *chunk; + + memset( head, 0, sizeof(mmZoneHead) ); + mtSpinInit( &head->spinlock ); + head->pagesize = sysconf( _SC_PAGESIZE ) - 1; + head->pagealignment = head->pagesize - 1; + zonesize = ( zonesize + head->pagealignment ) & ~head->pagealignment; + head->zonesize = zonesize; + mtSpinLock( &head->spinlock ); + + head->address = mmap( 0x0, head->zonesize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 ); + if( head->address == MAP_FAILED ) + { +#ifdef MAP_NORESERVE + head->address = mmap( 0x0, head->zonesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0 ); + if( head->address == MAP_FAILED ) + return 0; +#else + return 0; +#endif + } + + head->alignment = 3; + if( alignment > 4 ) + head->alignment = alignment - 1; + head->chunkheadersize = ( sizeof(mmZoneChunk) + head->alignment ) & ~head->alignment; + + mprotect( head->address, head->pagesize, PROT_READ | PROT_WRITE ); + madvise( head->address, head->pagesize, MADV_NORMAL ); + chunk = head->address; + chunk->flags = MM_ZONE_CHUNK_FLAGS_FREE | MM_ZONE_CHUNK_FLAGS_LAST; + chunk->prev = 0; + chunk->next = ADDRESS( chunk, head->zonesize ); + head->chunklist = chunk; + + mtSpinUnlock( &head->spinlock ); + return 1; +} + + +void *MM_FUNC(ZoneAlloc)( mmZoneHead *head, size_t bytes MM_PARAMS ) +{ + int32_t lastflag, chunkflags; + mmZoneChunk *chunk, *newchunk, *nextchunk; + size_t chunknewsize, chunksize, unlocksize; + + bytes += head->chunkheadersize; + bytes = ( bytes + head->alignment ) & ~head->alignment; + + lastflag = 0; + for( chunk = head->chunklist ; ; chunk = chunk->next ) + { + if( lastflag ) + { + /* Our zone runs out of space? This is bad. Bad bad bad. */ + printf( "CRITICAL : Memory zone exhausted all virtual mapping space!\n" ); + return 0; + } + lastflag = chunk->flags & MM_ZONE_CHUNK_FLAGS_LAST; + if( !( chunk->flags & MM_ZONE_CHUNK_FLAGS_FREE ) ) + continue; + chunksize = ADDRESSDIFF( chunk->next, chunk ); + if( chunksize >= bytes ) + break; + } + + chunkflags = chunk->flags; + chunknewsize = ( bytes + head->pagealignment ) & ~head->pagealignment; + if( ( chunksize - chunknewsize ) >= head->pagesize ) + { + chunk->flags = 0; + newchunk = ADDRESS( chunk, chunknewsize ); + unlocksize = chunknewsize + head->chunkheadersize; + mprotect( chunk, unlocksize, PROT_READ | PROT_WRITE ); + madvise( chunk, unlocksize, MADV_NORMAL ); + newchunk->flags = chunkflags; + newchunk->prev = chunk; + newchunk->next = chunk->next; + if( !( chunkflags & MM_ZONE_CHUNK_FLAGS_LAST ) ) + { + nextchunk = chunk->next; + nextchunk->prev = newchunk; + } + chunk->next = newchunk; + } + else + { + chunk->flags &= ~MM_ZONE_CHUNK_FLAGS_FREE; + mprotect( chunk, chunknewsize, PROT_READ | PROT_WRITE ); + madvise( chunk, chunknewsize, MADV_NORMAL ); + } + + return ADDRESS( chunk, head->chunkheadersize ); +} + + +void MM_FUNC(ZoneFree)( mmZoneHead *head, void *v MM_PARAMS ) +{ + mmZoneChunk *chunk, *prevchunk, *nextchunk, *next2chunk; + size_t extrasize; + void *page; + + chunk = ADDRESS( v, -head->chunkheadersize ); + page = (void *)( ( (intptr_t)v + head->pagealignment ) & ~head->pagealignment ); + extrasize = ADDRESSDIFF( chunk->next, page ); + mprotect( page, extrasize, PROT_READ ); + madvise( page, extrasize, MADV_DONTNEED ); + + chunk->flags |= MM_ZONE_CHUNK_FLAGS_FREE; + prevchunk = chunk->prev; + nextchunk = 0; + if( !( chunk->flags & MM_ZONE_CHUNK_FLAGS_LAST ) ) + nextchunk = chunk->next; + + if( ( nextchunk ) && ( nextchunk->flags & MM_ZONE_CHUNK_FLAGS_FREE ) ) + { + chunk->next = nextchunk->next; + chunk->flags |= nextchunk->flags & MM_ZONE_CHUNK_FLAGS_LAST; + next2chunk = 0; + if( !( nextchunk->flags & MM_ZONE_CHUNK_FLAGS_LAST ) ) + { + next2chunk = nextchunk->next; + next2chunk->prev = chunk; + } + mprotect( nextchunk, head->pagesize, PROT_READ ); + madvise( nextchunk, head->pagesize, MADV_DONTNEED ); + nextchunk = 0; + if( !( chunk->flags & MM_ZONE_CHUNK_FLAGS_LAST ) ) + nextchunk = chunk->next; + } + + if( ( prevchunk ) && ( prevchunk->flags & MM_ZONE_CHUNK_FLAGS_FREE ) ) + { + prevchunk->next = chunk->next; + if( nextchunk ) + nextchunk->prev = prevchunk; + mprotect( chunk, head->pagesize, PROT_READ ); + madvise( chunk, head->pagesize, MADV_DONTNEED ); + } + + return; +} + + +void MM_FUNC(ZoneFreeAll)( mmZoneHead *head MM_PARAMS ) +{ + mtSpinLock( &head->spinlock ); + munmap( head->address, head->zonesize ); + mtSpinUnlock( &head->spinlock ); + return; +} + + +#endif + + + +//// + + + +#if MM_DEBUG + +#undef malloc +#undef free +#undef realloc + +#endif + +#undef mmDebugAlloc +#undef mmDebugFree +#undef mmDebugRealloc +#undef mmListUses + + +typedef struct +{ + const char *file; + int line; + size_t size; + size_t count; + mmListNode list; +#if MM_DEBUG_GUARD_BYTES + char guard[MM_DEBUG_GUARD_BYTES]; +#endif +} mmChunk; + +static void *mmChunkList = 0; +static size_t mmCount = 0; +static size_t mmMaxCount = 0; + + +void *mmDebugAlloc( size_t bytes, const char *file, int line ) +{ +#if MM_DEBUG_GUARD_BYTES + int guard; + char *endguard; +#endif + mmChunk *chunk; + mtMutexLock( &mmGlobalMutex ); +#if MM_DEBUG_MMAP + if( !( chunk = mmap( 0, sizeof(mmChunk) + bytes + MM_DEBUG_GUARD_BYTES, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 ) ) ) +#else + if( !( chunk = malloc( sizeof(mmChunk) + bytes + MM_DEBUG_GUARD_BYTES ) ) ) +#endif + { +#if MM_WINDOWS + fprintf( stderr, "FATAL : Denied memory allocation ( %ld ) at %s:%d !\nExiting\n", (long)bytes, file, line ); +#else + fprintf( stderr, "FATAL : Denied memory allocation ( %lld ) at %s:%d !\nExiting\n", (long long)bytes, file, line ); +#endif + exit( 1 ); + } + mmListAdd( &mmChunkList, chunk, offsetof(mmChunk,list) ); + chunk->file = file; + chunk->line = line; + chunk->size = bytes; +#if MM_DEBUG_GUARD_BYTES + endguard = ADDRESS( chunk, sizeof(mmChunk) + bytes ); + for( guard = 0 ; guard < MM_DEBUG_GUARD_BYTES ; guard++ ) + { + chunk->guard[guard] = 0x55; + endguard[guard] = 0x75; + } +#endif + mmCount += bytes; + if( mmCount > mmMaxCount ) + mmMaxCount = mmCount; + mtMutexUnlock( &mmGlobalMutex ); + return ADDRESS( chunk, sizeof(mmChunk) ); +} + + +void *mmDebugRealloc( void *v, size_t bytes, const char *file, int line ) +{ + size_t size; + mmChunk *chunk; + void *newchunk = 0; + newchunk = mmDebugAlloc( bytes, file, line ); + if( v ) + { + size = bytes; + chunk = ADDRESS( v, -(int)sizeof(mmChunk) ); + if( chunk->size < size ) + size = chunk->size; + if( size ) + memcpy( newchunk, v, size ); + mmDebugFree( v, file, line ); + } + return newchunk; +} + + +void mmDebugFree( void *v, const char *file, int line ) +{ + mmChunk *chunk; +#if MM_DEBUG_GUARD_BYTES + int guard; + char *endguard; +#endif + chunk = ADDRESS( v, -(int)sizeof(mmChunk) ); +#if MM_DEBUG_GUARD_BYTES + endguard = ADDRESS( chunk, sizeof(mmChunk) + chunk->size ); + for( guard = 0 ; guard < MM_DEBUG_GUARD_BYTES ; guard++ ) + { + if( chunk->guard[guard] != 0x55 ) + { + fprintf( stderr, "MALLOC START[%d] GUARD ERROR : Corruption of %s:%d\n", guard, chunk->file, chunk->line ); + exit( 1 ); + } + if( endguard[guard] != 0x75 ) + { + fprintf( stderr, "MALLOC END[%d] GUARD ERROR : Corruption of %s:%d\n", guard, chunk->file, chunk->line ); + exit( 1 ); + } + } +#endif + mtMutexLock( &mmGlobalMutex ); + mmListRemove( chunk, offsetof(mmChunk,list) ); + mmCount -= chunk->size; +#if MM_DEBUG_MMAP + #if MM_DEBUG_MMAP_LINGERING + mprotect( chunk, sizeof(mmChunk) + chunk->size + MM_DEBUG_GUARD_BYTES, PROT_NONE ); + #else + munmap( chunk, sizeof(mmChunk) + chunk->size + MM_DEBUG_GUARD_BYTES ); + #endif +#else + free( chunk ); +#endif + mtMutexUnlock( &mmGlobalMutex ); + return; +} + + +void *mmAlloc( void *unused, size_t bytes MM_PARAMS ) +{ +#if MM_DEBUG + return mmDebugAlloc( bytes MM_PASSPARAMS ); +#else + void *chunk; + #if MM_WINDOWS + if( !( chunk = malloc( bytes ) ) ) + fprintf( stderr, "WARNING : Denied memory allocation ( %ld )!\nExiting\n", (long)bytes ); + #else + if( !( chunk = malloc( bytes ) ) ) + fprintf( stderr, "WARNING : Denied memory allocation ( %lld )!\nExiting\n", (long long)bytes ); + #endif + return chunk; +#endif +} + + +void *mmRealloc( void *unused, void *v, size_t bytes MM_PARAMS ) +{ +#if MM_DEBUG + return mmDebugRealloc( v, bytes MM_PASSPARAMS ); +#else + #if MM_WINDOWS + if( !( v = realloc( v, bytes ) ) && ( bytes ) ) + fprintf( stderr, "WARNING : Denied memory reallocation ( %ld )!\nExiting\n", (long)bytes ); + #else + if( !( v = realloc( v, bytes ) ) && ( bytes ) ) + fprintf( stderr, "WARNING : Denied memory reallocation ( %lld )!\nExiting\n", (long long)bytes ); + #endif + return v; +#endif +} + + +void mmFree( void *unused, void *v, size_t bytes MM_PARAMS ) +{ +#if MM_DEBUG + mmDebugFree( v MM_PASSPARAMS ); +#else + free( v ); +#endif + return; +} + + + +/** + * Private function to add the sorted memory item in the linked list. + */ +static void mmListSortAdd( mmChunk *chunk, void **sortlist ) +{ + intptr_t i; + const char *s0, *s1; + mmChunk *sortchunk; + s0 = chunk->file; + for( ; *sortlist ; sortlist = &sortchunk->list.next ) + { + sortchunk = *sortlist; + s1 = sortchunk->file; + for( i = 0 ; ; i++ ) + { + if( s0[i] < s1[i] ) + goto insert; + if( s0[i] > s1[i] ) + break; + if( s1[i] ) + continue; + if( chunk->line > sortchunk->line ) + break; + goto insert; + } + } + insert: + mmListAdd( sortlist, chunk, offsetof(mmChunk,list) ); + return; +} + + +/** + * List all memory allocations at any time and print the results on stdout. + */ +void mmListUses( const char *file, int line ) +{ + mmChunk *chunk; + mmChunk *chunksort; + void *sortlist; + size_t totalsize; + mtMutexLock( &mmGlobalMutex ); + printf( "-- Memory allocation listing at %s:%d --\n", file, line ); + if( !( mmChunkList ) ) + { + printf( " List empty\n\n" ); + mtMutexUnlock( &mmGlobalMutex ); + return; + } + sortlist = 0; + for( chunk = mmChunkList ; chunk ; chunk = chunk->list.next ) + { + for( chunksort = sortlist ; chunksort ; chunksort = chunksort->list.next ) + { + if( chunk->file != chunksort->file ) + continue; + if( chunk->line != chunksort->line ) + continue; + goto found; + } + if( !( chunksort = malloc( sizeof(mmChunk) ) ) ) + { + mtMutexUnlock( &mmGlobalMutex ); + return; + } + memset( chunksort, 0, sizeof(mmChunk) ); + chunksort->file = chunk->file; + chunksort->line = chunk->line; + chunksort->count = 0; + mmListSortAdd( chunksort, &sortlist ); + found: + chunksort->size += chunk->size; + chunksort->count++; + } + totalsize = 0; + for( chunksort = sortlist ; chunksort ; chunksort = chunk ) + { + printf( " %10ld bytes in %6ld chunk(s) allocated at %s:%d\n", (long)chunksort->size, (long)chunksort->count, chunksort->file, chunksort->line ); + totalsize += chunksort->size; + chunk = chunksort->list.next; + free( chunksort ); + } +#if MM_WINDOWS + printf( " %10ld bytes total\n", (long)totalsize ); + printf( " %10ld bytes maximum reached\n\n", (long)mmMaxCount ); +#else + printf( " %10lld bytes total\n", (long long)totalsize ); + printf( " %10lld bytes maximum reached\n\n", (long long)mmMaxCount ); +#endif + mtMutexUnlock( &mmGlobalMutex ); + return; +} + + + +//// + + + +#ifdef MM_WIN32 + +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL + +int mmGetTimeOfDay( struct timeval *tv ) +{ + FILETIME ft; + uint64_t curtime; + if( tv ) + { + GetSystemTimeAsFileTime( &ft ); + curtime = ft.dwHighDateTime; + curtime <<= 32; + curtime |= ft.dwLowDateTime; + curtime /= 10; + curtime -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long)( curtime / 1000000UL ); + tv->tv_usec = (long)( curtime % 1000000UL ); + } + return 0; +} + +#endif diff --git a/ecere/src/gfx/newFonts/cc/mm.h b/ecere/src/gfx/newFonts/cc/mm.h new file mode 100644 index 0000000000..7f8f8a01a7 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mm.h @@ -0,0 +1,823 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2016 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +/** + * @file + * + * Global memory management header. + */ + +#ifndef MM_H +#define MM_H + +#include "cpuconfig.h" + +#include + +#define OFFSET(s, m) ((unsigned int)(uintptr_t) (&((s *) 0)->m)) + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define MM_DEBUG (0) + +#define MM_INLINE_LIST_FUNCTIONS (1) + +#define MM_ALLOC_CHECK (1) + + +#define MM_DEBUG_GUARD_BYTES (32) +#define MM_DEBUG_MMAP (1) +/* Enabling this will lead to ever growing memory usage! Strictly for debugging. */ +#define MM_DEBUG_MMAP_LINGERING (0) + + + +//// + + +#ifdef MM_NUMA + #include +#endif + + +#if defined(__linux__) || defined(__gnu_linux__) || defined(__linux) || defined(__linux) + #define MM_LINUX (1) + #define MM_UNIX (1) +#elif defined(__APPLE__) + #define MM_OSX (1) + #define MM_UNIX (1) +#elif defined(__unix__) || defined(__unix) || defined(unix) + #define MM_UNIX (1) +#elif defined(_WIN64) || defined(__WIN64__) || defined(WIN64) + #define MM_WIN64 (1) + #define MM_WIN32 (1) + #define MM_WINDOWS (1) +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + #define MM_WIN32 (1) + #define MM_WINDOWS (1) +#endif + +#if __MINGW64__ + #define MM_MINGW32 (1) + #define MM_MINGW64 (1) +#elif __MINGW32__ + #define MM_MINGW32 (1) +#endif + + +#if !MM_UNIX + #undef MM_DEBUG_MMAP + #define MM_DEBUG_MMAP (0) +#endif + + +#define MM_CPU_COUNT_MAXIMUM (1024) +#define MM_NODE_COUNT_MAXIMUM (256) + + +#ifndef CPUCONF_CACHE_LINE_SIZE + #define CPUCONF_CACHE_LINE_SIZE 64 +#endif + + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) + #define MM_CACHE_ALIGN __attribute__((aligned(CPUCONF_CACHE_LINE_SIZE))) + #define MM_RESTRICT __restrict + #define MM_NOINLINE __attribute__((noinline)) + #define MM_ALWAYSINLINE __attribute__((always_inline)) + #define MM_ALIGN8 __attribute__((aligned(8))) + #define MM_ALIGN16 __attribute__((aligned(16))) + #define MM_ALIGN32 __attribute__((aligned(32))) + #define MM_ALIGN64 __attribute__((aligned(64))) + #define MM_ALIGNED8(p) __builtin_assume_aligned(p,8) + #define MM_ALIGNED16(p) __builtin_assume_aligned(p,16) + #define MM_ALIGNED32(p) __builtin_assume_aligned(p,32) + #define MM_ALIGNED64(p) __builtin_assume_aligned(p,64) +#elif defined(_MSC_VER) + #define MM_CACHE_ALIGN __declspec(align(64)) + #define MM_RESTRICT __restrict + #define MM_NOINLINE __declspec(noinline) + #define MM_ALWAYSINLINE __forceinline + #define MM_ALIGN8 __declspec(align(8)) + #define MM_ALIGN16 __declspec(align(16)) + #define MM_ALIGN32 __declspec(align(32)) + #define MM_ALIGN64 __declspec(align(64)) + #define MM_ALIGNED8(p) (p) + #define MM_ALIGNED16(p) (p) + #define MM_ALIGNED32(p) (p) + #define MM_ALIGNED64(p) (p) + #ifndef inline + #define inline __inline + #endif + #ifndef restrict + #define restrict __restrict + #endif + #ifndef ssize_t + #if MM_ARCH_AMD64 + #define ssize_t int64_t + #else + #define ssize_t int32_t + #endif + #endif +#else + #define MM_CACHE_ALIGN + #define MM_RESTRICT + #define MM_NOINLINE + #define MM_ALWAYSINLINE + #define MM_ALIGN8 + #define MM_ALIGN16 + #define MM_ALIGN32 + #define MM_ALIGN64 + #define MM_ALIGNED8(p) (p) + #define MM_ALIGNED16(p) (p) + #define MM_ALIGNED32(p) (p) + #define MM_ALIGNED64(p) (p) +#endif + +#define MM_ERROR() {printf("MM Error at %s:%d\n",file,line);exit(1)} + +#ifndef ADDRESS + #define ADDRESS(p,o) ((void *)(((char *)p)+(o))) +#endif + +#ifndef ADDRESSDIFF + #define ADDRESSDIFF(a,b) (((char *)a)-((char *)b)) +#endif + + +#if CC_UNIX || defined(__MINGW32__) +#include +#endif + +#ifdef __WIN32__ +int mmGetTimeOfDay( struct timeval *tv ); +#define gettimeofday(a,b) mmGetTimeOfDay(a) +#endif + + +typedef struct +{ + int numaflag; + int pagesize; + int cpucount; + int nodecount; + int cpunode[MM_CPU_COUNT_MAXIMUM]; + int64_t nodesize[MM_NODE_COUNT_MAXIMUM]; + int nodecpucount[MM_NODE_COUNT_MAXIMUM]; + int64_t sysmemory; +} mmContext; + +extern mmContext mmcontext; + + +#include "mmatomic.h" + + +#if MM_DEBUG + #define MM_FUNC(n) mm##n##Debug + #define MM_PARAMS , const char *file, int line +#else + #define MM_FUNC(n) mm##n + #define MM_PARAMS +#endif + + +//// + + +void mmInit(); +void mmEnd(); + + +void mmThreadBindToNode( int nodeindex ); +void mmThreadBindToNode( int nodeindex ); +void mmThreadBindToCpu( int cpuindex ); +int mmCpuGetNode( int cpuindex ); + +void *mmNodeAlloc( int nodeindex, size_t size ); +void mmNodeFree( int nodeindex, void *v, size_t size ); +void mmNodeMap( int nodeindex, void *start, size_t bytes ); + +void *mmNodeAlignAlloc( int nodeindex, size_t size, intptr_t align ); +void mmNodeAlignFree( int nodeindex, void *v, size_t size ); + + + +//// + + +#include "mmthread.h" + +typedef struct +{ + void **prev; + void *next; +} mmListNode; + +typedef struct +{ + void *first; + void **last; +} mmListDualHead; + +typedef struct +{ + void *first; + void *last; +} mmListLoopHead; + +#if !MM_INLINE_LIST_FUNCTIONS + +void mmListAdd( void **list, void *item, intptr_t offset ); +void mmListRemove( void *item, intptr_t offset ); +void mmListMergeList( void **listdst, void **listsrc, intptr_t offset ); +void mmListMoveList( void **listdst, void **listsrc, intptr_t offset ); + +void mmListDualInit( mmListDualHead *head ); +void mmListDualAddFirst( mmListDualHead *head, void *item, intptr_t offset ); +void mmListDualAddLast( mmListDualHead *head, void *item, intptr_t offset ); +void mmListDualInsertAfter( mmListDualHead *head, void **prevnext, void *item, intptr_t offset ); +void mmListDualRemove( mmListDualHead *head, void *item, intptr_t offset ); +void *mmListDualLast( mmListDualHead *head, intptr_t offset ); +void *mmListDualPrevious( mmListDualHead *head, void *item, intptr_t offset ); + +#else + +static inline void mmListAdd( void **list, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + node->prev = list; + node->next = *list; + if( *list ) + { + next = ADDRESS( *list, offset ); + next->prev = &(node->next); + } + *list = item; + return; +} + +static inline void mmListRemove( void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + *(node->prev) = (void *)node->next; + if( node->next ) + { + next = ADDRESS( node->next, offset ); + next->prev = node->prev; + } + return; +} + +static inline void mmListMergeList( void **listdst, void **listsrc, intptr_t offset ) +{ + void *item; + mmListNode *node; + if( !( *listsrc ) ) + return; + for( item = *listdst ; item ; item = node->next ) + { + node = ADDRESS( item, offset ); + listdst = &node->next; + } + item = *listsrc; + node = ADDRESS( item, offset ); + node->prev = listdst; + *listdst = item; + *listsrc = 0; + return; +} + +static inline void mmListMoveList( void **listdst, void **listsrc, intptr_t offset ) +{ + void *item; + mmListNode *node; + if( !( *listsrc ) ) + { + *listdst = 0; + return; + } + item = *listsrc; + node = ADDRESS( item, offset ); + node->prev = listdst; + *listdst = item; + *listsrc = 0; + return; +} + +static inline void mmListDualInit( mmListDualHead *head ) +{ + head->first = 0; + head->last = &head->first; + return; +} + +static inline void mmListDualAddFirst( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + node->prev = &head->first; + node->next = head->first; + if( node->next ) + { + next = ADDRESS( node->next, offset ); + next->prev = &(node->next); + } + else + head->last = &(node->next); + head->first = item; + return; +} + +static inline void mmListDualAddLast( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node; + void **prev; + prev = head->last; + *prev = item; + node = ADDRESS( item, offset ); + node->prev = head->last; + head->last = &(node->next); + node->next = 0; + return; +} + +static inline void mmListDualInsertAfter( mmListDualHead *head, void **prevnext, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + node->prev = prevnext; + node->next = *prevnext; + if( *prevnext ) + { + next = ADDRESS( *prevnext, offset ); + next->prev = &(node->next); + } + else + head->last = &(node->next); + *prevnext = item; + return; +} + +static inline void mmListDualRemove( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + *(node->prev) = (void *)node->next; + if( node->next ) + { + next = ADDRESS( node->next, offset ); + next->prev = node->prev; + } + else + head->last = node->prev; + return; +} + +static inline void *mmListDualLast( mmListDualHead *head, intptr_t offset ) +{ + if( !( head->first ) ) + return 0; + return ADDRESS( head->last, -( offset + OFFSET(mmListNode,next) ) ); +} + +static inline void *mmListDualPrevious( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node; + if( item == head->first ) + return 0; + node = ADDRESS( item, offset ); + return ADDRESS( node->prev, -( offset + OFFSET(mmListNode,next) ) ); +} + +#endif + +void mmListLoopInit( mmListLoopHead *head ); +void mmListLoopAddFirst( mmListLoopHead *head, void *item, intptr_t offset ); +void mmListLoopAddLast( mmListLoopHead *head, void *item, intptr_t offset ); +void mmListLoopInsert( mmListLoopHead *head, void *previtem, void *item, intptr_t offset ); +void mmListLoopRemove( mmListLoopHead *head, void *item, intptr_t offset ); +void *mmListLoopLast( mmListLoopHead *head, intptr_t offset ); + + + +//// + + + +typedef struct +{ + void *child[2]; + void *parent; + int flags; +} mmBTreeNode; + +#define MM_BTREE_FLAGS_LEFT (0) +#define MM_BTREE_FLAGS_RIGHT (1) +#define MM_BTREE_FLAGS_DIRECTION_MASK (1) +#define MM_BTREE_FLAGS_STEP (2) + +void mmBTreeInsert( void *item, void *parent, int itemflag, intptr_t offset, void **root ); +void mmBTreeRemove( void *item, intptr_t offset, void **root ); + +void *mmBtreeMostLeft( void *root, intptr_t offset ); +void *mmBtreeMostRight( void *root, intptr_t offset ); +void *mmBtreeNeighbourLeft( void *item, intptr_t offset ); +void *mmBtreeNeighbourRight( void *item, intptr_t offset ); +intptr_t mmBtreeItemCount( void *root, intptr_t offset ); +int mmBtreeListOrdered( void *root, intptr_t offset, int (*callback)( void *item, void *v ), void *v ); +int mmBtreeListBalanced( void *root, intptr_t offset, int (*callback)( void *item, void *v ), void *v ); + + + +//// + + + +typedef struct +{ + mmListNode listnode; + mmBTreeNode node; + int freecount; + int blockindex; +} mmBlock; + +typedef struct +{ + void *blocklist; + void *freelist; + size_t chunksize; + int chunkperblock; + int alignment; + size_t allocsize; + int keepfreecount; + int chunkfreecount; + void *treeroot; + void *(*relayalloc)( void *head, size_t bytes MM_PARAMS ); + void (*relayfree)( void *head, void *v, size_t bytes MM_PARAMS ); + void *relayvalue; + mtSpin spinlock; +} mmBlockHead; + +void MM_FUNC(BlockInit)( mmBlockHead *head, size_t chunksize, int chunkperblock, int keepfreecount, int alignment MM_PARAMS ); +void MM_FUNC(BlockNodeInit)( mmBlockHead *head, int nodeindex, size_t chunksize, int chunkperblock, int keepfreecount, int alignment MM_PARAMS ); +void *MM_FUNC(BlockAlloc)( mmBlockHead *head MM_PARAMS ); +void MM_FUNC(BlockRelease)( mmBlockHead *head, void *v MM_PARAMS ); +void MM_FUNC(BlockFree)( mmBlockHead *head, void *v MM_PARAMS ); +void MM_FUNC(BlockFreeAll)( mmBlockHead *head MM_PARAMS ); +void MM_FUNC(BlockProcessList)( mmBlockHead *head, void *userpointer, int (*processchunk)( void *chunk, void *userpointer ) MM_PARAMS ); +int MM_FUNC(BlockUseCount)( mmBlockHead *head MM_PARAMS ); +int MM_FUNC(BlockFreeCount)( mmBlockHead *head MM_PARAMS ); + +#if MM_DEBUG + #define mmBlockInit(v,w,x,y,z) MM_FUNC(BlockInit)(v,w,x,y,z,__FILE__,__LINE__) + #define mmBlockNodeInit(u,v,w,x,y,z) MM_FUNC(BlockNodeInit)(u,v,w,x,y,z,__FILE__,__LINE__) + #define mmBlockAlloc(x) MM_FUNC(BlockAlloc)(x,__FILE__,__LINE__) + #define mmBlockRelease(x,y) MM_FUNC(BlockRelease)(x,y,__FILE__,__LINE__) + #define mmBlockFree(x,y) MM_FUNC(BlockFree)(x,y,__FILE__,__LINE__) + #define mmBlockFreeAll(x) MM_FUNC(BlockFreeAll)(x,__FILE__,__LINE__) + #define mmBlockProcessList(x,y,z) MM_FUNC(BlockProcessList)(x,y,z,__FILE__,__LINE__) + #define mmBlockUseCount(x) MM_FUNC(BlockProcessList)(x,__FILE__,__LINE__) + #define mmBlockFreeCount(x) MM_FUNC(BlockProcessList)(x,__FILE__,__LINE__) +#endif + +/* +void mmBlockRelayByVolume( mmBlockHead *head, void *volumehead ); +void mmBlockRelayByZone( mmBlockHead *head, void *zonehead ); +*/ + + + +//// + + + +typedef struct +{ + mmBlockHead indexblock; + void *indextree; + intptr_t indexlimit; + mtSpin spinlock; +} mmIndexHead; + +void mmIndexInit( mmIndexHead *head, int indexesperblock ); +void mmIndexFreeAll( mmIndexHead *head ); +void mmIndexAdd( mmIndexHead *head, intptr_t index ); +intptr_t mmIndexGet( mmIndexHead *head ); +int mmIndexRemove( mmIndexHead *head, intptr_t index ); +size_t mmIndexCount( mmIndexHead *head ); + + + +//// + + + +typedef struct +{ + uintptr_t bitmask; + int bitshift; + uintptr_t countalign; + uintptr_t indexshift; + uintptr_t indexmask; + uintptr_t initmask; + size_t mapsize; + uintptr_t *map; + mtSpin spinlock; +} mmBitTableHead; + +void mmBitTableInit( mmBitTableHead *head, int bitsperentry, int chunksize, int initmask ); +void mmBitTableFreeAll( mmBitTableHead *head ); +void mmBitTableSet( mmBitTableHead *head, uintptr_t index, int flags, int editmask ); +uintptr_t mmBitTableGet( mmBitTableHead *head, uintptr_t index ); + + + +//// + + + +typedef struct +{ + size_t size; + size_t used; + void *next; +} mmGrowNode; + +typedef struct +{ + mmGrowNode *first; + size_t nodesize; + mtSpin spinlock; +} mmGrow; + +int MM_FUNC(GrowInit)( mmGrow *mgrow, size_t nodesize MM_PARAMS ); +void MM_FUNC(GrowFreeAll)( mmGrow *mgrow MM_PARAMS ); +void *MM_FUNC(GrowAlloc)( mmGrow *mgrow, size_t bytes MM_PARAMS ); +void MM_FUNC(GrowRewindLast)( mmGrow *mgrow, size_t rewind MM_PARAMS ); + +#if MM_DEBUG + #define mmGrowInit(x,y) MM_FUNC(GrowInit)(x,y,__FILE__,__LINE__) + #define mmGrowFreeAll(x) MM_FUNC(GrowFreeAll)(x,__FILE__,__LINE__) + #define mmGrowAlloc(x,y) MM_FUNC(GrowAlloc)(x,y,__FILE__,__LINE__) + #define mmGrowRewindLast(x) MM_FUNC(GrowRewindLast)(x,__FILE__,__LINE__) +#endif + + + +//// + + +#if 0 + +typedef struct +{ + void ***table; + intptr_t pagecount; + intptr_t pagesize; + intptr_t pagemask; + intptr_t pageshift; + mtSpin spinlock; +} mmDirectory; + +#define MM_DIR_ENTRY(dir,index) ( (dir)->table[ index >> (dir)->pageshift ][ index & (dir)->pagemask ] ) + +int MM_FUNC(DirInit)( mmDirectory *dir, intptr_t pageshift, intptr_t pagecount MM_PARAMS ); +void MM_FUNC(DirSize)( mmDirectory *dir, intptr_t size MM_PARAMS ); +void MM_FUNC(DirSet)( mmDirectory *dir, intptr_t index, void *entry MM_PARAMS ); +void *MM_FUNC(DirGet)( mmDirectory *dir, intptr_t index MM_PARAMS ); +void MM_FUNC(DirSetFast)( mmDirectory *dir, intptr_t index, void *entry MM_PARAMS ); +void *MM_FUNC(DirGetFast)( mmDirectory *dir, intptr_t index MM_PARAMS ); +void MM_FUNC(DirFree)( mmDirectory *dir MM_PARAMS ); + +#if MM_DEBUG + #define mmDirInit(x,y,z) MM_FUNC(DirInit)(x,y,z,__FILE__,__LINE__) + #define mmDirSize(x,y) MM_FUNC(DirSize)(x,y,__FILE__,__LINE__) + #define mmDirSet(x,y,z) MM_FUNC(DirSet)(x,y,z,__FILE__,__LINE__) + #define mmDirGet(x,y) MM_FUNC(DirGet)(x,y,__FILE__,__LINE__) + #define mmDirSetFast(x,y,z) MM_FUNC(DirSetFast)(x,y,z,__FILE__,__LINE__) + #define mmDirGetFast(x,y) MM_FUNC(DirGetFast)(x,y,__FILE__,__LINE__) + #define mmDirFree(x) MM_FUNC(DirFree)(x,__FILE__,__LINE__) +#endif + +#endif + + +//// + + + +void *MM_FUNC(AlignAlloc)( size_t bytes, intptr_t align MM_PARAMS ); +void MM_FUNC(AlignFree)( void *v MM_PARAMS ); +void *MM_FUNC(AlignGrow)( void *v, size_t bytes, size_t copybytes, intptr_t align MM_PARAMS ); +void *MM_FUNC(AlignRelayAlloc)( void *(*relayalloc)( void *head, size_t bytes MM_PARAMS ), void *relayvalue, size_t bytes, intptr_t align, size_t displacement MM_PARAMS ); +void MM_FUNC(AlignRelayFree)( void (*relayfree)( void *head, void *v, size_t bytes MM_PARAMS ), void *relayvalue, void *v, size_t bytes MM_PARAMS ); + +#if MM_DEBUG + #define mmAlignAlloc(x,y) MM_FUNC(AlignAlloc)(x,y,__FILE__,__LINE__) + #define mmAlignFree(x) MM_FUNC(AlignFree)(x,__FILE__,__LINE__) + #define mmAlignGrow(x) MM_FUNC(AlignGrow)(x,__FILE__,__LINE__) + #define mmAlignRelayAlloc(v,w,x,y,z) MM_FUNC(AlignRelayAlloc)(v,w,x,y,z,__FILE__,__LINE__) + #define mmAlignRelayFree(w,x,y,z) MM_FUNC(AlignRelayFree)(w,x,y,z,__FILE__,__LINE__) +#endif + + + +//// + + + +typedef struct +{ + size_t volumesize; + size_t volumeblocksize; + size_t minchunksize; + size_t volumechunksize; + size_t keepfreesize; + size_t totalfreesize; + size_t alignment; + void *freeroot; + void *volumelist; + void *(*relayalloc)( void *head, size_t bytes MM_PARAMS ); + void (*relayfree)( void *head, void *v, size_t bytes MM_PARAMS ); + void *relayvalue; + mtSpin spinlock; +} mmVolumeHead; + +void MM_FUNC(VolumeInit)( mmVolumeHead *head, size_t volumesize, size_t minchunksize, size_t keepfreesize, size_t alignment MM_PARAMS ); +void MM_FUNC(VolumeNodeInit)( mmVolumeHead *head, int nodeindex, size_t volumesize, size_t minchunksize, size_t keepfreesize, size_t alignment MM_PARAMS ); +void *MM_FUNC(VolumeAlloc)( mmVolumeHead *head, size_t bytes MM_PARAMS ); +void MM_FUNC(VolumeRelease)( mmVolumeHead *head, void *v MM_PARAMS ); +void MM_FUNC(VolumeFree)( mmVolumeHead *head, void *v MM_PARAMS ); +void MM_FUNC(VolumeShrink)( mmVolumeHead *head, void *v, size_t bytes MM_PARAMS ); +size_t MM_FUNC(VolumeGetAllocSize)( mmVolumeHead *head, void *v MM_PARAMS ); +void MM_FUNC(VolumeClean)( mmVolumeHead *head MM_PARAMS ); +void MM_FUNC(VolumeFreeAll)( mmVolumeHead *head MM_PARAMS ); +void *MM_FUNC(VolumeRealloc)( mmVolumeHead *head, void *v, size_t bytes MM_PARAMS ); + +#if MM_DEBUG + #define mmVolumeInit(w,x,y,z,a) MM_FUNC(VolumeInit)(w,x,y,z,a,__FILE__,__LINE__); + #define mmVolumeNodeInit(v,w,x,y,z,a) MM_FUNC(VolumeNodeInit)(v,w,x,y,z,a,__FILE__,__LINE__); + #define mmVolumeAlloc(x,y) MM_FUNC(VolumeAlloc)(x,y,__FILE__,__LINE__); + #define mmVolumeRelease(x,y) MM_FUNC(VolumeRelease)(x,y,__FILE__,__LINE__); + #define mmVolumeFree(x,y) MM_FUNC(VolumeFree)(x,y,__FILE__,__LINE__); + #define mmVolumeShrink(x,y,z) MM_FUNC(VolumeShrink)(x,y,z,__FILE__,__LINE__); + #define mmVolumeGetAllocSize(x,y) MM_FUNC(VolumeGetAllocSize)(x,y,__FILE__,__LINE__); + #define mmVolumeClean(x) MM_FUNC(VolumeClean)(x,__FILE__,__LINE__); + #define mmVolumeFreeAll(x) MM_FUNC(VolumeFreeAll)(x,__FILE__,__LINE__); + #define mmVolumeRealloc(x,y,z) MM_FUNC(VolumeRealloc)(x,y,z,__FILE__,__LINE__); +/* + #define mmVolumeAlloc MM_FUNC(VolumeAlloc) + #define mmVolumeRelease MM_FUNC(VolumeRelease) + #define mmVolumeFree MM_FUNC(VolumeFree) +*/ +#endif + +void mmVolumeDebugList( mmVolumeHead *volumehead ); +int mmVolumeDebugGetTreeDepth( mmVolumeHead *volumehead ); + +/* +void mmVolumeRelayByZone( mmVolumeHead *head, void *zonehead ); +*/ + + + +//// + + + +typedef struct +{ + void *address; + size_t pagesize; + size_t pagealignment; + size_t zonesize; + size_t alignment; + size_t chunkheadersize; + void *chunklist; + mtSpin spinlock; +} mmZoneHead; + +int MM_FUNC(ZoneInit)( mmZoneHead *head, size_t zonesize, intptr_t alignment MM_PARAMS ); +void *MM_FUNC(ZoneAlloc)( mmZoneHead *head, size_t bytes MM_PARAMS ); +void MM_FUNC(ZoneFree)( mmZoneHead *head, void *v MM_PARAMS ); +void MM_FUNC(ZoneFreeAll)( mmZoneHead *head MM_PARAMS ); + +#if MM_DEBUG + #define mmZoneInit(x,y,z) MM_FUNC(ZoneInit)(x,y,z,__FILE__,__LINE__); + #define mmZoneAlloc(x,y) MM_FUNC(ZoneAlloc)(x,y,__FILE__,__LINE__); + #define mmZoneFree(x,y) MM_FUNC(ZoneFree)(x,y,__FILE__,__LINE__); + #define mmZoneFreeAll(x) MM_FUNC(ZoneFreeAll)(x,__FILE__,__LINE__); +#endif + + + +//// + + + +void *mmAlloc( void *unused, size_t bytes MM_PARAMS ); +void *mmRealloc( void *unused, void *v, size_t bytes MM_PARAMS ); +void mmFree( void *unused, void *v, size_t bytes MM_PARAMS ); + +void *mmDebugAlloc( size_t bytes, const char *file, int line ); +void *mmDebugRealloc( void *v, size_t bytes, const char *file, int line ); +void mmDebugFree( void *v, const char *file, int line ); +#define mmDebugAlloc(x) mmDebugAlloc(x,__FILE__,__LINE__) +#define mmDebugFree(x) mmDebugFree(x,__FILE__,__LINE__) +#define mmDebugRealloc(x,y) mmDebugRealloc(x,y,__FILE__,__LINE__) + +void mmListUses( const char *file, int line ); +#define mmListUses() mmListUses(__FILE__,__LINE__); + + +//// + + +#if MM_DEBUG + #define malloc(x) mmAlloc(0,(x),__FILE__,__LINE__) + #define realloc(x,y) mmRealloc(0,(x),(y),__FILE__,__LINE__) + #define free(x) mmFree(0,(x),0,__FILE__,__LINE__) +#elif MM_ALLOC_CHECK + +static inline void *mmAllocCheck( size_t size, const char *file, int line ) +{ + void *p; + p = malloc( size ); +#if MM_WINDOWS + if( !( p ) ) + fprintf( stderr, "WARNING : Denied memory allocation ( %ld bytes ) at %s:%d\n", (long)size, file, line ); +#else + if( !( p ) ) + fprintf( stderr, "WARNING : Denied memory allocation ( %lld bytes ) at %s:%d\n", (long long)size, file, line ); +#endif + return p; +} + +static inline void *mmReallocCheck( void *p, size_t size, const char *file, int line ) +{ + p = realloc( p, size ); +#if MM_WINDOWS + if( !( p ) ) + fprintf( stderr, "WARNING : Denied memory allocation ( %ld bytes ) at %s:%d\n", (long)size, file, line ); +#else + if( !( p ) ) + fprintf( stderr, "WARNING : Denied memory allocation ( %lld bytes ) at %s:%d\n", (long long)size, file, line ); +#endif + return p; +} + + #define malloc(x) mmAllocCheck((x),__FILE__,__LINE__) + #define realloc(x,y) mmReallocCheck((x),(y),__FILE__,__LINE__) +#endif + + + + +//// + + + +static inline uint64_t mmGetMillisecondsTime() +{ + struct timeval lntime; + gettimeofday( &lntime, 0 ); + return ( (uint64_t)lntime.tv_sec * 1000 ) + ( (uint64_t)lntime.tv_usec / 1000 ); +} + + +static inline uint64_t mmGetMicrosecondsTime() +{ + struct timeval lntime; + gettimeofday( &lntime, 0 ); + return ( (uint64_t)lntime.tv_sec * 1000000 ) + (uint64_t)lntime.tv_usec; +} + + +static inline uint64_t mmGetNanosecondsTime() +{ + struct timeval lntime; + gettimeofday( &lntime, 0 ); + return ( (uint64_t)lntime.tv_sec * 1000000000 ) + ( (uint64_t)lntime.tv_usec * 1000 ); +} + + + +//// + + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/ecere/src/gfx/newFonts/cc/mmatomic.h b/ecere/src/gfx/newFonts/cc/mmatomic.h new file mode 100644 index 0000000000..a13a885a99 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mmatomic.h @@ -0,0 +1,2474 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +/** + * @file + * + * Atomic memory operations. + */ + +#include "mm.h" + +#if ( defined(CPUCONF_ARCH_IA32) || defined(CPUCONF_ARCH_AMD64) ) && defined(__GNUC__) && !defined(MM_ATOMIC_SUPPORT) && !defined(__UWP__) + + +#define MM_ATOMIC_SUPPORT + + +#ifdef CPUCONF_ARCH_AMD64 + #define MM_ATOMIC_64_BITS_SUPPORT +#endif + + +typedef struct { volatile int8_t value; } mmAtomic8; +typedef struct { volatile int16_t value; } mmAtomic16; +typedef struct { volatile int32_t value; } mmAtomic32; +#ifdef MM_ATOMIC_64_BITS_SUPPORT +typedef struct { volatile int64_t value; } mmAtomic64; +#endif + + +//// + + +/* + +Architecture Memory Ordering + +Memory model for x86 and amd64 +--- Aligned stores can not be partially seen by loads +--- Loads can NOT be reordered after loads +--- Loads can NOT be reordered after stores +--- Stores can NOT be reordered after stores +-X- Stores CAN be reordered after loads +--- Atomic instructions are NOT reordered with loads +--- Atomic instructions are NOT reordered with stores +--- Dependent loads can NOT be reordered + +*/ + + +/* Memory model configuration for x86/amd64 */ +// #define CPUCONF_LOAD_REODERING_AFTER_LOAD +// #define CPUCONF_LOAD_REODERING_AFTER_STORE +#define CPUCONF_STORE_REODERING_AFTER_LOAD +// #define CPUCONF_STORE_REODERING_AFTER_STORE +// #define CPUCONF_ATOMIC_REODERING_WITH_LOAD +// #define CPUCONF_ATOMIC_REODERING_WITH_STORE +// #define CPUCONF_DEPENDENT_REODERING + + +//// + + +/* Do nothing, prevent compiler reordering */ +static inline void mmBarrier() +{ + __asm__ __volatile__( "":::"memory" ); + return; +} + +/* All previous loads must complete before future loads */ +static inline void mmReadBarrier() +{ +#ifdef CPUCONF_CAP_SSE2 + __asm__ __volatile__( "lfence":::"memory" ); +#else + __asm__ __volatile__( "lock ; addl $0,(%%esp)":::"memory" ); +#endif + return; +} + +/* All previous stores must complete before future stores */ +static inline void mmWriteBarrier() +{ + /* x86 and AMD64 never reorder stores : the sfence instruction is useless unless chatting with devices on MMIO */ + __asm__ __volatile__( "":::"memory" ); + return; +} + +/* All previous loads/stores must complete before future loads/stores */ +static inline void mmFullBarrier() +{ +#ifdef CPUCONF_CAP_SSE2 + __asm__ __volatile__( "mfence":::"memory" ); +#else + __asm__ __volatile__( "lock ; addl $0,(%%esp)":::"memory" ); +#endif + return; +} + + +//// + + +/* Direct access to the atomic variables, for use when the caller knows no atomicity is needed */ +#define MM_ATOMIC_ACCESS_8(v) ((v)->value) +#define MM_ATOMIC_ACCESS_16(v) ((v)->value) +#define MM_ATOMIC_ACCESS_32(v) ((v)->value) +#ifdef MM_ATOMIC_64_BITS_SUPPORT + #define MM_ATOMIC_ACCESS_64(v) ((v)->value) +#endif + + +//// + + +/* +mmAtomicRead*() +Atomically read the value +*/ +static inline int8_t mmAtomicRead8( mmAtomic8 *v ) +{ + mmBarrier(); + return v->value; +} + +static inline int16_t mmAtomicRead16( mmAtomic16 *v ) +{ + mmBarrier(); + return v->value; +} + +static inline int32_t mmAtomicRead32( mmAtomic32 *v ) +{ + mmBarrier(); + return v->value; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicRead64( mmAtomic64 *v ) +{ + mmBarrier(); + return v->value; +} +#endif + + +//// + + +/* +mmAtomicWrite*() +Atomically write the value +*/ +static inline void mmAtomicWrite8( mmAtomic8 *v, int8_t i ) +{ + mmBarrier(); + v->value = i; + return; +} + +static inline void mmAtomicWrite16( mmAtomic16 *v, int16_t i ) +{ + mmBarrier(); + v->value = i; + return; +} + +static inline void mmAtomicWrite32( mmAtomic32 *v, int32_t i ) +{ + mmBarrier(); + v->value = i; + return; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicWrite64( mmAtomic64 *v, int64_t i ) +{ + mmBarrier(); + v->value = i; + return; +} +#endif + + +//// + + +/* +mmAtomicBarrierWrite*() +Atomically write the value and act as a full memory barrier +*/ +static inline void mmAtomicBarrierWrite8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; xchgb %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return; +} + +static inline void mmAtomicBarrierWrite16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; xchgw %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return; +} + +static inline void mmAtomicBarrierWrite32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; xchgl %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicBarrierWrite64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; xchgq %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return; +} +#endif + + +//// + + +static inline void mmAtomicAdd8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; addb %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicAdd16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; addw %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicAdd32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; addl %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicAdd64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; addq %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline void mmAtomicSub8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; subb %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicSub16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; subw %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicSub32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; subl %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicSub64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; subq %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline int mmAtomicAddTestZero8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addb %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicAddTestZero16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addw %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicAddTestZero32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addl %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicAddTestZero64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addq %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + +//// + + +static inline int mmAtomicSubTestZero8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subb %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicSubTestZero16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subw %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicSubTestZero32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subl %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicSubTestZero64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subq %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + +//// + + +static inline void mmAtomicInc8( mmAtomic8 *v ) +{ + __asm__ __volatile__( + "lock ; incb %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} + +static inline void mmAtomicInc16( mmAtomic16 *v ) +{ + __asm__ __volatile__( + "lock ; incw %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} + +static inline void mmAtomicInc32( mmAtomic32 *v ) +{ + __asm__ __volatile__( + "lock ; incl %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicInc64( mmAtomic64 *v ) +{ + __asm__ __volatile__( + "lock ; incq %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline void mmAtomicDec8( mmAtomic8 *v ) +{ + __asm__ __volatile__( + "lock ; decl %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} + +static inline void mmAtomicDec16( mmAtomic16 *v ) +{ + __asm__ __volatile__( + "lock ; decl %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} + +static inline void mmAtomicDec32( mmAtomic32 *v ) +{ + __asm__ __volatile__( + "lock ; decl %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicDec64( mmAtomic64 *v ) +{ + __asm__ __volatile__( + "lock ; decq %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline int mmAtomicIncTestZero8( mmAtomic8 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; incb %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} + +static inline int mmAtomicIncTestZero16( mmAtomic16 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; incw %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} + +static inline int mmAtomicIncTestZero32( mmAtomic32 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; incl %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicIncTestZero64( mmAtomic64 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; incq %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} +#endif + + +//// + + +static inline int mmAtomicDecTestZero8( mmAtomic8 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; decb %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} + +static inline int mmAtomicDecTestZero16( mmAtomic16 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; decw %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} + +static inline int mmAtomicDecTestZero32( mmAtomic32 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; decl %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicDecTestZero64( mmAtomic64 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; decq %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} +#endif + + +//// + + +static inline int mmAtomicAddTestNegative8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addb %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicAddTestNegative16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addw %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicAddTestNegative32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addl %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicAddTestNegative64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addq %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + +//// + + +static inline int mmAtomicSubTestNegative8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subb %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicSubTestNegative16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subw %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicSubTestNegative32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subl %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicSubTestNegative64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subq %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + + +//////////////// + + + +static inline void mmAtomicAnd8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; andb %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicAnd16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; andw %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicAnd32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; andl %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicAnd64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; andq %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline int mmAtomicAndTestZero8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; andb %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicAndTestZero16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; andw %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicAndTestZero32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; andl %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicAndTestZero64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; andq %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + +//// + + +static inline void mmAtomicOr8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; orb %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicOr16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; orw %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicOr32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; orl %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicOr64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; orq %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline int mmAtomicOrTestZero8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; orb %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicOrTestZero16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; orw %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicOrTestZero32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; orl %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicOrTestZero64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; orq %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + +//// + + +static inline void mmAtomicXor8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; xorb %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicXor16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; xorw %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicXor32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; xorl %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicXor64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; xorq %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline int mmAtomicXorTestZero8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; xorb %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicXorTestZero16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; xorw %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicXorTestZero32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; xorl %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicXorTestZero64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; xorq %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + + +//////////////// + + + +static inline int8_t mmAtomicXchg8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; xchgb %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return i; +} + +static inline int16_t mmAtomicXchg16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; xchgw %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return i; +} + +static inline int32_t mmAtomicXchg32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; xchgl %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return i; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicXchg64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; xchgq %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return i; +} +#endif + + +//// + + +static inline int8_t mmAtomicCmpXchg8( mmAtomic8 *v, int8_t old, int8_t n ) +{ + int8_t prev; + __asm__ __volatile__( + "lock ; cmpxchgb %1,%2" + :"=a"(prev) + :"r"(n), "m"(v->value), "a"(old) :"memory" ); + return prev; +} + +static inline int16_t mmAtomicCmpXchg16( mmAtomic16 *v, int16_t old, int16_t n ) +{ + int16_t prev; + __asm__ __volatile__( + "lock ; cmpxchgw %1,%2" + :"=a"(prev) + :"r"(n), "m"(v->value), "a"(old) :"memory" ); + return prev; +} + +static inline int32_t mmAtomicCmpXchg32( mmAtomic32 *v, int32_t old, int32_t n ) +{ + int32_t prev; + __asm__ __volatile__( + "lock ; cmpxchgl %1,%2" + :"=a"(prev) + :"r"(n), "m"(v->value), "a"(old) :"memory" ); + return prev; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicCmpXchg64( mmAtomic64 *v, int64_t old, int64_t n ) +{ + int64_t prev; + __asm__ __volatile__( + "lock ; cmpxchgq %1,%2" + :"=a"(prev) + :"r"(n), "m"(v->value), "a"(old) :"memory" ); + return prev; +} +#endif + + + +//////////////// + + + +static inline void mmAtomicPause() +{ + __asm__ __volatile__( + "rep ; nop" + : + ::"memory" ); + return; +} + + +static inline void mmAtomicSpinWaitEq8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpb %1,%0\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} + + +static inline void mmAtomicSpinWaitEq16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpw %1,%0\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} + + +static inline void mmAtomicSpinWaitEq32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpl %1,%0\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} + + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicSpinWaitEq64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpq %1,%0\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} +#endif + + +static inline int32_t mmAtomicSpinWaitEq8Count( mmAtomic8 *v, int8_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpb %2,%1\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} + + +static inline int32_t mmAtomicSpinWaitEq16Count( mmAtomic16 *v, int16_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpw %2,%1\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} + + +static inline int32_t mmAtomicSpinWaitEq32Count( mmAtomic32 *v, int32_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpl %2,%1\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} + + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int32_t mmAtomicSpinWaitEq64Count( mmAtomic64 *v, int64_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpq %2,%1\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} +#endif + + +static inline void mmAtomicSpinWaitNeq8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpb %1,%0\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} + + +static inline void mmAtomicSpinWaitNeq16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpw %1,%0\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} + + +static inline void mmAtomicSpinWaitNeq32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpl %1,%0\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} + + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicSpinWaitNeq64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpq %1,%0\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} +#endif + + +static inline int32_t mmAtomicSpinWaitNeq8Count( mmAtomic8 *v, int8_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpb %2,%1\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} + + +static inline int32_t mmAtomicSpinWaitNeq16Count( mmAtomic16 *v, int16_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpw %2,%1\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} + + +static inline int32_t mmAtomicSpinWaitNeq32Count( mmAtomic32 *v, int32_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpl %2,%1\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} + + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int32_t mmAtomicSpinWaitNeq64Count( mmAtomic64 *v, int64_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpq %2,%1\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} +#endif + + +//// + + +static inline void mmAtomicSpin8( mmAtomic8 *v, int8_t old, int8_t n ) +{ + for( ; mmAtomicCmpXchg8( v, old, n ) != old ; ) + { + for( ; mmAtomicRead8( v ) != old ; ) + mmAtomicPause(); + } + return; +} + +static inline void mmAtomicSpin16( mmAtomic16 *v, int16_t old, int16_t n ) +{ + for( ; mmAtomicCmpXchg16( v, old, n ) != old ; ) + { + for( ; mmAtomicRead16( v ) != old ; ) + mmAtomicPause(); + } + return; +} + +static inline void mmAtomicSpin32( mmAtomic32 *v, int32_t old, int32_t n ) +{ + for( ; mmAtomicCmpXchg32( v, old, n ) != old ; ) + { + for( ; mmAtomicRead32( v ) != old ; ) + mmAtomicPause(); + } + return; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicSpin64( mmAtomic64 *v, int64_t old, int64_t n ) +{ + for( ; mmAtomicCmpXchg64( v, old, n ) != old ; ) + { + for( ; mmAtomicRead64( v ) != old ; ) + mmAtomicPause(); + } + return; +} +#endif + + +static inline int mmAtomicTrySpin8( mmAtomic8 *v, int8_t old, int8_t n, int spincount ) +{ + for( ; mmAtomicCmpXchg8( v, old, n ) != old ; ) + { + for( ; mmAtomicRead8( v ) != old ; ) + { + if( !( --spincount ) ) + return 0; + mmAtomicPause(); + } + } + return 1; +} + +static inline int mmAtomicTrySpin16( mmAtomic16 *v, int16_t old, int16_t n, int spincount ) +{ + for( ; mmAtomicCmpXchg16( v, old, n ) != old ; ) + { + for( ; mmAtomicRead16( v ) != old ; ) + { + if( !( --spincount ) ) + return 0; + mmAtomicPause(); + } + } + return 1; +} + +static inline int mmAtomicTrySpin32( mmAtomic32 *v, int32_t old, int32_t n, int spincount ) +{ + for( ; mmAtomicCmpXchg32( v, old, n ) != old ; ) + { + for( ; mmAtomicRead32( v ) != old ; ) + { + if( !( --spincount ) ) + return 0; + mmAtomicPause(); + } + } + return 1; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicTrySpin64( mmAtomic64 *v, int64_t old, int64_t n, int spincount ) +{ + for( ; mmAtomicCmpXchg64( v, old, n ) != old ; ) + { + for( ; mmAtomicRead64( v ) != old ; ) + { + if( !( --spincount ) ) + return 0; + mmAtomicPause(); + } + } + return 1; +} +#endif + + +//////////////// + + +static inline int8_t mmAtomicAddRead8( mmAtomic8 *v, int8_t add ) +{ + int8_t i; + do + { + i = mmAtomicRead8( v ); + } while( mmAtomicCmpXchg8( v, i, (int8_t)(i + add) ) != i ); + return (int8_t)(i + add); +} + +static inline int16_t mmAtomicAddRead16( mmAtomic16 *v, int16_t add ) +{ + int16_t i; + do + { + i = mmAtomicRead16( v ); + } while( mmAtomicCmpXchg16( v, i, (int16_t)(i + add) ) != i ); + return (int16_t)(i + add); +} + +static inline int32_t mmAtomicAddRead32( mmAtomic32 *v, int32_t add ) +{ + int32_t i; + do + { + i = mmAtomicRead32( v ); + } while( mmAtomicCmpXchg32( v, i, i + add ) != i ); + return i + add; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicAddRead64( mmAtomic64 *v, int64_t add ) +{ + int64_t i; + do + { + i = mmAtomicRead64( v ); + } while( mmAtomicCmpXchg64( v, i, i + add ) != i ); + return i + add; +} +#endif + + +//// + + +static inline int8_t mmAtomicReadAdd8( mmAtomic8 *v, int8_t add ) +{ + int8_t i; + do + { + i = mmAtomicRead8( v ); + } while( mmAtomicCmpXchg8( v, i, (int8_t)(i + add) ) != i ); + return i; +} + +static inline int16_t mmAtomicReadAdd16( mmAtomic16 *v, int16_t add ) +{ + int16_t i; + do + { + i = mmAtomicRead16( v ); + } while( mmAtomicCmpXchg16( v, i, (int16_t)(i + add) ) != i ); + return i; +} + +static inline int32_t mmAtomicReadAdd32( mmAtomic32 *v, int32_t add ) +{ + int32_t i; + do + { + i = mmAtomicRead32( v ); + } while( mmAtomicCmpXchg32( v, i, i + add ) != i ); + return i; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicReadAdd64( mmAtomic64 *v, int64_t add ) +{ + int64_t i; + do + { + i = mmAtomicRead64( v ); + } while( mmAtomicCmpXchg64( v, i, i + add ) != i ); + return i; +} +#endif + + +//// + + +static inline int8_t mmAtomicReadAnd8( mmAtomic8 *v, int8_t mask ) +{ + int8_t i, j; + do + { + i = mmAtomicRead8( v ); + j = i & mask; + } while( mmAtomicCmpXchg8( v, i, j ) != i ); + return i; +} + +static inline int16_t mmAtomicReadAnd16( mmAtomic16 *v, int16_t mask ) +{ + int16_t i, j; + do + { + i = mmAtomicRead16( v ); + j = i & mask; + } while( mmAtomicCmpXchg16( v, i, j ) != i ); + return i; +} + +static inline int32_t mmAtomicReadAnd32( mmAtomic32 *v, int32_t mask ) +{ + int32_t i, j; + do + { + i = mmAtomicRead32( v ); + j = i & mask; + } while( mmAtomicCmpXchg32( v, i, j ) != i ); + return i; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicReadAnd64( mmAtomic64 *v, int64_t mask ) +{ + int64_t i, j; + do + { + i = mmAtomicRead64( v ); + j = i & mask; + } while( mmAtomicCmpXchg64( v, i, j ) != i ); + return i; +} +#endif + + +//// + + +static inline int8_t mmAtomicReadOr8( mmAtomic8 *v, int8_t mask ) +{ + int8_t i, j; + do + { + i = mmAtomicRead8( v ); + j = i | mask; + } while( mmAtomicCmpXchg8( v, i, j ) != i ); + return i; +} + +static inline int16_t mmAtomicReadOr16( mmAtomic16 *v, int16_t mask ) +{ + int16_t i, j; + do + { + i = mmAtomicRead16( v ); + j = i | mask; + } while( mmAtomicCmpXchg16( v, i, j ) != i ); + return i; +} + +static inline int32_t mmAtomicReadOr32( mmAtomic32 *v, int32_t mask ) +{ + int32_t i, j; + do + { + i = mmAtomicRead32( v ); + j = i | mask; + } while( mmAtomicCmpXchg32( v, i, j ) != i ); + return i; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicReadOr64( mmAtomic64 *v, int64_t mask ) +{ + int64_t i, j; + do + { + i = mmAtomicRead64( v ); + j = i | mask; + } while( mmAtomicCmpXchg64( v, i, j ) != i ); + return i; +} +#endif + + +//// + + +static inline int8_t mmAtomicReadIncLoop8( mmAtomic8 *v, int8_t max ) +{ + int8_t i, j; + do + { + i = mmAtomicRead8( v ); + j = (int8_t)(i + 1); + if( j >= max ) + j = 0; + } while( mmAtomicCmpXchg8( v, i, j ) != i ); + return i; +} + +static inline int16_t mmAtomicReadIncLoop16( mmAtomic16 *v, int16_t max ) +{ + int16_t i, j; + do + { + i = mmAtomicRead16( v ); + j = (int8_t)(i + 1); + if( j >= max ) + j = 0; + } while( mmAtomicCmpXchg16( v, i, j ) != i ); + return i; +} + +static inline int32_t mmAtomicReadIncLoop32( mmAtomic32 *v, int32_t max ) +{ + int32_t i, j; + do + { + i = mmAtomicRead32( v ); + j = i + 1; + if( j >= max ) + j = 0; + } while( mmAtomicCmpXchg32( v, i, j ) != i ); + return i; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicReadIncLoop64( mmAtomic64 *v, int64_t max ) +{ + int64_t i, j; + do + { + i = mmAtomicRead64( v ); + j = i + 1; + if( j >= max ) + j = 0; + } while( mmAtomicCmpXchg64( v, i, j ) != i ); + return i; +} +#endif + + +//// + + +static inline int8_t mmAtomicReadAddLoop8( mmAtomic8 *v, int8_t add, int8_t base, int8_t max ) +{ + int8_t i, j; + do + { + i = mmAtomicRead8( v ); + j = (int8_t)(i + add); + if( j >= max ) + j = base; + } while( mmAtomicCmpXchg8( v, i, j ) != i ); + return i; +} + +static inline int16_t mmAtomicReadAddLoop16( mmAtomic16 *v, int16_t add, int16_t base, int16_t max ) +{ + int16_t i, j; + do + { + i = mmAtomicRead16( v ); + j = (int16_t)(i + add); + if( j >= max ) + j = base; + } while( mmAtomicCmpXchg16( v, i, j ) != i ); + return i; +} + +static inline int32_t mmAtomicReadAddLoop32( mmAtomic32 *v, int32_t add, int32_t base, int32_t max ) +{ + int32_t i, j; + do + { + i = mmAtomicRead32( v ); + j = i + add; + if( j >= max ) + j = base; + } while( mmAtomicCmpXchg32( v, i, j ) != i ); + return i; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicReadAddLoop64( mmAtomic64 *v, int64_t add, int64_t base, int64_t max ) +{ + int64_t i, j; + do + { + i = mmAtomicRead64( v ); + j = i + add; + if( j >= max ) + j = base; + } while( mmAtomicCmpXchg64( v, i, j ) != i ); + return i; +} +#endif + + + +//////////////// + + + + +#define mmAtomicCmpReplace8(v,old,new) (mmAtomicCmpXchg8(v,old,new)==(old)) +#define mmAtomicCmpReplace16(v,old,new) (mmAtomicCmpXchg16(v,old,new)==(old)) +#define mmAtomicCmpReplace32(v,old,new) (mmAtomicCmpXchg32(v,old,new)==(old)) +#define mmAtomicCmpReplace64(v,old,new) (mmAtomicCmpXchg64(v,old,new)==(old)) + + +#if CPUCONF_POINTER_BITS == 64 + #define mmAtomicP mmAtomic64 + #define MM_ATOMIC_ACCESS_P(v) (void *)MM_ATOMIC_ACCESS_64(v) + #define mmAtomicReadP(v) (void *)mmAtomicRead64(v) + #define mmAtomicWriteP(v,i) mmAtomicWrite64(v,(int64_t)i) + #define mmAtomicAddP(v,i) mmAtomicAdd64(v,(int64_t)i) + #define mmAtomicSubP(v,i) mmAtomicSub64(v,(int64_t)i) + #define mmAtomicAddTestZeroP(v,i) mmAtomicAddTestZero64(v,(int64_t)i) + #define mmAtomicSubTestZeroP(v,i) mmAtomicSubTestZero64(v,(int64_t)i) + #define mmAtomicIncP(v) mmAtomicInc64(v) + #define mmAtomicDecP(v) mmAtomicDec64(v) + #define mmAtomicIncTestZeroP(v) mmAtomicIncTestZero64(v) + #define mmAtomicDecTestZeroP(v) mmAtomicDecTestZero64(v) + #define mmAtomicAddTestNegativeP(v,i) mmAtomicAddTestNegative64(v,(int64_t)i) + #define mmAtomicSubTestNegativeP(v,i) mmAtomicSubTestNegative64(v,(int64_t)i) + #define mmAtomicAndP(v,i) mmAtomicAnd64(v,(int64_t)i) + #define mmAtomicAndTestZeroP(v,i) mmAtomicAndTestZero64(v,(int64_t)i) + #define mmAtomicOrP(v,i) mmAtomicOr64(v,(int64_t)i) + #define mmAtomicOrTestZeroP(v,i) mmAtomicOrTestZero64(v,(int64_t)i) + #define mmAtomicXorP(v,i) mmAtomicXor64(v,(int64_t)i) + #define mmAtomicXorTestZeroP(v,i) mmAtomicXorTestZero64(v,(int64_t)i) + #define mmAtomicXchgP(v,i) (void *)mmAtomicXchg64(v,(int64_t)i) + #define mmAtomicCmpXchgP(v,i,j) (void *)mmAtomicCmpXchg64(v,(int64_t)i,(int64_t)j) + #define mmAtomicCmpReplaceP(v,i,j) mmAtomicCmpReplace64(v,(int64_t)i,(int64_t)j) + #define mmAtomicSpinP(v,i,j) (void *)mmAtomicSpin64(v,(int64_t)i,(int64_t)j) + #define mmAtomicAddReadP(v,i) (void *)mmAtomicAddRead64(v,(int64_t)i) +#elif CPUCONF_POINTER_BITS == 32 + #define mmAtomicP mmAtomic32 + #define MM_ATOMIC_ACCESS_P(v) (void *)MM_ATOMIC_ACCESS_32(v) + #define mmAtomicReadP(v) (void *)mmAtomicRead32(v) + #define mmAtomicWriteP(v,i) mmAtomicWrite32(v,(int32_t)i) + #define mmAtomicAddP(v,i) mmAtomicAdd32(v,(int32_t)i) + #define mmAtomicSubP(v,i) mmAtomicSub32(v,(int32_t)i) + #define mmAtomicAddTestZeroP(v,i) mmAtomicAddTestZero32(v,(int32_t)i) + #define mmAtomicSubTestZeroP(v,i) mmAtomicSubTestZero32(v,(int32_t)i) + #define mmAtomicIncP(v) mmAtomicInc32(v) + #define mmAtomicDecP(v) mmAtomicDec32(v) + #define mmAtomicIncTestZeroP(v) mmAtomicIncTestZero32(v) + #define mmAtomicDecTestZeroP(v) mmAtomicDecTestZero32(v) + #define mmAtomicAddTestNegativeP(v,i) mmAtomicAddTestNegative32(v,(int32_t)i) + #define mmAtomicSubTestNegativeP(v,i) mmAtomicSubTestNegative32(v,(int32_t)i) + #define mmAtomicAndP(v,i) mmAtomicAnd32(v,(int32_t)i) + #define mmAtomicAndTestZeroP(v,i) mmAtomicAndTestZero32(v,(int32_t)i) + #define mmAtomicOrP(v,i) mmAtomicOr32(v,(int32_t)i) + #define mmAtomicOrTestZeroP(v,i) mmAtomicOrTestZero32(v,(int32_t)i) + #define mmAtomicXorP(v,i) mmAtomicXor32(v,(int32_t)i) + #define mmAtomicXorTestZeroP(v,i) mmAtomicXorTestZero32(v,(int32_t)i) + #define mmAtomicXchgP(v,i) (void *)mmAtomicXchg32(v,(int32_t)i) + #define mmAtomicCmpXchgP(v,i,j) (void *)mmAtomicCmpXchg32(v,(int32_t)i,(int32_t)j) + #define mmAtomicCmpReplaceP(v,i,j) mmAtomicCmpReplace32(v,(int32_t)i,(int32_t)j) + #define mmAtomicSpinP(v,i,j) (void *)mmAtomicSpin32(v,(int32_t)i,(int32_t)j) + #define mmAtomicAddReadP(v,i) (void *)mmAtomicAddRead32(v,(int32_t)i) +#else + #error CPUCONF_POINTER_BITS undefined +#endif + +#ifdef MM_ATOMIC_64_BITS_SUPPORT + #define intlarge int64_t + #define uintlarge uint64_t + #define mmAtomicL mmAtomic64 + #define MM_ATOMIC_ACCESS_L(v) MM_ATOMIC_ACCESS_64(v) + #define mmAtomicReadL(v) mmAtomicRead64(v) + #define mmAtomicWriteL(v,i) mmAtomicWrite64(v,i) + #define mmAtomicAddL(v,i) mmAtomicAdd64(v,i) + #define mmAtomicSubL(v,i) mmAtomicSub64(v,i) + #define mmAtomicAddTestZeroL(v,i) mmAtomicAddTestZero64(v,i) + #define mmAtomicSubTestZeroL(v,i) mmAtomicSubTestZero64(v,i) + #define mmAtomicIncL(v) mmAtomicInc64(v) + #define mmAtomicDecL(v) mmAtomicDec64(v) + #define mmAtomicIncTestZeroL(v) mmAtomicIncTestZero64(v) + #define mmAtomicDecTestZeroL(v) mmAtomicDecTestZero64(v) + #define mmAtomicAddTestNegativeL(v,i) mmAtomicAddTestNegative64(v,i) + #define mmAtomicSubTestNegativeL(v,i) mmAtomicSubTestNegative64(v,i) + #define mmAtomicAndL(v,i) mmAtomicAnd64(v,i) + #define mmAtomicAndTestZeroL(v,i) mmAtomicAndTestZero64(v,i) + #define mmAtomicOrL(v,i) mmAtomicOr64(v,i) + #define mmAtomicOrTestZeroL(v,i) mmAtomicOrTestZero64(v,i) + #define mmAtomicXorL(v,i) mmAtomicXor64(v,i) + #define mmAtomicXorTestZeroL(v,i) mmAtomicXorTestZero64(v,i) + #define mmAtomicXchgL(v,i) mmAtomicXchg64(v,i) + #define mmAtomicCmpXchgL(v,i,j) mmAtomicCmpXchg64(v,i,j) + #define mmAtomicCmpReplaceL(v,i,j) mmAtomicCmpReplace64(v,i,j) + #define mmAtomicSpinL(v,i,j) mmAtomicSpin64(v,i,j) + #define mmAtomicAddReadL(v,i) mmAtomicAddRead64(v,(int64_t)i) +#else + #define intlarge int32_t + #define uintlarge uint32_t + #define mmAtomicL mmAtomic32 + #define MM_ATOMIC_ACCESS_L(v) MM_ATOMIC_ACCESS_32(v) + #define mmAtomicReadL(v) mmAtomicRead32(v) + #define mmAtomicWriteL(v,i) mmAtomicWrite32(v,i) + #define mmAtomicAddL(v,i) mmAtomicAdd32(v,i) + #define mmAtomicSubL(v,i) mmAtomicSub32(v,i) + #define mmAtomicAddTestZeroL(v,i) mmAtomicAddTestZero32(v,i) + #define mmAtomicSubTestZeroL(v,i) mmAtomicSubTestZero32(v,i) + #define mmAtomicIncL(v) mmAtomicInc32(v) + #define mmAtomicDecL(v) mmAtomicDec32(v) + #define mmAtomicIncTestZeroL(v) mmAtomicIncTestZero32(v) + #define mmAtomicDecTestZeroL(v) mmAtomicDecTestZero32(v) + #define mmAtomicAddTestNegativeL(v,i) mmAtomicAddTestNegative32(v,i) + #define mmAtomicSubTestNegativeL(v,i) mmAtomicSubTestNegative32(v,i) + #define mmAtomicAndL(v,i) mmAtomicAnd32(v,i) + #define mmAtomicAndTestZeroL(v,i) mmAtomicAndTestZero32(v,i) + #define mmAtomicOrL(v,i) mmAtomicOr32(v,i) + #define mmAtomicOrTestZeroL(v,i) mmAtomicOrTestZero32(v,i) + #define mmAtomicXorL(v,i) mmAtomicXor32(v,i) + #define mmAtomicXorTestZeroL(v,i) mmAtomicXorTestZero32(v,i) + #define mmAtomicXchgL(v,i) mmAtomicXchg32(v,i) + #define mmAtomicCmpXchgL(v,i,j) mmAtomicCmpXchg32(v,i,j) + #define mmAtomicCmpReplaceL(v,i,j) mmAtomicCmpReplace32(v,i,j) + #define mmAtomicSpinL(v,i,j) mmAtomicSpin32(v,i,j) + #define mmAtomicAddReadL(v,i) mmAtomicAddRead32(v,(int32_t)i) +#endif + + + +//////////////// + + + +typedef struct { mmAtomic8 v; } mmAtomicLock8; + +#define MM_ATOMIC_LOCK8_WRITE (-((int8_t)0x7f)) + +static inline void mmAtomicLockInit8( mmAtomicLock8 *v ) +{ + mmAtomicWrite8( &v->v, 0 ); + return; +} + +static inline int mmAtomicLockAttemptRead8( mmAtomicLock8 *v ) +{ + if( mmAtomicAddTestNegative8( &v->v, 1 ) ) + { + mmAtomicAdd8( &v->v, -1 ); + return 0; + } + return 1; +} + +static inline int mmAtomicLockAttemptWrite8( mmAtomicLock8 *v ) +{ + if( mmAtomicCmpXchg8( &v->v, 0, MM_ATOMIC_LOCK8_WRITE ) ) + return 0; + return 1; +} + +static inline void mmAtomicLockSpinRead8( mmAtomicLock8 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead8( &v->v ) < 0 ; ) + mmAtomicPause(); + if( !( mmAtomicAddTestNegative8( &v->v, 1 ) ) ) + break; + mmAtomicAdd8( &v->v, -1 ); + } + return; +} + +static inline void mmAtomicLockSpinWrite8( mmAtomicLock8 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead8( &v->v ) ; ) + mmAtomicPause(); + if( !( mmAtomicCmpXchg8( &v->v, 0, MM_ATOMIC_LOCK8_WRITE ) ) ) + break; + } + return; +} + +static inline int mmAtomicLockTryRead8( mmAtomicLock8 *v, int spincount ) +{ + do + { + if( mmAtomicRead8( &v->v ) < 0 ) + mmAtomicPause(); + else + { + if( !( mmAtomicAddTestNegative8( &v->v, 1 ) ) ) + return 1; + mmAtomicAdd8( &v->v, -1 ); + } + } while( --spincount ); + return 0; +} + +static inline int mmAtomicLockTryWrite8( mmAtomicLock8 *v, int spincount ) +{ + do + { + if( mmAtomicRead8( &v->v ) ) + mmAtomicPause(); + else + { + if( !( mmAtomicCmpXchg8( &v->v, 0, MM_ATOMIC_LOCK8_WRITE ) ) ) + return 1; + } + } while( --spincount ); + return 0; +} + +static inline void mmAtomicLockDoneRead8( mmAtomicLock8 *v ) +{ + mmAtomicAdd8( &v->v, -1 ); + return; +} + +static inline void mmAtomicLockDoneWrite8( mmAtomicLock8 *v ) +{ + mmAtomicAdd8( &v->v, -MM_ATOMIC_LOCK8_WRITE ); + return; +} + + +//// + + +typedef struct { mmAtomic16 v; } mmAtomicLock16; + +#define MM_ATOMIC_LOCK16_WRITE (-((int16_t)0x7fff)) + +static inline void mmAtomicLockInit16( mmAtomicLock16 *v ) +{ + mmAtomicWrite16( &v->v, 0 ); + return; +} + +static inline int mmAtomicLockAttemptRead16( mmAtomicLock16 *v ) +{ + if( mmAtomicAddTestNegative16( &v->v, 1 ) ) + { + mmAtomicAdd16( &v->v, -1 ); + return 0; + } + return 1; +} + +static inline int mmAtomicLockAttemptWrite16( mmAtomicLock16 *v ) +{ + if( mmAtomicCmpXchg16( &v->v, 0, MM_ATOMIC_LOCK16_WRITE ) ) + return 0; + return 1; +} + +static inline void mmAtomicLockSpinRead16( mmAtomicLock16 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead16( &v->v ) < 0 ; ) + mmAtomicPause(); + if( !( mmAtomicAddTestNegative16( &v->v, 1 ) ) ) + break; + mmAtomicAdd16( &v->v, -1 ); + } + return; +} + +static inline void mmAtomicLockSpinWrite16( mmAtomicLock16 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead16( &v->v ) ; ) + mmAtomicPause(); + if( !( mmAtomicCmpXchg16( &v->v, 0, MM_ATOMIC_LOCK16_WRITE ) ) ) + break; + } + return; +} + +static inline int mmAtomicLockTryRead16( mmAtomicLock16 *v, int spincount ) +{ + do + { + if( mmAtomicRead16( &v->v ) < 0 ) + mmAtomicPause(); + else + { + if( !( mmAtomicAddTestNegative16( &v->v, 1 ) ) ) + return 1; + mmAtomicAdd16( &v->v, -1 ); + } + } while( --spincount ); + return 0; +} + +static inline int mmAtomicLockTryWrite16( mmAtomicLock16 *v, int spincount ) +{ + do + { + if( mmAtomicRead16( &v->v ) ) + mmAtomicPause(); + else + { + if( !( mmAtomicCmpXchg16( &v->v, 0, MM_ATOMIC_LOCK16_WRITE ) ) ) + return 1; + } + } while( --spincount ); + return 0; +} + +static inline void mmAtomicLockDoneRead16( mmAtomicLock16 *v ) +{ + mmAtomicAdd16( &v->v, -1 ); + return; +} + +static inline void mmAtomicLockDoneWrite16( mmAtomicLock16 *v ) +{ + mmAtomicAdd16( &v->v, -MM_ATOMIC_LOCK16_WRITE ); + return; +} + + +//// + + +typedef struct { mmAtomic32 v; } mmAtomicLock32; + +/* +#define MM_ATOMIC_LOCK32_WRITE (-((int32_t)0x7fffffff)) +*/ +#define MM_ATOMIC_LOCK32_WRITE (-((int32_t)0x10000000)) + +static inline void mmAtomicLockInit32( mmAtomicLock32 *v ) +{ + mmAtomicWrite32( &v->v, 0 ); + return; +} + +static inline int mmAtomicLockAttemptRead32( mmAtomicLock32 *v ) +{ + if( mmAtomicAddTestNegative32( &v->v, 1 ) ) + { + mmAtomicAdd32( &v->v, -1 ); + return 0; + } + return 1; +} + +static inline int mmAtomicLockAttemptWrite32( mmAtomicLock32 *v ) +{ + if( mmAtomicCmpXchg32( &v->v, 0, MM_ATOMIC_LOCK32_WRITE ) ) + return 0; + return 1; +} + +static inline void mmAtomicLockSpinRead32( mmAtomicLock32 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead32( &v->v ) < 0 ; ) + mmAtomicPause(); + if( !( mmAtomicAddTestNegative32( &v->v, 1 ) ) ) + break; + mmAtomicAdd32( &v->v, -1 ); + } + return; +} + +static inline void mmAtomicLockSpinWrite32( mmAtomicLock32 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead32( &v->v ) ; ) + mmAtomicPause(); + if( !( mmAtomicCmpXchg32( &v->v, 0, MM_ATOMIC_LOCK32_WRITE ) ) ) + break; + } + return; +} + +static inline int mmAtomicLockTryRead32( mmAtomicLock32 *v, int spincount ) +{ + do + { + if( mmAtomicRead32( &v->v ) < 0 ) + mmAtomicPause(); + else + { + if( !( mmAtomicAddTestNegative32( &v->v, 1 ) ) ) + return 1; + mmAtomicAdd32( &v->v, -1 ); + } + } while( --spincount ); + return 0; +} + +static inline int mmAtomicLockTryWrite32( mmAtomicLock32 *v, int spincount ) +{ + do + { + if( mmAtomicRead32( &v->v ) ) + mmAtomicPause(); + else + { + if( !( mmAtomicCmpXchg32( &v->v, 0, MM_ATOMIC_LOCK32_WRITE ) ) ) + return 1; + } + } while( --spincount ); + return 0; +} + +static inline void mmAtomicLockDoneRead32( mmAtomicLock32 *v ) +{ + mmAtomicAdd32( &v->v, -1 ); + return; +} + +static inline void mmAtomicLockDoneWrite32( mmAtomicLock32 *v ) +{ + mmAtomicAdd32( &v->v, -MM_ATOMIC_LOCK32_WRITE ); + return; +} + + +//// + + +#ifdef MM_ATOMIC_64_BITS_SUPPORT + +typedef struct { mmAtomic64 v; } mmAtomicLock64; + +#define MM_ATOMIC_LOCK64_WRITE (-((int64_t)0x7fffffffffffffff)) + +static inline void mmAtomicLockInit64( mmAtomicLock64 *v ) +{ + mmAtomicWrite64( &v->v, 0 ); + return; +} + +static inline int mmAtomicLockAttemptRead64( mmAtomicLock64 *v ) +{ + if( mmAtomicAddTestNegative64( &v->v, 1 ) ) + { + mmAtomicAdd64( &v->v, -1 ); + return 0; + } + return 1; +} + +static inline int mmAtomicLockAttemptWrite64( mmAtomicLock64 *v ) +{ + if( mmAtomicCmpXchg64( &v->v, 0, MM_ATOMIC_LOCK64_WRITE ) ) + return 0; + return 1; +} + +static inline void mmAtomicLockSpinRead64( mmAtomicLock64 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead64( &v->v ) < 0 ; ) + mmAtomicPause(); + if( !( mmAtomicAddTestNegative64( &v->v, 1 ) ) ) + break; + mmAtomicAdd64( &v->v, -1 ); + } + return; +} + +static inline void mmAtomicLockSpinWrite64( mmAtomicLock64 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead64( &v->v ) ; ) + mmAtomicPause(); + if( !( mmAtomicCmpXchg64( &v->v, 0, MM_ATOMIC_LOCK64_WRITE ) ) ) + break; + } + return; +} + +static inline int mmAtomicLockTryRead64( mmAtomicLock64 *v, int spincount ) +{ + do + { + if( mmAtomicRead64( &v->v ) < 0 ) + mmAtomicPause(); + else + { + if( !( mmAtomicAddTestNegative64( &v->v, 1 ) ) ) + return 1; + mmAtomicAdd64( &v->v, -1 ); + } + } while( --spincount ); + return 0; +} + +static inline int mmAtomicLockTryWrite64( mmAtomicLock64 *v, int spincount ) +{ + do + { + if( mmAtomicRead64( &v->v ) ) + mmAtomicPause(); + else + { + if( !( mmAtomicCmpXchg64( &v->v, 0, MM_ATOMIC_LOCK64_WRITE ) ) ) + return 1; + } + } while( --spincount ); + return 0; +} + +static inline void mmAtomicLockDoneRead64( mmAtomicLock64 *v ) +{ + mmAtomicAdd64( &v->v, -1 ); + return; +} + +static inline void mmAtomicLockDoneWrite64( mmAtomicLock64 *v ) +{ + mmAtomicAdd64( &v->v, -MM_ATOMIC_LOCK64_WRITE ); + return; +} + +#endif + + + +//////////////// + + + +#define MM_ATOMIC_LIST_BUSY ((void *)0x1) + +typedef struct +{ + mmAtomicP prev; /* mmAtomicP* to &(prev->next) */ + mmAtomicP next; /* void* to next */ + mmAtomic32 status; +} mmAtomicListNode; + +#define MM_ATOMIC_LIST_VALID (0x0) +#define MM_ATOMIC_LIST_DELETED (0x1) + +typedef struct +{ + mmAtomicP first; /* void* to item */ + mmAtomicP last; /* mmAtomicP* to &(lastitem->next) */ +} mmAtomicListDualHead; + + +void mmAtomicListAdd( mmAtomicP *list, void *item, intptr_t offset ); +void mmAtomicListRemove( void *item, intptr_t offset ); + +static inline void *mmAtomicListFirst( mmAtomicP *head ) +{ + void *item; + for( ; ( item = mmAtomicReadP( head ) ) == MM_ATOMIC_LIST_BUSY ; ) + mmAtomicPause(); + return item; +} + +static inline void *mmAtomicListNext( mmAtomicListNode *node ) +{ + void *item; + for( ; ; ) + { + item = mmAtomicReadP( &node->next ); + if( mmAtomicRead32( &node->status ) == MM_ATOMIC_LIST_DELETED ) + return 0; + /* At the time we have read node->next, the node has not been deleted yet */ + if( item != MM_ATOMIC_LIST_BUSY ) + break; + mmAtomicPause(); + } + return item; +} + + +void mmAtomicListDualInit( mmAtomicListDualHead *head ); +void mmAtomicListDualAddFirst( mmAtomicListDualHead *head, void *item, intptr_t offset ); +void mmAtomicListDualAddLast( mmAtomicListDualHead *head, void *item, intptr_t offset ); +void mmAtomicListDualRemove( mmAtomicListDualHead *head, void *item, intptr_t offset ); + +static inline void *mmAtomicListDualFirst( mmAtomicListDualHead *head ) +{ + void *item; + for( ; ( item = mmAtomicReadP( &head->first ) ) == MM_ATOMIC_LIST_BUSY ; ) + mmAtomicPause(); + return item; +} + +static inline void *mmAtomicListDualNext( mmAtomicListNode *node ) +{ + void *item; + for( ; ; ) + { + item = mmAtomicReadP( &node->next ); + if( mmAtomicRead32( &node->status ) == MM_ATOMIC_LIST_DELETED ) + return 0; + /* At the time we have read node->next, the node has not been deleted yet */ + if( item != MM_ATOMIC_LIST_BUSY ) + break; + mmAtomicPause(); + } + return item; +} + + + +//////////////// + + + +/* +#define MM_ATOMIC_BARRIER_DEBUG +*/ + + +#define MM_ATOMIC_BARRIER_DELAYED_RESET + + +typedef struct +{ + int32_t clearcounter; + int32_t yieldcounter; +} mmAtomicBarrierStat; + +typedef struct +{ + mmAtomic32 flag MM_CACHE_ALIGN; + mmAtomic32 counter MM_CACHE_ALIGN; + volatile int32_t flagref MM_CACHE_ALIGN; + void *parent MM_CACHE_ALIGN; + int32_t resetvalue; +} mmAtomicBarrier; + +void mmAtomicBarrierBuild( mmAtomicBarrier *barrier, int childcount, mmAtomicBarrier *parent ); +int mmAtomicBarrierWait( mmAtomicBarrier *barrier, int32_t spinwaitcounter, mmAtomicBarrierStat *barrierstat ); + + + + +//// + + + +typedef struct +{ + mmAtomic32 counter MM_CACHE_ALIGN; + /* Data below remains constant */ + void *parent MM_CACHE_ALIGN; + int32_t resetvalue; +} mmAtomicCounterNode; + +typedef struct +{ + int32_t lockcount; + int32_t nodecount; + mmAtomicCounterNode *nodearray; + mmAtomicCounterNode **locknode; +} mmAtomicCounter; + +void mmAtomicCounterInit( mmAtomicCounter *counter, int lockcount, int stagesize ); +void mmAtomicCounterDestroy( mmAtomicCounter *counter ); +int mmAtomicCounterHit( mmAtomicCounter *counter, int lockindex ); + + + +//// + + +/* +Stockpile callbacks for freeing items + +*/ + +typedef struct +{ + mmAtomicP p; + + +} mmAtomicRcu; + +static inline void mmAtomicRcuEnter( mmAtomicRcu *rcu ) +{ + /* On archs with messed up memory models like Alpha, we would need some memory barrier here */ + return; +} + +static inline void mmAtomicRcuLeave( mmAtomicRcu *rcu ) +{ + /* On archs with messed up memory models like Alpha, we would need some memory barrier here */ + return; +} + + +/* +To reiterate, synchronize_rcu() waits only for ongoing RCU read-side critical sections to complete, +not necessarily for any that begin after synchronize_rcu() is invoked. +*/ +static inline void mmAtomicRcuSync() +{ + +} + +static inline void mmAtomicRcuRead() +{ + + +} + + +static inline void mmAtomicRcuWrite() +{ + + +} + + + +#endif + + diff --git a/ecere/src/gfx/newFonts/cc/mmbitmap.c b/ecere/src/gfx/newFonts/cc/mmbitmap.c new file mode 100644 index 0000000000..52dc127a31 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mmbitmap.c @@ -0,0 +1,339 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include "cpuconfig.h" +#include "cc.h" +#include "mm.h" +#include "mmbitmap.h" + + +/* Friendlier to cache on SMP systems */ +#define BP_BITMAP_PREWRITE_CHECK + + +/* +TODO +If we don't have atomic instruction support somehow, we need a much better mutex locking mechanism!! +*/ + + +int mmBitMapInit( mmBitMap *bitmap, size_t entrycount, int initvalue ) +{ + size_t mapsize, index; + long value; +#ifdef MM_ATOMIC_SUPPORT + mmAtomicL *map; +#else + long *map; +#endif + + mapsize = ( entrycount + CPUCONF_LONG_BITS - 1 ) >> CPUCONF_LONG_BITSHIFT; + bitmap->map = 0; + if( mapsize ) + { +#ifdef MM_ATOMIC_SUPPORT + if( !( bitmap->map = malloc( mapsize * sizeof(mmAtomicL) ) ) ) + return 0; +#else + if( !( bitmap->map = malloc( mapsize * sizeof(long) ) ) ) + return 0; +#endif + } + bitmap->mapsize = mapsize; + bitmap->entrycount = entrycount; + + map = bitmap->map; + value = ( initvalue & 0x1 ? ~0x0 : 0x0 ); +#ifdef MM_ATOMIC_SUPPORT + for( index = 0 ; index < mapsize ; index++ ) + mmAtomicWriteL( &map[index], value ); +#else + for( index = 0 ; index < mapsize ; index++ ) + map[index] = value; + mtMutexInit( &bitmap->mutex ); +#endif + + return 1; +} + +void mmBitMapReset( mmBitMap *bitmap, int resetvalue ) +{ + size_t index; + long value; +#ifdef MM_ATOMIC_SUPPORT + mmAtomicL *map; +#else + long *map; +#endif + + map = bitmap->map; + value = ( resetvalue & 0x1 ? ~(long)0x0 : (long)0x0 ); +#ifdef MM_ATOMIC_SUPPORT + for( index = 0 ; index < bitmap->mapsize ; index++ ) + mmAtomicWriteL( &map[index], value ); +#else + for( index = 0 ; index < bitmap->mapsize ; index++ ) + map[index] = value; +#endif + + return; +} + +void mmBitMapResetRange( mmBitMap *bitmap, int minimumentrycount, int resetvalue ) +{ + size_t index, entrycount; + long value; +#ifdef MM_ATOMIC_SUPPORT + mmAtomicL *map; +#else + long *map; +#endif + + map = bitmap->map; + value = ( resetvalue & 0x1 ? ~(long)0x0 : (long)0x0 ); + entrycount = ( minimumentrycount + CPUCONF_LONG_BITS - 1 ) >> CPUCONF_LONG_BITSHIFT; +#ifdef MM_ATOMIC_SUPPORT + for( index = 0 ; index < entrycount ; index++ ) + mmAtomicWriteL( &map[index], value ); +#else + for( index = 0 ; index < entrycount ; index++ ) + map[index] = value; +#endif + + return; +} + +void mmBitMapFree( mmBitMap *bitmap ) +{ + free( bitmap->map ); + bitmap->map = 0; + bitmap->mapsize = 0; +#ifndef MM_ATOMIC_SUPPORT + mtMutexDestroy( &bitmap->mutex ); +#endif + return; +} + +/* TODO: Yeah... That code was written in one go, maybe I should test if it's working fine, just in case? */ +int mmBitMapFindSet( mmBitMap *bitmap, size_t entryindex, size_t entryindexlast, size_t *retentryindex ) +{ + unsigned long value; + size_t index, shift, shiftbase, shiftmax, indexlast, shiftlast; + + if( !( bitmap->entrycount ) ) + return 0; + + indexlast = entryindexlast >> CPUCONF_LONG_BITSHIFT; + shiftlast = entryindexlast & ( CPUCONF_LONG_BITS - 1 ); + + /* Leading bits search */ + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shiftbase = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = mmAtomicReadL( &bitmap->map[index] ); +#else + mtMutexLock( &bitmap->mutex ); + value = bitmap->map[index]; +#endif + if( value != (unsigned long)0x0 ) + { + shiftmax = CPUCONF_LONG_BITS-1; + if( ( index == indexlast ) && ( shiftlast > shiftbase ) ) + shiftmax = shiftlast; + value >>= shiftbase; + for( shift = shiftbase ; shift <= shiftmax ; shift++, value >>= 1 ) + { + if( !( value & 0x1 ) ) + continue; + entryindex = ( index << CPUCONF_LONG_BITSHIFT ) | shift; + if( entryindex >= bitmap->entrycount ) + break; +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + *retentryindex = entryindex; + return 1; + } + } + if( ( index == indexlast ) && ( shiftlast > shiftbase ) ) + { +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + return 0; + } + + /* Main search */ + for( ; ; ) + { + index = ( index + 1 ) % bitmap->mapsize; + if( index == indexlast ) + break; +#ifdef MM_ATOMIC_SUPPORT + value = mmAtomicReadL( &bitmap->map[index] ); +#else + value = bitmap->map[index]; +#endif + if( value != (unsigned long)0x0 ) + { + for( shift = 0 ; ; shift++, value >>= 1 ) + { + if( !( value & 0x1 ) ) + continue; + entryindex = ( index << CPUCONF_LONG_BITSHIFT ) | shift; + if( entryindex >= bitmap->entrycount ) + break; +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + *retentryindex = entryindex; + return 1; + } + } + } + + /* Trailing bits search */ + shiftlast = entryindexlast & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = mmAtomicReadL( &bitmap->map[index] ); +#else + value = bitmap->map[index]; +#endif + if( value != (unsigned long)0x0 ) + { + for( shift = 0 ; shift < shiftlast ; shift++, value >>= 1 ) + { + if( !( value & 0x1 ) ) + continue; + entryindex = ( index << CPUCONF_LONG_BITSHIFT ) | shift; + if( entryindex >= bitmap->entrycount ) + break; +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + *retentryindex = entryindex; + return 1; + } + } +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + + return 0; +} + +int mmBitMapFindClear( mmBitMap *bitmap, size_t entryindex, size_t entryindexlast, size_t *retentryindex ) +{ + unsigned long value; + size_t index, shift, shiftbase, shiftmax, indexlast, shiftlast; + + if( !( bitmap->entrycount ) ) + return 0; + + indexlast = entryindexlast >> CPUCONF_LONG_BITSHIFT; + shiftlast = entryindexlast & ( CPUCONF_LONG_BITS - 1 ); + + /* Leading bits search */ + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shiftbase = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = mmAtomicReadL( &bitmap->map[index] ); +#else + mtMutexLock( &bitmap->mutex ); + value = bitmap->map[index]; +#endif + if( value != ~(unsigned long)0x0 ) + { + shiftmax = CPUCONF_LONG_BITS-1; + if( ( index == indexlast ) && ( shiftlast > shiftbase ) ) + shiftmax = shiftlast; + value >>= shiftbase; + for( shift = shiftbase ; shift <= shiftmax ; shift++, value >>= 1 ) + { + if( value & 0x1 ) + continue; + entryindex = ( index << CPUCONF_LONG_BITSHIFT ) | shift; + if( entryindex >= bitmap->entrycount ) + break; +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + *retentryindex = entryindex; + return 1; + } + } + if( ( index == indexlast ) && ( shiftlast > shiftbase ) ) + { +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + return 0; + } + + /* Main search */ + for( ; ; ) + { + index = ( index + 1 ) % bitmap->mapsize; + if( index == indexlast ) + break; +#ifdef MM_ATOMIC_SUPPORT + value = mmAtomicReadL( &bitmap->map[index] ); +#else + value = bitmap->map[index]; +#endif + if( value != ~(unsigned long)0x0 ) + { + for( shift = 0 ; ; shift++, value >>= 1 ) + { + if( value & 0x1 ) + continue; + entryindex = ( index << CPUCONF_LONG_BITSHIFT ) | shift; + if( entryindex >= bitmap->entrycount ) + break; +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + *retentryindex = entryindex; + return 1; + } + } + } + + /* Trailing bits search */ + shiftlast = entryindexlast & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = mmAtomicReadL( &bitmap->map[index] ); +#else + value = bitmap->map[index]; +#endif + if( value != ~(unsigned long)0x0 ) + { + for( shift = 0 ; shift <= shiftlast ; shift++, value >>= 1 ) + { + if( value & 0x1 ) + continue; + entryindex = ( index << CPUCONF_LONG_BITSHIFT ) | shift; + if( entryindex >= bitmap->entrycount ) + break; +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + *retentryindex = entryindex; + return 1; + } + } +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + + return 0; +} diff --git a/ecere/src/gfx/newFonts/cc/mmbitmap.h b/ecere/src/gfx/newFonts/cc/mmbitmap.h new file mode 100644 index 0000000000..33360c7291 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mmbitmap.h @@ -0,0 +1,212 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +#include "cpuconfig.h" +#include "mm.h" + +#if !defined(CPUCONF_LONG_BITSHIFT) || !defined(CPUCONF_LONG_BITS) + #error Preprocessor symbols CPUCONF_LONG_BITSHIFT and CPUCONF_LONG_BITS are undefined! + #error This header requires cpuconfig.h +#endif + +#if !defined(MM_ATOMIC_SUPPORT) + //#warning Compiling mmbitmap without atomic support, it is going to be SLOW. +#ifndef MM_H + #warning This header requires mm.h +#endif +#endif + + +typedef struct +{ + size_t entrycount; + size_t mapsize; +#ifdef MM_ATOMIC_SUPPORT + mmAtomicL *map; +#else + long *map; + mtMutex mutex; +#endif +} mmBitMap; + +int mmBitMapInit( mmBitMap *bitmap, size_t entrycount, int initvalue ); +void mmBitMapReset( mmBitMap *bitmap, int resetvalue ); +void mmBitMapResetRange( mmBitMap *bitmap, int minimumentrycount, int resetvalue ); +void mmBitMapFree( mmBitMap *bitmap ); + +int mmBitMapFindSet( mmBitMap *bitmap, size_t entryindex, size_t entryindexlast, size_t *retentryindex ); +int mmBitMapFindClear( mmBitMap *bitmap, size_t entryindex, size_t entryindexlast, size_t *retentryindex ); + + +//// + + +/* No atomic locking, single-threaded access */ + +static inline int mmBitMapDirectGet( mmBitMap *bitmap, size_t entryindex ) +{ + int value; + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = (int)(( MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) >> shift ) & 0x1); +#else + value = (int)(( bitmap->map[index] >> shift ) & 0x1); +#endif + return value; +} + +static inline void mmBitMapDirectSet( mmBitMap *bitmap, size_t entryindex ) +{ + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) |= (long)1 << shift; +#else + bitmap->map[index] |= (long)1 << shift; +#endif + return; +} + +static inline void mmBitMapDirectClear( mmBitMap *bitmap, size_t entryindex ) +{ + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) &= ~( (long)1 << shift ); +#else + bitmap->map[index] &= ~( (long)1 << shift ); +#endif + return; +} + +static inline int mmBitMapDirectMaskGet( mmBitMap *bitmap, size_t entryindex, long mask ) +{ + int value; + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = (int)(( MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) >> shift ) & mask); +#else + value = (int)(( bitmap->map[index] >> shift ) & mask); +#endif + return value; +} + +static inline void mmBitMapDirectMaskSet( mmBitMap *bitmap, size_t entryindex, long value, long mask ) +{ + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) = ( MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) & ~( mask << shift ) ) | ( value << shift ); +#else + bitmap->map[index] = ( bitmap->map[index] & ~( mask << shift ) ) | ( value << shift ); +#endif + return; +} + + +//// + + +/* Atomic locking, multi-threaded access */ + +static inline int mmBitMapGet( mmBitMap *bitmap, size_t entryindex ) +{ + int value; + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = (int)(( mmAtomicReadL( &bitmap->map[index] ) >> shift ) & 0x1); +#else + mtMutexLock( &bitmap->mutex ); + value = (int)(( bitmap->map[index] >> shift ) & 0x1); + mtMutexUnlock( &bitmap->mutex ); +#endif + return value; +} + +static inline void mmBitMapSet( mmBitMap *bitmap, size_t entryindex ) +{ + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + #ifdef BP_BITMAP_PREWRITE_CHECK + if( !( mmAtomicReadL( &bitmap->map[index] ) & ( (long)1 << shift ) ) ) + mmAtomicOrL( &bitmap->map[index], (long)1 << shift ); + #else + mmAtomicOrL( &bitmap->map[index], (long)1 << shift ); + #endif +#else + mtMutexLock( &bitmap->mutex ); + bitmap->map[index] |= (long)1 << shift; + mtMutexUnlock( &bitmap->mutex ); +#endif + return; +} + +static inline void mmBitMapClear( mmBitMap *bitmap, size_t entryindex ) +{ + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + #ifdef BP_BITMAP_PREWRITE_CHECK + if( mmAtomicReadL( &bitmap->map[index] ) & ( (long)1 << shift ) ) + mmAtomicAndL( &bitmap->map[index], ~( (long)1 << shift ) ); + #else + mmAtomicAndL( &bitmap->map[index], ~( (long)1 << shift ) ); + #endif +#else + mtMutexLock( &bitmap->mutex ); + bitmap->map[index] &= ~( (long)1 << shift ); + mtMutexUnlock( &bitmap->mutex ); +#endif + return; +} + +static inline int mmBitMapMaskGet( mmBitMap *bitmap, size_t entryindex, long mask ) +{ + int value; + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = (int)(( mmAtomicReadL( &bitmap->map[index] ) >> shift ) & mask); +#else + mtMutexLock( &bitmap->mutex ); + value = (int)(( bitmap->map[index] >> shift ) & mask); + mtMutexUnlock( &bitmap->mutex ); +#endif + return value; +} + +static inline void mmBitMapMaskSet( mmBitMap *bitmap, size_t entryindex, long value, long mask ) +{ + size_t index = entryindex >> CPUCONF_LONG_BITSHIFT; + size_t shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + long oldvalue, newvalue; + for( ; ; ) + { + oldvalue = (int)mmAtomicReadL( &bitmap->map[index] ); + newvalue = ( oldvalue & ~( mask << shift ) ) | ( value << shift ); + if( mmAtomicCmpReplaceL( &bitmap->map[index], oldvalue, newvalue ) ) + break; + } +#else + mtMutexLock( &bitmap->mutex ); + bitmap->map[index] = ( bitmap->map[index] & ~( mask << shift ) ) | ( value << shift ); + mtMutexUnlock( &bitmap->mutex ); +#endif + return; +} diff --git a/ecere/src/gfx/newFonts/cc/mmhash.c b/ecere/src/gfx/newFonts/cc/mmhash.c new file mode 100644 index 0000000000..65a6a4f6a6 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mmhash.c @@ -0,0 +1,2361 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2015 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "cpuconfig.h" +#include "cc.h" +#include "mm.h" +#include "mmhash.h" + + +//// + + +#include "mmhashinternal.h" + + +//// + + + +static void mmHashSetBounds( mmHashTable *table ) +{ + table->lowcount = 0; + if( table->hashbits > table->minhashbits ) + table->lowcount = table->hashsize / 5; + table->highcount = table->hashsize / 2; + return; +} + + +size_t mmHashRequiredSize( size_t entrysize, uint32_t hashbits, uint32_t pageshift ) +{ + uint32_t entrycount; + uint32_t pagecount; + entrycount = 1 << hashbits; + pagecount = entrycount >> pageshift; + if( !( pagecount ) ) + pagecount = 1; + return MM_HASH_ALIGN64( MM_HASH_SIZEOF_ALIGN16(mmHashTable) + ( entrycount * entrysize ) ) + ( pagecount * sizeof(mmHashPage) ); +} + + +void mmHashInit( void *hashtable, const mmHashAccess *access, size_t entrysize, uint32_t hashbits, uint32_t pageshift, uint32_t flags ) +{ + uint32_t hashkey, pageindex; + void *entry; + mmHashTable *table; + mmHashPage *page; + void (*clearentry)( void *entry ) = access->clearentry; + void (*clearentries)( void *entry, unsigned int ) = access->clearentries; + + table = hashtable; + table->status = MM_HASH_STATUS_NORMAL; + if( flags & MM_HASH_FLAGS_NO_COUNT ) + table->status = MM_HASH_STATUS_UNKNOWN; + table->flags = flags; + table->entrysize = entrysize; + table->minhashbits = hashbits; + table->hashbits = hashbits; + table->hashsize = 1 << table->hashbits; + table->hashmask = table->hashsize - 1; + table->pageshift = pageshift; + table->pagecount = table->hashsize >> pageshift; + if( !( table->pagecount ) ) + table->pagecount = 1; + table->pagemask = table->pagecount - 1; + table->page = MM_HASH_PAGELIST( table ); +#ifdef MM_ATOMIC_SUPPORT + mmAtomicWrite32( &table->entrycount, 0 ); +#else + mtMutexInit( &table->countmutex ); + table->entrycount = 0; +#endif + mmHashSetBounds( table ); + + /* Clear the table */ + entry = MM_HASH_ENTRYLIST( table ); + if(clearentries) + clearentries(entry, table->hashsize); + else if(clearentry) + { + for( hashkey = 0 ; hashkey < table->hashsize ; hashkey++ ) + { + clearentry( entry ); + entry = ADDRESS( entry, entrysize ); + } + } + else + memset(entry, 0, entrysize * table->hashsize); + + /* Clear the lock pages */ + page = table->page; +#ifdef MM_ATOMIC_SUPPORT + for( pageindex = table->pagecount ; pageindex ; pageindex--, page++ ) + { + mmAtomicLockInit32( &page->lock ); + mmAtomicWriteP( &page->owner, 0 ); + } + mmAtomicWrite32( &table->globallock, 0x0 ); +#else + for( pageindex = table->pagecount ; pageindex ; pageindex--, page++ ) + { + mtMutexInit( &page->mutex ); + page->owner = 0; + } + mtMutexInit( &table->globalmutex ); +#endif + +#ifdef MM_HASH_DEBUG_STATISTICS + table->entrycountmax = 0; + mmAtomicWriteL( &table->statentrycount, 0 ); + mmAtomicWriteL( &table->accesscount, 0 ); + mmAtomicWriteL( &table->collisioncount, 0 ); + mmAtomicWriteL( &table->relocationcount, 0 ); +#endif + + return; +} + + +int mmHashGetStatus( void *hashtable, int *rethashbits ) +{ + mmHashTable *table; + table = hashtable; + if( rethashbits ) + *rethashbits = table->hashbits; + return table->status; +} + + +void mmHashReset( void *hashtable, const mmHashAccess *access ) +{ + int hashkey; + mmHashTable *table; + void *entry; + void (*clearentry)( void *entry ) = access->clearentry; + + /* Clear the table */ + table = hashtable; + entry = MM_HASH_ENTRYLIST( table ); + if(clearentry) + { + for( hashkey = 0 ; hashkey < table->hashsize ; hashkey++ ) + { + clearentry( entry ); + entry = ADDRESS( entry, table->entrysize ); + } + } + else + memset(entry, 0, table->hashsize * table->entrysize); +#ifdef MM_ATOMIC_SUPPORT + mmAtomicWrite32( &table->entrycount, 0 ); +#else + table->entrycount = 0; +#endif + return; +} + + +//// + + + +void *mmHashDirectFindEntry( void *hashtable, const mmHashAccess *access, const void *findentry ) +{ + int cmpvalue; + uint32_t hashkey; + void *entry; + mmHashTable *table; + + table = hashtable; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = access->entrykey( findentry ) & table->hashmask; + + /* Search the entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + entry = MM_HASH_ENTRY( table, hashkey ); + cmpvalue = access->entrycmp( entry, findentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + return entry; +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + return 0; +} + + +static int mmHashTryFindEntry( mmHashTable *table, const mmHashAccess *access, void *findentry, void **retentry ) +{ + uint32_t hashkey; + uint32_t pageindex, pagestart, pagefinal; + int cmpvalue, retvalue; + void *entry; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = access->entrykey( findentry ) & table->hashmask; + + /* Lock first page */ + pagestart = hashkey >> table->pageshift; + pagefinal = pagestart; + if( !( MM_HASH_LOCK_TRY_READ( table, pagestart ) ) ) + return MM_HASH_TRYAGAIN; + + /* Search the entry */ + entry = 0; + retvalue = MM_HASH_SUCCESS; + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + /* Lock new pages */ + pageindex = hashkey >> table->pageshift; + if( pageindex != pagefinal ) + { + if( !( MM_HASH_LOCK_TRY_READ( table, pageindex ) ) ) + { + retvalue = MM_HASH_TRYAGAIN; + break; + } + pagefinal = pageindex; + } + + /* Check for entry match */ + entry = MM_HASH_ENTRY( table, hashkey ); + cmpvalue = access->entrycmp( entry, findentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + { + retvalue = MM_HASH_FAILURE; + entry = 0; + break; + } + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + break; +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + /* Unlock all pages */ + for( pageindex = pagestart ; ; pageindex = ( pageindex + 1 ) & table->pagemask ) + { + MM_HASH_LOCK_DONE_READ( table, pageindex ); + if( pageindex == pagefinal ) + break; + } + + *retentry = entry; + return retvalue; +} + + +void *mmHashLockFindEntry( void *hashtable, const mmHashAccess *access, void *findentry ) +{ + int retvalue; + void *entry; + mmHashTable *table; + + table = hashtable; + retvalue = mmHashTryFindEntry( table, access, findentry, &entry ); + if( retvalue == MM_HASH_TRYAGAIN ) + { + MM_HASH_GLOBAL_LOCK( table ); + do + { + retvalue = mmHashTryFindEntry( table, access, findentry, &entry ); + } while( retvalue == MM_HASH_TRYAGAIN ); + MM_HASH_GLOBAL_UNLOCK( table ); + } + + return entry; +} + + + +//// + + + +void mmHashDirectListEntry( void *hashtable, const mmHashAccess *access, void *listentry, void *opaque ) +{ + int cmpvalue; + uint32_t hashkey; + void *entry; + mmHashTable *table; + + table = hashtable; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = access->entrykey( listentry ) & table->hashmask; + + /* Search the entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + entry = MM_HASH_ENTRY( table, hashkey ); + cmpvalue = access->entrylist( opaque, entry, listentry ); + if( cmpvalue == MM_HASH_ENTRYLIST_BREAK ) + break; +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + return; +} + +// WARNING: This is ordered by hash, not key values... +void * mmHashGetNext( void *hashtable, void * entry, const mmHashAccess *access) +{ + mmHashTable * table = hashtable; + uint32_t hashkey = entry ? MM_HASH_HASHKEY(table, entry) : 0; + if(hashkey == table->hashmask) return NULL; + if(entry) hashkey++; + for( ; ; hashkey = ( hashkey + 1 )) + { + entry = MM_HASH_ENTRY( table, hashkey ); + if(access->entryvalid( entry )) + return entry; + if(hashkey == table->hashmask) break; + } + return NULL; +} + +void * mmHashGetPrev( void *hashtable, void * entry, const mmHashAccess *access) +{ + mmHashTable * table = hashtable; + uint32_t hashkey = entry ? MM_HASH_HASHKEY(table, entry) : table->hashmask; + if(!hashkey) return NULL; + if(entry) hashkey--; + for( ; ; hashkey = ( hashkey - 1 )) + { + entry = MM_HASH_ENTRY( table, hashkey ); + if(access->entryvalid( entry )) + return entry; + if(!hashkey) break; + } + return NULL; +} + +static int mmHashTryListEntry( mmHashTable *table, const mmHashAccess *access, void *listentry, void *opaque ) +{ + uint32_t hashkey; + uint32_t pageindex, pagestart, pagefinal; + int cmpvalue, retvalue; + void *entry; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = access->entrykey( listentry ) & table->hashmask; + + /* Lock first page */ + pagestart = hashkey >> table->pageshift; + pagefinal = pagestart; + if( !( MM_HASH_LOCK_TRY_READ( table, pagestart ) ) ) + return MM_HASH_TRYAGAIN; + + /* Search the entry */ + entry = 0; + retvalue = MM_HASH_SUCCESS; + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + /* Lock new pages */ + pageindex = hashkey >> table->pageshift; + if( pageindex != pagefinal ) + { + if( !( MM_HASH_LOCK_TRY_READ( table, pageindex ) ) ) + { + retvalue = MM_HASH_TRYAGAIN; + break; + } + pagefinal = pageindex; + } + + /* Check for entry match */ + entry = MM_HASH_ENTRY( table, hashkey ); + cmpvalue = access->entrylist( opaque, entry, listentry ); + if( cmpvalue == MM_HASH_ENTRYLIST_BREAK ) + break; +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + /* Unlock all pages */ + for( pageindex = pagestart ; ; pageindex = ( pageindex + 1 ) & table->pagemask ) + { + MM_HASH_LOCK_DONE_READ( table, pageindex ); + if( pageindex == pagefinal ) + break; + } + + return retvalue; +} + + +void mmHashLockListEntry( void *hashtable, const mmHashAccess *access, void *listentry, void *opaque ) +{ + int retvalue; + mmHashTable *table; + + table = hashtable; + retvalue = mmHashTryListEntry( table, access, listentry, opaque ); + if( retvalue == MM_HASH_TRYAGAIN ) + { + MM_HASH_GLOBAL_LOCK( table ); + do + { + retvalue = mmHashTryListEntry( table, access, listentry, opaque ); + } while( retvalue == MM_HASH_TRYAGAIN ); + MM_HASH_GLOBAL_UNLOCK( table ); + } + + return; +} + + + +//// + + + +int mmHashDirectReadEntry( void *hashtable, const mmHashAccess *access, void *readentry ) +{ + int cmpvalue; + uint32_t hashkey; + void *entry; + mmHashTable *table; + + table = hashtable; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = access->entrykey( readentry ) & table->hashmask; + + /* Search the entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + entry = MM_HASH_ENTRY( table, hashkey ); + cmpvalue = access->entrycmp( entry, readentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + { + memcpy( readentry, entry, table->entrysize ); + return MM_HASH_SUCCESS; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + return MM_HASH_FAILURE; +} + + +static int mmHashTryReadEntry( mmHashTable *table, const mmHashAccess *access, void *readentry ) +{ + uint32_t hashkey; + uint32_t pageindex, pagestart, pagefinal; + int cmpvalue, retvalue; + void *entry; + + /* Hash key of entry */ + hashkey = access->entrykey( readentry ) & table->hashmask; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Lock first page */ + pagestart = hashkey >> table->pageshift; + pagefinal = pagestart; + if( !( MM_HASH_LOCK_TRY_READ( table, pagestart ) ) ) + return MM_HASH_TRYAGAIN; + + /* Search the entry */ + entry = 0; + retvalue = MM_HASH_SUCCESS; + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + /* Lock new pages */ + pageindex = hashkey >> table->pageshift; + if( pageindex != pagefinal ) + { + if( !( MM_HASH_LOCK_TRY_READ( table, pageindex ) ) ) + { + retvalue = MM_HASH_TRYAGAIN; + break; + } + pagefinal = pageindex; + } + + /* Check for entry match */ + entry = MM_HASH_ENTRY( table, hashkey ); + cmpvalue = access->entrycmp( entry, readentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + { + retvalue = MM_HASH_FAILURE; + entry = 0; + break; + } + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + break; +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + if( entry ) + memcpy( readentry, entry, table->entrysize ); + + /* Unlock all pages */ + for( pageindex = pagestart ; ; pageindex = ( pageindex + 1 ) & table->pagemask ) + { + MM_HASH_LOCK_DONE_READ( table, pageindex ); + if( pageindex == pagefinal ) + break; + } + + return retvalue; +} + + +int mmHashLockReadEntry( void *hashtable, const mmHashAccess *access, void *readentry ) +{ + int retvalue; + mmHashTable *table; + + table = hashtable; + retvalue = mmHashTryReadEntry( table, access, readentry ); + if( retvalue == MM_HASH_TRYAGAIN ) + { + MM_HASH_GLOBAL_LOCK( table ); + do + { + retvalue = mmHashTryReadEntry( table, access, readentry ); + } while( retvalue == MM_HASH_TRYAGAIN ); + MM_HASH_GLOBAL_UNLOCK( table ); + } + + return retvalue; +} + + + +//// + + + +int mmHashDirectCallEntry( void *hashtable, const mmHashAccess *access, void *callentry, void (*callback)( void *opaque, void *entry, int newflag ), void *opaque, int addflag ) +{ + int cmpvalue; + uint32_t hashkey, entrycount; + void *entry; + mmHashTable *table; + + table = hashtable; + + /* Hash key of entry */ + hashkey = access->entrykey( callentry ) & table->hashmask; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Search an available entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + entry = MM_HASH_ENTRY( table, hashkey ); + cmpvalue = access->entrycmp( entry, callentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + { + callback( opaque, entry, 0 ); + return MM_HASH_SUCCESS; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + if( !( addflag ) ) + return MM_HASH_FAILURE; + + /* Store new entry */ + memcpy( entry, callentry, table->entrysize ); + callback( opaque, entry, 1 ); + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + return MM_HASH_SUCCESS; +} + + +static int mmHashTryCallEntry( mmHashTable *table, const mmHashAccess *access, void *callentry, void (*callback)( void *opaque, void *entry, int newflag ), void *opaque, int addflag ) +{ + uint32_t hashkey, entrycount; + uint32_t pageindex, pagestart, pagefinal; + int cmpvalue, retvalue; + void *entry; + + /* Hash key of entry */ + hashkey = access->entrykey( callentry ) & table->hashmask; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Lock first page */ + pagestart = hashkey >> table->pageshift; + pagefinal = pagestart; + if( !( MM_HASH_LOCK_TRY_WRITE( table, pagestart ) ) ) + return MM_HASH_TRYAGAIN; + + /* Search an available entry */ + retvalue = MM_HASH_SUCCESS; + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + /* Lock new pages */ + pageindex = hashkey >> table->pageshift; + if( pageindex != pagefinal ) + { + if( !( MM_HASH_LOCK_TRY_WRITE( table, pageindex ) ) ) + { + retvalue = MM_HASH_TRYAGAIN; + goto end; + } + pagefinal = pageindex; + } + + /* Check for entry available */ + entry = MM_HASH_ENTRY( table, hashkey ); + cmpvalue = access->entrycmp( entry, callentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + { + callback( opaque, entry, 0 ); + retvalue = MM_HASH_SUCCESS; + goto end; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + if( !( addflag ) ) + { + retvalue = MM_HASH_FAILURE; + goto end; + } + + /* Store new entry */ + memcpy( entry, callentry, table->entrysize ); + callback( opaque, entry, 1 ); + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + end: + + /* Unlock all pages */ + for( pageindex = pagestart ; ; pageindex = ( pageindex + 1 ) & table->pagemask ) + { + MM_HASH_LOCK_DONE_WRITE( table, pageindex ); + if( pageindex == pagefinal ) + break; + } + + return retvalue; +} + + +int mmHashLockCallEntry( void *hashtable, const mmHashAccess *access, void *callentry, void (*callback)( void *opaque, void *entry, int newflag ), void *opaque, int addflag ) +{ + int retvalue; + mmHashTable *table; + + table = hashtable; + retvalue = mmHashTryCallEntry( table, access, callentry, callback, opaque, addflag ); + if( retvalue == MM_HASH_TRYAGAIN ) + { + MM_HASH_GLOBAL_LOCK( table ); + do + { + retvalue = mmHashTryCallEntry( table, access, callentry, callback, opaque, addflag ); + } while( retvalue == MM_HASH_TRYAGAIN ); + MM_HASH_GLOBAL_UNLOCK( table ); + } + + return retvalue; +} + + + +//// + + + +int mmHashDirectReplaceEntry( void *hashtable, const mmHashAccess *access, void *replaceentry, int addflag ) +{ + int cmpvalue; + uint32_t hashkey, entrycount; + void *entry; + mmHashTable *table; + + table = hashtable; + + /* Hash key of entry */ + hashkey = access->entrykey( replaceentry ) & table->hashmask; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Search an available entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + entry = MM_HASH_ENTRY( table, hashkey ); + cmpvalue = access->entrycmp( entry, replaceentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + { + memcpy( entry, replaceentry, table->entrysize ); + return MM_HASH_SUCCESS; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + if( !( addflag ) ) + return MM_HASH_FAILURE; + + /* Store new entry */ + memcpy( entry, replaceentry, table->entrysize ); + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + return MM_HASH_SUCCESS; +} + + +static int mmHashTryReplaceEntry( mmHashTable *table, const mmHashAccess *access, void *replaceentry, int addflag ) +{ + uint32_t hashkey, entrycount; + uint32_t pageindex, pagestart, pagefinal; + int cmpvalue, retvalue; + void *entry; + + /* Hash key of entry */ + hashkey = access->entrykey( replaceentry ) & table->hashmask; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Lock first page */ + pagestart = hashkey >> table->pageshift; + pagefinal = pagestart; + if( !( MM_HASH_LOCK_TRY_WRITE( table, pagestart ) ) ) + return MM_HASH_TRYAGAIN; + + /* Search an available entry */ + retvalue = MM_HASH_SUCCESS; + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + /* Lock new pages */ + pageindex = hashkey >> table->pageshift; + if( pageindex != pagefinal ) + { + if( !( MM_HASH_LOCK_TRY_WRITE( table, pageindex ) ) ) + { + retvalue = MM_HASH_TRYAGAIN; + goto end; + } + pagefinal = pageindex; + } + + /* Check for entry available */ + entry = MM_HASH_ENTRY( table, hashkey ); + cmpvalue = access->entrycmp( entry, replaceentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + { + memcpy( entry, replaceentry, table->entrysize ); + retvalue = MM_HASH_SUCCESS; + goto end; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + if( !( addflag ) ) + { + retvalue = MM_HASH_FAILURE; + goto end; + } + + /* Store new entry */ + memcpy( entry, replaceentry, table->entrysize ); + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + end: + + /* Unlock all pages */ + for( pageindex = pagestart ; ; pageindex = ( pageindex + 1 ) & table->pagemask ) + { + MM_HASH_LOCK_DONE_WRITE( table, pageindex ); + if( pageindex == pagefinal ) + break; + } + + return retvalue; +} + + +int mmHashLockReplaceEntry( void *hashtable, const mmHashAccess *access, void *replaceentry, int addflag ) +{ + int retvalue; + mmHashTable *table; + + table = hashtable; + retvalue = mmHashTryReplaceEntry( table, access, replaceentry, addflag ); + if( retvalue == MM_HASH_TRYAGAIN ) + { + MM_HASH_GLOBAL_LOCK( table ); + do + { + retvalue = mmHashTryReplaceEntry( table, access, replaceentry, addflag ); + } while( retvalue == MM_HASH_TRYAGAIN ); + MM_HASH_GLOBAL_UNLOCK( table ); + } + + return retvalue; +} + + + +//// + + + +int mmHashDirectAddEntry( void *hashtable, const mmHashAccess *access, void *addentry, int nodupflag ) +{ + int cmpvalue; + uint32_t hashkey, entrycount; + void *entry; + mmHashTable *table; + + table = hashtable; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = access->entrykey( addentry ) & table->hashmask; + + /* Search an available entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + entry = MM_HASH_ENTRY( table, hashkey ); + /* Do we allow duplicate entries? */ + if( nodupflag ) + { + cmpvalue = access->entrycmp( entry, addentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + return MM_HASH_FAILURE; + } + else + { + if( !( access->entryvalid( entry ) ) ) + break; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + /* Store new entry */ + memcpy( entry, addentry, table->entrysize ); + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + return MM_HASH_SUCCESS; +} + +int mmHashDirectAddEntry2( void *hashtable, const mmHashAccess *access, void *addentry, int nodupflag, void ** retEntry) +{ + int cmpvalue; + uint32_t hashkey, entrycount; + void *entry; + mmHashTable *table; + + table = hashtable; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = access->entrykey( addentry ) & table->hashmask; + + /* Search an available entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + entry = MM_HASH_ENTRY( table, hashkey ); + /* Do we allow duplicate entries? */ + if( nodupflag ) + { + cmpvalue = access->entrycmp( entry, addentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + { + if(retEntry) *retEntry = entry; + return MM_HASH_FOUND; + } + } + else + { + if( !( access->entryvalid( entry ) ) ) + break; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + /* Store new entry */ + memcpy( entry, addentry, table->entrysize ); + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + if(retEntry) *retEntry = entry; + return MM_HASH_SUCCESS; +} + +static int mmHashTryAddEntry( mmHashTable *table, const mmHashAccess *access, void *addentry, int nodupflag ) +{ + uint32_t hashkey, entrycount; + uint32_t pageindex, pagestart, pagefinal; + int cmpvalue, retvalue; + void *entry; + + /* Hash key of entry */ + hashkey = access->entrykey( addentry ) & table->hashmask; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Lock first page */ + pagestart = hashkey >> table->pageshift; + pagefinal = pagestart; + if( !( MM_HASH_LOCK_TRY_WRITE( table, pagestart ) ) ) + return MM_HASH_TRYAGAIN; + + /* Search an available entry */ + retvalue = MM_HASH_SUCCESS; + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + /* Lock new pages */ + pageindex = hashkey >> table->pageshift; + if( pageindex != pagefinal ) + { + if( !( MM_HASH_LOCK_TRY_WRITE( table, pageindex ) ) ) + { + retvalue = MM_HASH_TRYAGAIN; + goto end; + } + pagefinal = pageindex; + } + + /* Check for entry available */ + entry = MM_HASH_ENTRY( table, hashkey ); + /* Do we allow duplicate entries? */ + if( nodupflag ) + { + cmpvalue = access->entrycmp( entry, addentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + { + retvalue = MM_HASH_FAILURE; + goto end; + } + } + else + { + if( !( access->entryvalid( entry ) ) ) + break; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + /* Store new entry */ + memcpy( entry, addentry, table->entrysize ); + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + end: + + /* Unlock all pages */ + for( pageindex = pagestart ; ; pageindex = ( pageindex + 1 ) & table->pagemask ) + { + MM_HASH_LOCK_DONE_WRITE( table, pageindex ); + if( pageindex == pagefinal ) + break; + } + + return retvalue; +} + + + +int mmHashLockAddEntry( void *hashtable, const mmHashAccess *access, void *addentry, int nodupflag ) +{ + int retvalue; + mmHashTable *table; + + table = hashtable; + retvalue = mmHashTryAddEntry( table, access, addentry, nodupflag ); + if( retvalue == MM_HASH_TRYAGAIN ) + { + MM_HASH_GLOBAL_LOCK( table ); + do + { + retvalue = mmHashTryAddEntry( table, access, addentry, nodupflag ); + } while( retvalue == MM_HASH_TRYAGAIN ); + MM_HASH_GLOBAL_UNLOCK( table ); + } + + return retvalue; +} + + + +//// + + + +int mmHashDirectReadOrAddEntry( void *hashtable, const mmHashAccess *access, void *readaddentry, int *retreadflag ) +{ + int cmpvalue; + uint32_t hashkey, entrycount; + void *entry; + mmHashTable *table; + + table = hashtable; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = access->entrykey( readaddentry ) & table->hashmask; + + /* Search an available entry */ + *retreadflag = 0; + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + entry = MM_HASH_ENTRY( table, hashkey ); + cmpvalue = access->entrycmp( entry, readaddentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + { + memcpy( readaddentry, entry, table->entrysize ); + *retreadflag = 1; + return MM_HASH_SUCCESS; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + /* Store new entry */ + memcpy( entry, readaddentry, table->entrysize ); + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + return MM_HASH_SUCCESS; +} + + +static int mmHashTryReadOrAddEntry( mmHashTable *table, const mmHashAccess *access, void *readaddentry, int *retreadflag ) +{ + uint32_t hashkey, entrycount; + uint32_t pageindex, pagestart, pagefinal; + int cmpvalue, retvalue; + void *entry; + + /* Hash key of entry */ + hashkey = access->entrykey( readaddentry ) & table->hashmask; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Lock first page */ + pagestart = hashkey >> table->pageshift; + pagefinal = pagestart; + if( !( MM_HASH_LOCK_TRY_WRITE( table, pagestart ) ) ) + return MM_HASH_TRYAGAIN; + + /* Search an available entry */ + retvalue = MM_HASH_SUCCESS; + *retreadflag = 0; + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + /* Lock new pages */ + pageindex = hashkey >> table->pageshift; + if( pageindex != pagefinal ) + { + if( !( MM_HASH_LOCK_TRY_WRITE( table, pageindex ) ) ) + { + retvalue = MM_HASH_TRYAGAIN; + goto end; + } + pagefinal = pageindex; + } + + /* Check for entry available */ + entry = MM_HASH_ENTRY( table, hashkey ); + + cmpvalue = access->entrycmp( entry, readaddentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + { + memcpy( readaddentry, entry, table->entrysize ); + *retreadflag = 1; + goto end; + } + + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + /* Store new entry */ + memcpy( entry, readaddentry, table->entrysize ); + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + end: + + /* Unlock all pages */ + for( pageindex = pagestart ; ; pageindex = ( pageindex + 1 ) & table->pagemask ) + { + MM_HASH_LOCK_DONE_WRITE( table, pageindex ); + if( pageindex == pagefinal ) + break; + } + + return retvalue; +} + + + +int mmHashLockReadOrAddEntry( void *hashtable, const mmHashAccess *access, void *readaddentry, int *retreadflag ) +{ + int retvalue; + mmHashTable *table; + + table = hashtable; + retvalue = mmHashTryReadOrAddEntry( table, access, readaddentry, retreadflag ); + if( retvalue == MM_HASH_TRYAGAIN ) + { + MM_HASH_GLOBAL_LOCK( table ); + do + { + retvalue = mmHashTryReadOrAddEntry( table, access, readaddentry, retreadflag ); + } while( retvalue == MM_HASH_TRYAGAIN ); + MM_HASH_GLOBAL_UNLOCK( table ); + } + + return retvalue; +} + + + +//// + + + +#define MM_ROBUST_DELETION + + +int mmHashDirectDeleteEntry( void *hashtable, const mmHashAccess *access, void *deleteentry, int readflag ) +{ + int cmpvalue; + uint32_t hashkey, srckey, srcpos, targetpos, targetkey, entrycount; +#ifdef MM_ROBUST_DELETION + uint32_t delbase; +#else + int32_t halftablesize, distance; +#endif + void *entry, *srcentry, *targetentry; + mmHashTable *table = hashtable; + size_t entrysize = table->entrysize; + void (*clearentry)( void *entry ) = access->clearentry; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = access->entrykey( deleteentry ) & table->hashmask; + + /* Search the entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + entry = MM_HASH_ENTRY( table, hashkey ); + cmpvalue = access->entrycmp( entry, deleteentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + return MM_HASH_FAILURE; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + break; +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + if( readflag ) + memcpy( deleteentry, entry, table->entrysize ); + +#ifdef MM_ROBUST_DELETION + for( delbase = hashkey ; ; ) + { + delbase = ( delbase - 1 ) & table->hashmask; + if( !( access->entryvalid( MM_HASH_ENTRY( table, delbase ) ) ) ) + break; + } + delbase = ( delbase + 1 ) & table->hashmask; +#else + halftablesize = table->hashsize >> 1; +#endif + + /* Entry found, delete it */ + for( ; ; ) + { + /* Find replacement target */ + targetkey = hashkey; + targetentry = 0; + for( srcpos = hashkey ; ; ) + { + srcpos = ( srcpos + 1 ) & table->hashmask; + + /* Try next entry */ + srcentry = MM_HASH_ENTRY( table, srcpos ); + if( !( access->entryvalid( srcentry ) ) ) + break; + srckey = access->entrykey( srcentry ) & table->hashmask; + +#ifdef MM_ROBUST_DELETION + if( targetkey >= delbase ) + { + if( ( srckey < delbase ) || ( srckey > targetkey ) ) + continue; + } + else if( ( srckey > targetkey ) && ( srckey < delbase ) ) + continue; + targetentry = srcentry; + targetkey = srckey; + targetpos = srcpos; +#else + /* Check if moving the entry backwards is allowed without breaking chain */ + distance = (int32_t)targetkey - (int32_t)srckey; + if( distance > halftablesize ) + distance -= table->hashsize; + else if( distance < -halftablesize ) + distance += table->hashsize; + if( distance >= 0 ) + { + targetentry = srcentry; + targetkey = srckey; + targetpos = srcpos; + } +#endif + } + + /* No replacement found, just clear it */ + entry = MM_HASH_ENTRY( table, hashkey ); + if( !( targetentry ) ) + { + if(clearentry) + clearentry( entry ); + else + memset(entry, 0, entrysize); + break; + } + + /* Move entry in place and continue the repair process */ + memcpy( entry, targetentry, table->entrysize ); + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->relocationcount, 1 ); +#endif + + hashkey = targetpos; + } + + /* Decrement count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, -1 ); + if( entrycount < table->lowcount ) + table->status = MM_HASH_STATUS_MUSTSHRINK; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->statentrycount, -1 ); +#endif + + return MM_HASH_SUCCESS; +} + +// WARNING: hen setting dontMoveStuff, a resize must be done after to re-pack! +int mmHashDirectDeleteEntry2( void *hashtable, const mmHashAccess *access, void *entry, int dontMoveStuff) +{ + uint32_t hashkey, srckey, srcpos, targetpos, targetkey, entrycount; +#ifdef MM_ROBUST_DELETION + uint32_t delbase; +#else + int32_t halftablesize, distance; +#endif + void *srcentry, *targetentry; + mmHashTable *table = hashtable; + size_t entrysize = table->entrysize; + void (*clearentry)( void *entry ) = access->clearentry; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = MM_HASH_HASHKEY(table, entry); + +#ifdef MM_ROBUST_DELETION + for( delbase = hashkey ; ; ) + { + delbase = ( delbase - 1 ) & table->hashmask; + if( !( access->entryvalid( MM_HASH_ENTRY( table, delbase ) ) ) ) + break; + } + delbase = ( delbase + 1 ) & table->hashmask; +#else + halftablesize = table->hashsize >> 1; +#endif + + /* Entry found, delete it */ + for( ; ; ) + { + targetentry = 0; + if(dontMoveStuff) + targetpos = hashkey; + else + { + /* Find replacement target */ + targetkey = hashkey; + for( srcpos = hashkey ; ; ) + { + srcpos = ( srcpos + 1 ) & table->hashmask; + + /* Try next entry */ + srcentry = MM_HASH_ENTRY( table, srcpos ); + if( !( access->entryvalid( srcentry ) ) ) + break; + srckey = access->entrykey( srcentry ) & table->hashmask; + + #ifdef MM_ROBUST_DELETION + if( targetkey >= delbase ) + { + if( ( srckey < delbase ) || ( srckey > targetkey ) ) + continue; + } + else if( ( srckey > targetkey ) && ( srckey < delbase ) ) + continue; + targetentry = srcentry; + targetkey = srckey; + targetpos = srcpos; + #else + /* Check if moving the entry backwards is allowed without breaking chain */ + distance = (int32_t)targetkey - (int32_t)srckey; + if( distance > halftablesize ) + distance -= table->hashsize; + else if( distance < -halftablesize ) + distance += table->hashsize; + if( distance >= 0 ) + { + targetentry = srcentry; + targetkey = srckey; + targetpos = srcpos; + } + #endif + } + } + + /* No replacement found, just clear it */ + entry = MM_HASH_ENTRY( table, hashkey ); + if( !( targetentry ) ) + { + if(clearentry) + clearentry( entry ); + else + memset(entry, 0, entrysize); + break; + } + + /* Move entry in place and continue the repair process */ + memcpy( entry, targetentry, entrysize ); + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->relocationcount, 1 ); +#endif + + hashkey = targetpos; + } + + /* Decrement count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, -1 ); + if( entrycount < table->lowcount ) + table->status = MM_HASH_STATUS_MUSTSHRINK; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->statentrycount, -1 ); +#endif + + return MM_HASH_SUCCESS; +} + +static int mmHashTryDeleteEntry( mmHashTable *table, const mmHashAccess *access, void *deleteentry, int readflag ) +{ + uint32_t hashkey, srckey, srcpos, srcend, targetpos, targetkey, entrycount; + uint32_t pageindex, pagestart, pagefinal; +#ifdef MM_ROBUST_DELETION + uint32_t delbase; +#else + int32_t halftablesize, distance; +#endif + int cmpvalue, retvalue; + void *entry, *srcentry, *targetentry; + size_t entrysize = table->entrysize; + void (*clearentry)( void *entry ) = access->clearentry; + + /* Hash key of entry */ + hashkey = access->entrykey( deleteentry ) & table->hashmask; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Lock first page */ + pagestart = hashkey >> table->pageshift; + pagefinal = pagestart; + if( !( MM_HASH_LOCK_TRY_WRITE( table, pagestart ) ) ) + return MM_HASH_TRYAGAIN; + + /* Search the entry */ + retvalue = MM_HASH_SUCCESS; + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { + /* Lock new pages */ + pageindex = hashkey >> table->pageshift; + if( pageindex != pagefinal ) + { + if( !( MM_HASH_LOCK_TRY_WRITE( table, pageindex ) ) ) + { + retvalue = MM_HASH_TRYAGAIN; + goto end; + } + pagefinal = pageindex; + } + + /* Check for entry match */ + entry = MM_HASH_ENTRY( table, hashkey ); + cmpvalue = access->entrycmp( entry, deleteentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + { + retvalue = MM_HASH_FAILURE; + goto end; + } + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + break; +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + if( readflag ) + memcpy( deleteentry, entry, table->entrysize ); + +#ifdef MM_ROBUST_DELETION + for( delbase = hashkey ; ; ) + { + delbase = ( delbase - 1 ) & table->hashmask; + if( !( access->entryvalid( MM_HASH_ENTRY( table, delbase ) ) ) ) + break; + } + delbase = ( delbase + 1 ) & table->hashmask; +#else + halftablesize = table->hashsize >> 1; +#endif + + /* Preemtively lock all pages in the stream before starting the operation */ + for( srcend = hashkey ; ; ) + { + srcend = ( srcend + 1 ) & table->hashmask; + + /* Lock new pages */ + pageindex = srcend >> table->pageshift; + if( pageindex != pagefinal ) + { + if( !( MM_HASH_LOCK_TRY_WRITE( table, pageindex ) ) ) + { + retvalue = MM_HASH_TRYAGAIN; + goto end; + } + pagefinal = pageindex; + } + + /* Check for valid entry */ + srcentry = MM_HASH_ENTRY( table, srcend ); + if( !( access->entryvalid( srcentry ) ) ) + break; + } + + /* Entry found, delete it and reorder the hash stream of entries */ + for( ; ; ) + { + /* Find replacement target */ + targetkey = hashkey; + targetentry = 0; + for( srcpos = hashkey ; ; ) + { + srcpos = ( srcpos + 1 ) & table->hashmask; + srcentry = MM_HASH_ENTRY( table, srcpos ); + + /* Don't loop beyond the end of hash stream */ + if( srcpos == srcend ) + break; + + /* Try next entry */ + srckey = access->entrykey( srcentry ) & table->hashmask; + +#ifdef MM_ROBUST_DELETION + if( targetkey >= delbase ) + { + if( ( srckey < delbase ) || ( srckey > targetkey ) ) + continue; + } + else if( ( srckey > targetkey ) && ( srckey < delbase ) ) + continue; + targetentry = srcentry; + targetkey = srckey; + targetpos = srcpos; +#else + /* Check if moving the entry backwards is allowed without breaking chain */ + distance = (int32_t)targetkey - (int32_t)srckey; + if( distance > halftablesize ) + distance -= table->hashsize; + else if( distance < -halftablesize ) + distance += table->hashsize; + if( distance >= 0 ) + { + targetentry = srcentry; + targetkey = srckey; + targetpos = srcpos; + } +#endif + } + + /* No replacement found, just clear it */ + entry = MM_HASH_ENTRY( table, hashkey ); + if( !( targetentry ) ) + { + if(clearentry) + clearentry( entry ); + else + memset(entry, 0, entrysize); + break; + } + + /* Move entry in place and continue the repair process */ + memcpy( entry, targetentry, entrysize ); + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->relocationcount, 1 ); +#endif + + hashkey = targetpos; + } + + /* Decrement count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, -1 ); + if( entrycount < table->lowcount ) + table->status = MM_HASH_STATUS_MUSTSHRINK; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->statentrycount, -1 ); +#endif + + end: + + /* Unlock all pages */ + for( pageindex = pagestart ; ; pageindex = ( pageindex + 1 ) & table->pagemask ) + { + MM_HASH_LOCK_DONE_WRITE( table, pageindex ); + if( pageindex == pagefinal ) + break; + } + + return retvalue; +} + + +int mmHashLockDeleteEntry( void *hashtable, const mmHashAccess *access, void *deleteentry, int readflag ) +{ + int retvalue; + mmHashTable *table; + + table = hashtable; + retvalue = mmHashTryDeleteEntry( table, access, deleteentry, readflag ); + if( retvalue == MM_HASH_TRYAGAIN ) + { + MM_HASH_GLOBAL_LOCK( table ); + do + { + retvalue = mmHashTryDeleteEntry( table, access, deleteentry, readflag ); + } while( retvalue == MM_HASH_TRYAGAIN ); + MM_HASH_GLOBAL_UNLOCK( table ); + } + + return retvalue; +} + + + +//// + + + +/* Must be called while NO other thread will ever access the table for writing */ +void mmHashResize( void *newtable, void *oldtable, const mmHashAccess *access, uint32_t hashbits, uint32_t pageshift ) +{ + uint32_t hashkey, hashpos, dstkey, dstpos, pageindex; + void *srcentry, *dstentry; + mmHashTable *dst, *src; + mmHashPage *page; + void (*clearentry)( void *entry ) = access->clearentry; + + dst = newtable; + src = oldtable; + dst->status = MM_HASH_STATUS_NORMAL; + dst->flags = src->flags; + dst->entrysize = src->entrysize; + dst->minhashbits = src->minhashbits; + dst->hashbits = hashbits; + dst->hashsize = 1 << dst->hashbits; + dst->hashmask = dst->hashsize - 1; + dst->pageshift = pageshift; + dst->pagecount = dst->hashsize >> pageshift; + dst->pagemask = dst->pagecount - 1; + dst->page = MM_HASH_PAGELIST( dst ); +#ifdef MM_ATOMIC_SUPPORT + mmAtomicWrite32( &dst->entrycount, mmAtomicRead32( &src->entrycount ) ); +#else + dst->entrycount = src->entrycount; +#endif + mmHashSetBounds( dst ); + + /* Clear the whole destination table */ + dstentry = MM_HASH_ENTRYLIST( dst ); + if(clearentry) + { + for( hashpos = 0 ; hashpos < dst->hashsize ; hashpos++ ) + { + clearentry( dstentry ); + dstentry = ADDRESS( dstentry, dst->entrysize ); + } + } + else + memset(dstentry, 0, dst->hashsize * dst->entrysize); + + /* Clear the lock pages */ +#ifdef MM_ATOMIC_SUPPORT + page = dst->page; + for( pageindex = 0 ; pageindex < dst->pagecount ; pageindex++, page++ ) + { + mmAtomicLockInit32( &page->lock ); + mmAtomicWriteP( &page->owner, (void *)0 ); + } + mmAtomicWrite32( &dst->globallock, 0x0 ); +#else + page = src->page; + for( pageindex = src->pagecount ; pageindex ; pageindex--, page++ ) + mtMutexDestroy( &page->mutex ); + mtMutexDestroy( &src->globalmutex ); + mtMutexDestroy( &src->countmutex ); + + page = dst->page; + for( pageindex = dst->pagecount ; pageindex ; pageindex--, page++ ) + { + mtMutexInit( &page->mutex ); + page->owner = 0; + } + mtMutexInit( &dst->globalmutex ); + mtMutexInit( &dst->countmutex ); +#endif + + /* Move all entries from the src table to the dst table */ + srcentry = MM_HASH_ENTRYLIST( src ); + + for( hashpos = 0 ; hashpos < src->hashsize ; hashpos++ ) + { + { + hashkey = access->entryvalid( srcentry ) ? access->entrykey( srcentry ) : 0; + dstkey = hashkey & dst->hashmask; + + /* Search an empty spot in the destination table */ + for( dstpos = dstkey ; ; dstpos = ( dstpos + 1 ) & dst->hashmask ) + { + dstentry = MM_HASH_ENTRY( dst, dstpos ); + if( !( access->entryvalid( dstentry ) ) ) + break; + } + + /* Copy entry from src table to dst table */ + memcpy( dstentry, srcentry, src->entrysize ); + srcentry = ADDRESS( srcentry, src->entrysize ); + } + } + return; +} + + +void mmHashResize2( void *newtable, void *oldtable, const mmHashAccess *access, uint32_t hashbits, uint32_t pageshift, void ** movedEntryPtr) +{ + uint32_t hashkey, hashpos, dstkey, dstpos, pageindex; + void *srcentry, *dstentry; + void *movedentry = movedEntryPtr ? *movedEntryPtr : NULL; + mmHashTable *dst, *src; + mmHashPage *page; + void (*clearentry)( void *entry ) = access->clearentry; + + dst = newtable; + src = oldtable; + dst->status = MM_HASH_STATUS_NORMAL; + dst->flags = src->flags; + dst->entrysize = src->entrysize; + dst->minhashbits = src->minhashbits; + dst->hashbits = hashbits; + dst->hashsize = 1 << dst->hashbits; + dst->hashmask = dst->hashsize - 1; + dst->pageshift = pageshift; + dst->pagecount = dst->hashsize >> pageshift; + dst->pagemask = dst->pagecount - 1; + dst->page = MM_HASH_PAGELIST( dst ); +#ifdef MM_ATOMIC_SUPPORT + mmAtomicWrite32( &dst->entrycount, mmAtomicRead32( &src->entrycount ) ); +#else + dst->entrycount = src->entrycount; +#endif + mmHashSetBounds( dst ); + + /* Clear the whole destination table */ + dstentry = MM_HASH_ENTRYLIST( dst ); + if(clearentry) + { + for( hashpos = 0 ; hashpos < dst->hashsize ; hashpos++ ) + { + clearentry( dstentry ); + dstentry = ADDRESS( dstentry, dst->entrysize ); + } + } + else + memset(dstentry, 0, dst->hashsize * dst->entrysize); + + /* Clear the lock pages */ +#ifdef MM_ATOMIC_SUPPORT + page = dst->page; + for( pageindex = 0 ; pageindex < dst->pagecount ; pageindex++, page++ ) + { + mmAtomicLockInit32( &page->lock ); + mmAtomicWriteP( &page->owner, (void *)0 ); + } + mmAtomicWrite32( &dst->globallock, 0x0 ); +#else + page = src->page; + for( pageindex = src->pagecount ; pageindex ; pageindex--, page++ ) + mtMutexDestroy( &page->mutex ); + mtMutexDestroy( &src->globalmutex ); + mtMutexDestroy( &src->countmutex ); + + page = dst->page; + for( pageindex = dst->pagecount ; pageindex ; pageindex--, page++ ) + { + mtMutexInit( &page->mutex ); + page->owner = 0; + } + mtMutexInit( &dst->globalmutex ); + mtMutexInit( &dst->countmutex ); +#endif + + /* Move all entries from the src table to the dst table */ + srcentry = MM_HASH_ENTRYLIST( src ); + for( hashpos = 0 ; hashpos < src->hashsize ; hashpos++ ) + { + { + hashkey = access->entryvalid( srcentry ) ? access->entrykey( srcentry ) : 0; + dstkey = hashkey & dst->hashmask; + + /* Search an empty spot in the destination table */ + for( dstpos = dstkey ; ; dstpos = ( dstpos + 1 ) & dst->hashmask ) + { + dstentry = MM_HASH_ENTRY( dst, dstpos ); + if( !( access->entryvalid( dstentry ) ) ) + break; + } + + if(srcentry == movedentry) + *movedEntryPtr = dstentry; + + /* Copy entry from src table to dst table */ + memcpy( dstentry, srcentry, src->entrysize ); + srcentry = ADDRESS( srcentry, src->entrysize ); + } + } + + return; +} + + + +/* Must be called while NO other thread will ever access the table for writing */ +void mmHashListAll( void *hashtable, int (*list)( void *opaque, void *entry ), void *opaque ) +{ + uint32_t hashkey; + size_t entrysize; + void *entry; + mmHashTable *table; + table = hashtable; + entrysize = table->entrysize; + entry = MM_HASH_ENTRYLIST( table ); + for( hashkey = 0 ; hashkey < table->hashsize ; hashkey++ ) + { + if( !list( opaque, entry ) ) + break; + entry = ADDRESS( entry, entrysize ); + } + return; +} + + +//// + + +static int mmHashLockRangeTry( mmHashTable *table, const mmHashAccess *access, mmHashLock *hashlock, mmHashLockRange *lockrange, uint32_t hashkey ) +{ + int newcount; + uint32_t srckey; + uint32_t pageindex, pagestart, pagefinal; + void *srcentry; + + /* Lock first page */ + pagestart = hashkey >> table->pageshift; + pagefinal = pagestart; +#ifdef MM_ATOMIC_SUPPORT + if( mmAtomicReadP( &table->page[pagestart].owner ) != (void *)hashlock ) + { + if( !( MM_HASH_LOCK_TRY_WRITE( table, pagestart ) ) ) + return MM_HASH_TRYAGAIN; + } + mmAtomicWriteP( &table->page[pagestart].owner, (void *)hashlock ); +#else + if( table->page[pagestart].owner != (void *)hashlock ) + { + if( !( MM_HASH_LOCK_TRY_WRITE( table, pagestart ) ) ) + return MM_HASH_TRYAGAIN; + } + table->page[pagestart].owner = hashlock; +#endif + + /* Lock all pages in stream */ + newcount = hashlock->newcount; + for( srckey = hashkey ; ; ) + { + srckey = ( srckey + 1 ) & table->hashmask; + + /* Lock new pages */ + pageindex = srckey >> table->pageshift; + if( pageindex != pagefinal ) + { +#ifdef MM_ATOMIC_SUPPORT + if( mmAtomicReadP( &table->page[pageindex].owner ) != (void *)hashlock ) + { + if( MM_HASH_LOCK_TRY_WRITE( table, pageindex ) ) + { + mmAtomicWriteP( &table->page[pageindex].owner, (void *)hashlock ); + continue; + } + /* Failed, unlock all pages */ + for( pageindex = pagestart ; ; pageindex = ( pageindex + 1 ) & table->pagemask ) + { + mmAtomicWriteP( &table->page[pageindex].owner, (void *)0 ); + MM_HASH_LOCK_DONE_WRITE( table, pageindex ); + if( pageindex == pagefinal ) + break; + } + return MM_HASH_TRYAGAIN; + } +#else + if( table->page[pageindex].owner != (void *)hashlock ) + { + if( MM_HASH_LOCK_TRY_WRITE( table, pageindex ) ) + { + table->page[pageindex].owner = (void *)hashlock; + continue; + } + /* Failed, unlock all pages */ + for( pageindex = pagestart ; ; pageindex = ( pageindex + 1 ) & table->pagemask ) + { + table->page[pageindex].owner = 0; + MM_HASH_LOCK_DONE_WRITE( table, pageindex ); + if( pageindex == pagefinal ) + break; + } + return MM_HASH_TRYAGAIN; + } +#endif + pagefinal = pageindex; + } + + /* Check for valid entry */ + srcentry = MM_HASH_ENTRY( table, srckey ); + if( !( access->entryvalid( srcentry ) ) ) + { + if( --newcount <= 0 ) + break; + } + } + + /* Store lock pagestart and pagefinal */ + lockrange->pagestart = pagestart; + lockrange->pagefinal = pagefinal; + + return MM_HASH_SUCCESS; +} + + +static void mmHashLockReleaseAll( mmHashTable *table, mmHashLock *hashlock ) +{ + uint32_t pageindex, pagestart, pagefinal; + mmHashLockRange *lockrange; + + for( lockrange = hashlock->rangelist ; lockrange ; lockrange = lockrange->next ) + { + if( lockrange->pagestart == ~(uint32_t)0x0 ) + continue; + pagestart = lockrange->pagestart; + pagefinal = lockrange->pagefinal; + for( pageindex = pagestart ; ; pageindex = ( pageindex + 1 ) & table->pagemask ) + { +#ifdef MM_ATOMIC_SUPPORT + if( mmAtomicReadP( &table->page[pageindex].owner ) == (void *)hashlock ) + { + mmAtomicWriteP( &table->page[pageindex].owner, (void *)0 ); + mmAtomicLockDoneWrite32( &table->page[pageindex].lock ); + } +#else + if( table->page[pageindex].owner == (void *)hashlock ) + { + table->page[pageindex].owner = (void *)0; + MM_HASH_LOCK_DONE_WRITE( table, pageindex ); + } +#endif + if( pageindex == pagefinal ) + break; + } + } + + for( lockrange = hashlock->rangelist ; lockrange ; lockrange = lockrange->next ) + { + lockrange->pagestart = ~0x0; + lockrange->pagefinal = ~0x0; + } + + return; +} + + +static int mmHashLockApplyAll( mmHashTable *table, const mmHashAccess *access, mmHashLock *hashlock ) +{ + mmHashLockRange *lockrange, *lockrangenext; + + for( lockrange = hashlock->rangelist ; lockrange ; lockrange = lockrangenext ) + { + lockrangenext = lockrange->next; + if( mmHashLockRangeTry( table, access, hashlock, lockrange, lockrange->hashkey ) == MM_HASH_TRYAGAIN ) + { + /* Failure, release all locks */ + mmHashLockReleaseAll( table, hashlock ); + return MM_HASH_TRYAGAIN; + } + } + + return MM_HASH_SUCCESS; +} + + + +//// + + + +void mmHashLockInit( mmHashLock *hashlock, int newcount ) +{ + memset( hashlock, 0, sizeof(mmHashLock) ); + hashlock->newcount = newcount; + return; +} + +void mmHashLockAdd( void *hashtable, const mmHashAccess *access, void *entry, mmHashLock *hashlock, mmHashLockRange *lockrange ) +{ + uint32_t hashkey; + mmHashTable *table; + + /* Hash key of entry */ + table = hashtable; + hashkey = access->entrykey( entry ) & table->hashmask; + + lockrange->hashkey = hashkey; + lockrange->pagestart = ~0x0; + lockrange->pagefinal = ~0x0; + lockrange->next = hashlock->rangelist; + hashlock->rangelist = lockrange; + + return; +} + +void mmHashLockAcquire( void *hashtable, const mmHashAccess *access, mmHashLock *hashlock ) +{ + int status; + mmHashTable *table; + + table = hashtable; + status = mmHashLockApplyAll( table, access, hashlock ); + if( status == MM_HASH_TRYAGAIN ) + { + MM_HASH_GLOBAL_LOCK( table ); + do + { + status = mmHashLockApplyAll( table, access, hashlock ); + } while( status == MM_HASH_TRYAGAIN ); + MM_HASH_GLOBAL_UNLOCK( table ); + } + + return; +} + +void mmHashLockRelease( void *hashtable, mmHashLock *hashlock ) +{ + mmHashTable *table; + + table = hashtable; + mmHashLockReleaseAll( table, hashlock ); + + return; +} + + +//// + + +void mmHashGlobalLockEnable( void *hashtable ) +{ + uint32_t pageindex; + mmHashTable *table; + + table = hashtable; + MM_HASH_GLOBAL_LOCK( table ); + for( pageindex = 0 ; pageindex < table->pagecount ; pageindex++ ) + MM_HASH_LOCK_TRY_WRITE( table, pageindex ); + + return; +} + + +void mmHashGlobalLockDisable( void *hashtable ) +{ + uint32_t pageindex; + mmHashTable *table; + + table = hashtable; + for( pageindex = 0 ; pageindex < table->pagecount ; pageindex++ ) + MM_HASH_LOCK_DONE_WRITE( table, pageindex ); + MM_HASH_GLOBAL_UNLOCK( table ); + + return; +} + + +//// + + + +void mmHashDirectDebugDuplicate( void *hashtable, const mmHashAccess *access, void (*callback)( void *opaque, void *entry0, void *entry1 ), void *opaque ) +{ + uint32_t hashbase, hashkey; + void *baseentry, *entry; + mmHashTable *table; + + table = hashtable; + + for( hashbase = 0 ; hashbase < table->hashsize ; hashbase++ ) + { + baseentry = MM_HASH_ENTRY( table, hashbase ); + for( hashkey = hashbase ; ; ) + { + hashkey = ( hashkey + 1 ) & table->hashmask; + entry = MM_HASH_ENTRY( table, hashkey ); + if( !( access->entryvalid( entry ) ) ) + break; + if( access->entrycmp( entry, baseentry ) ) + callback( opaque, entry, baseentry ); + } + } + + return; +} + + +void mmHashDirectDebugPages( void *hashtable ) +{ +#ifdef MM_ATOMIC_SUPPORT + mmHashTable *table = hashtable; + uint32_t pageindex; + mmHashPage *page = table->page; + for( pageindex = 0 ; pageindex < table->pagecount ; pageindex++, page++ ) + { + if( ( page->lock.v.value ) || mmAtomicReadP( &page->owner ) ) + printf( "Page[%d] = 0x%x ; %p\n", pageindex, page->lock.v.value, mmAtomicReadP( &page->owner ) ); + } + fflush( stdout ); +#endif +} + + +void mmHashDirectDebugContent( void *hashtable, void (*callback)( uint32_t hashkey, void *entry ) ) +{ + uint32_t hashkey; + size_t entrysize; + void *entry; + mmHashTable *table; + table = hashtable; + entrysize = table->entrysize; + entry = MM_HASH_ENTRYLIST( table ); + for( hashkey = 0 ; hashkey < table->hashsize ; hashkey++ ) + { + callback( hashkey, entry ); + entry = ADDRESS( entry, entrysize ); + } + return; +} + + + +void mmHashStatistics( void *hashtable, long *accesscount, long *collisioncount, long *relocationcount, long *entrycount, long *entrycountmax, long *hashsizemax ) +{ + mmHashTable *table; + + table = hashtable; +#ifdef MM_HASH_DEBUG_STATISTICS + *accesscount = mmAtomicReadL( &table->accesscount ); + *collisioncount = mmAtomicReadL( &table->collisioncount ); + *relocationcount = mmAtomicReadL( &table->relocationcount ); + *entrycount = mmAtomicReadL( &table->statentrycount ); + *entrycountmax = table->entrycountmax; + *hashsizemax = 1 << table->hashbits; +#else + *accesscount = 0; + *collisioncount = 0; + *relocationcount = 0; + *entrycount = 0; + *entrycountmax = 0; + *hashsizemax = 1 << table->hashbits; +#endif + + return; +} + +unsigned int mmHashGetCount( void *hashtable) +{ + mmHashTable *table = hashtable; + unsigned int count = 0; + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { +#ifdef MM_ATOMIC_SUPPORT + count = mmAtomicRead32(&table->entrycount); +#else + count = table->entrycount; +#endif + } + return count; +} diff --git a/ecere/src/gfx/newFonts/cc/mmhash.h b/ecere/src/gfx/newFonts/cc/mmhash.h new file mode 100644 index 0000000000..b26ab94155 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mmhash.h @@ -0,0 +1,151 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2015 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ + +#ifndef MMHASH_H +#define MMHASH_H + + +/* +#define MM_HASH_DEBUG_STATISTICS +*/ + + +enum +{ + MM_HASH_ENTRYCMP_INVALID, + MM_HASH_ENTRYCMP_FOUND, + MM_HASH_ENTRYCMP_SKIP +}; + +enum +{ + MM_HASH_ENTRYLIST_BREAK, + MM_HASH_ENTRYLIST_CONTINUE +}; + +typedef struct +{ + /* Clear the entry so that entryvalid() returns zero */ + void (*clearentry)( void *entry ); + /* Returns non-zero if the entry is valid and existing */ + int (*entryvalid)( const void *entry ); + /* Return key for an arbitrary set of user-defined data */ + uint32_t (*entrykey)( const void *entry ); + /* Return MM_HASH_ENTRYCMP* to stop or continue the search */ + int (*entrycmp)( const void *entry, const void *entryref ); + /* Return MM_HASH_ENTRYLIST* to stop or continue the search */ + int (*entrylist)( void *opaque, const void *entry, const void *entryref ); + /* Clear many entries at once */ + void (*clearentries)( void *entries, unsigned int count ); +} mmHashAccess; + + +/* Do not keep track of entry count, table will not be able to say when it needs to shrink or grow */ +#define MM_HASH_FLAGS_NO_COUNT (0x1) + + +size_t mmHashRequiredSize( size_t entrysize, uint32_t hashbits, uint32_t pageshift ); +void mmHashInit( void *hashtable, const mmHashAccess *access, size_t entrysize, uint32_t hashbits, uint32_t pageshift, uint32_t flags ); +int mmHashGetStatus( void *hashtable, int *rethashbits ); +void mmHashReset( void *hashtable, const mmHashAccess *access ); + +enum +{ + MM_HASH_STATUS_MUSTGROW, + MM_HASH_STATUS_MUSTSHRINK, + MM_HASH_STATUS_NORMAL, + MM_HASH_STATUS_UNKNOWN +}; + + +void *mmHashDirectFindEntry( void *hashtable, const mmHashAccess *access, const void *findentry ); +void *mmHashLockFindEntry( void *hashtable, const mmHashAccess *access, void *findentry ); + +void mmHashDirectListEntry( void *hashtable, const mmHashAccess *access, void *listentry, void *opaque ); +void mmHashLockListEntry( void *hashtable, const mmHashAccess *access, void *listentry, void *opaque ); + +int mmHashDirectReadEntry( void *hashtable, const mmHashAccess *access, void *readentry ); +int mmHashLockReadEntry( void *hashtable, const mmHashAccess *access, void *readentry ); + +int mmHashDirectCallEntry( void *hashtable, const mmHashAccess *access, void *callentry, void (*callback)( void *opaque, void *entry, int newflag ), void *opaque, int addflag ); +int mmHashLockCallEntry( void *hashtable, const mmHashAccess *access, void *callentry, void (*callback)( void *opaque, void *entry, int newflag ), void *opaque, int addflag ); + +/* The hash key for replaced entries must remain the same! */ +int mmHashDirectReplaceEntry( void *hashtable, const mmHashAccess *access, void *replaceentry, int addflag ); +int mmHashLockReplaceEntry( void *hashtable, const mmHashAccess *access, void *replaceentry, int addflag ); + +int mmHashDirectAddEntry( void *hashtable, const mmHashAccess *access, void *adddentry, int nodupflag ); +int mmHashLockAddEntry( void *hashtable, const mmHashAccess *access, void *adddentry, int nodupflag ); + +int mmHashDirectAddEntry2( void *hashtable, const mmHashAccess *access, void *addentry, int nodupflag, void ** retEntry); + +int mmHashDirectReadOrAddEntry( void *hashtable, const mmHashAccess *access, void *readaddentry, int *readflag ); +int mmHashLockReadOrAddEntry( void *hashtable, const mmHashAccess *access, void *readaddentry, int *readflag ); + +int mmHashDirectDeleteEntry( void *hashtable, const mmHashAccess *access, void *deleteentry, int readflag ); +int mmHashLockDeleteEntry( void *hashtable, const mmHashAccess *access, void *deleteentry, int readflag ); +// NOTE: When setting dontmovestuff to 1, a resize must be done to repack! +int mmHashDirectDeleteEntry2( void *hashtable, const mmHashAccess *access, void *entry, int dontmovestuff); + +void mmHashResize( void *newtable, void *oldtable, const mmHashAccess *access, uint32_t hashbits, uint32_t pageshift ); +void mmHashResize2( void *newtable, void *oldtable, const mmHashAccess *access, uint32_t hashbits, uint32_t pageshift, void ** movedEntryPtr); + +void mmHashListAll( void *hashtable, int (*list)( void *opaque, void *entry ), void *opaque ); + +void * mmHashGetNext( void *hashtable, void * entry, const mmHashAccess *access); +void * mmHashGetPrev( void *hashtable, void * entry, const mmHashAccess *access); + +enum +{ + MM_HASH_FAILURE, + MM_HASH_SUCCESS, + MM_HASH_TRYAGAIN, + MM_HASH_FOUND /* Internal, can not be returned by any public call */ +}; + + +//// + + +typedef struct +{ + uint32_t hashkey; + uint32_t pagestart; + uint32_t pagefinal; + void *next; +} mmHashLockRange; + +typedef struct +{ + void *hashtable; + void *rangelist; + int newcount; +} mmHashLock; + + +void mmHashLockInit( mmHashLock *hashlock, int newcount ); +void mmHashLockAdd( void *hashtable, const mmHashAccess *access, void *entry, mmHashLock *hashlock, mmHashLockRange *lockrange ); +void mmHashLockAcquire( void *hashtable, const mmHashAccess *access, mmHashLock *hashlock ); +void mmHashLockRelease( void *hashtable, mmHashLock *hashlock ); + +void mmHashGlobalLockEnable( void *hashtable ); +void mmHashGlobalLockDisable( void *hashtable ); + + +//// + + +void mmHashDirectDebugDuplicate( void *hashtable, const mmHashAccess *access, void (*callback)( void *opaque, void *entry0, void *entry1 ), void *opaque ); + +void mmHashDirectDebugPages( void *hashtable ); + +void mmHashDirectDebugContent( void *hashtable, void (*callback)( uint32_t hashkey, void *entry ) ); + +void mmHashStatistics( void *hashtable, long *accesscount, long *collisioncount, long *relocationcount, long *entrycount, long *entrycountmax, long *hashsizemax ); + +unsigned int mmHashGetCount( void *hashtable); + +#endif diff --git a/ecere/src/gfx/newFonts/cc/mmhashinline.h b/ecere/src/gfx/newFonts/cc/mmhashinline.h new file mode 100644 index 0000000000..231e11c795 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mmhashinline.h @@ -0,0 +1,671 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2015 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ + +/* + +Input preprocessor symbols + +#define HASH_CLEARENTRY SomeFunction +#define HASH_ENTRYVALID SomeFunction +#define HASH_ENTRYKEY SomeFunction +#define HASH_ENTRYCMP SomeFunction +#define HASH_ENTRYLIST SomeFunction +#define HASH_SIZEOFENTRY sizeof(EntrStruct) + +#define HASH_DECLARE_DIRECTFINDENTRY MyFunctionFindEntry +#define HASH_DECLARE_DIRECTLISTENTRY MyFunctionListEntry +#define HASH_DECLARE_DIRECTREADENTRY MyFunctionReadEntry +#define HASH_DECLARE_DIRECTCALLENTRY MyFunctionCallEntry +#define HASH_DECLARE_DIRECTREPLACEENTRY MyFunctionReplaceEntry +#define HASH_DECLARE_DIRECTADDENTRY MyFunctionAddEntry +#define HASH_DECLARE_DIRECTADDRETENTRY MyFunctionAddRetEntry +#define HASH_DECLARE_DIRECTREADORADDENTRY MyFunctionReadOrAddEntry +#define HASH_DECLARE_DIRECTDELETEENTRY MyFunctionDeleteEntry + +Inlined function prototypes: + void clearentry( void *entry ); + int entryvalid( void *entry ); + uint32_t entrykey( void *entry ); + int entrycmp( void *entry, void *entryref ); + int entrylist( void *opaque, void *entry, void *entryref ); + +*/ + + +#include "mmhashinternal.h" + + +//// + + +#ifdef HASH_DECLARE_DIRECTFINDENTRY + +void *HASH_DECLARE_DIRECTFINDENTRY( void *hashtable, void *findentry ) +{ + int cmpvalue; + uint32_t hashkey; + void *entry; + mmHashTable *table; + + table = hashtable; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = HASH_ENTRYKEY( findentry ) & table->hashmask; + + /* Search the entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { +#ifdef HASH_SIZEOFENTRY + entry = MM_HASH_INLINE_ENTRY( table, hashkey, HASH_SIZEOFENTRY ); +#else + entry = MM_HASH_ENTRY( table, hashkey ); +#endif + cmpvalue = HASH_ENTRYCMP( entry, findentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + return entry; +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + return 0; +} + +#endif + + +#ifdef HASH_DECLARE_DIRECTLISTENTRY + +void HASH_DECLARE_DIRECTLISTENTRY( void *hashtable, void *listentry, void *opaque ) +{ + int cmpvalue; + uint32_t hashkey; + void *entry; + mmHashTable *table; + + table = hashtable; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = HASH_ENTRYKEY( listentry ) & table->hashmask; + + /* Search the entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { +#ifdef HASH_SIZEOFENTRY + entry = MM_HASH_INLINE_ENTRY( table, hashkey, HASH_SIZEOFENTRY ); +#else + entry = MM_HASH_ENTRY( table, hashkey ); +#endif + cmpvalue = HASH_ENTRYLIST( opaque, entry, listentry ); + if( cmpvalue == MM_HASH_ENTRYLIST_BREAK ) + break; +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + return; +} + +#endif + + +#ifdef HASH_DECLARE_DIRECTREADENTRY + +int HASH_DECLARE_DIRECTREADENTRY( void *hashtable, void *readentry ) +{ + int cmpvalue; + uint32_t hashkey; + void *entry; + mmHashTable *table; + + table = hashtable; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = HASH_ENTRYKEY( readentry ) & table->hashmask; + + /* Search the entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { +#ifdef HASH_SIZEOFENTRY + entry = MM_HASH_INLINE_ENTRY( table, hashkey, HASH_SIZEOFENTRY ); +#else + entry = MM_HASH_ENTRY( table, hashkey ); +#endif + cmpvalue = HASH_ENTRYCMP( entry, readentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + { +#ifdef HASH_SIZEOFENTRY + memcpy( readentry, entry, HASH_SIZEOFENTRY ); +#else + memcpy( readentry, entry, table->entrysize ); +#endif + return MM_HASH_SUCCESS; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + return MM_HASH_FAILURE; +} + +#endif + + +#ifdef HASH_DECLARE_DIRECTCALLENTRY + +int HASH_DECLARE_DIRECTCALLENTRY( void *hashtable, void *callentry, void (*callback)( void *opaque, void *entry, int newflag ), void *opaque, int addflag ) +{ + int cmpvalue; + uint32_t hashkey, entrycount; + void *entry; + mmHashTable *table; + + table = hashtable; + + /* Hash key of entry */ + hashkey = HASH_ENTRYKEY( callentry ) & table->hashmask; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Search an available entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { +#ifdef HASH_SIZEOFENTRY + entry = MM_HASH_INLINE_ENTRY( table, hashkey, HASH_SIZEOFENTRY ); +#else + entry = MM_HASH_ENTRY( table, hashkey ); +#endif + cmpvalue = HASH_ENTRYCMP( entry, callentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + { + callback( opaque, entry, 0 ); + return MM_HASH_SUCCESS; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + if( !( addflag ) ) + return MM_HASH_FAILURE; + + /* Store new entry */ +#ifdef HASH_SIZEOFENTRY + memcpy( entry, callentry, HASH_SIZEOFENTRY ); +#else + memcpy( entry, callentry, table->entrysize ); +#endif + callback( opaque, entry, 1 ); + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + return MM_HASH_SUCCESS; +} + +#endif + + +#ifdef HASH_DECLARE_DIRECTREPLACEENTRY + +int HASH_DECLARE_DIRECTREPLACEENTRY( void *hashtable, void *replaceentry, int addflag ) +{ + int cmpvalue; + uint32_t hashkey, entrycount; + void *entry; + mmHashTable *table; + + table = hashtable; + + /* Hash key of entry */ + hashkey = HASH_ENTRYKEY( replaceentry ) & table->hashmask; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Search an available entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { +#ifdef HASH_SIZEOFENTRY + entry = MM_HASH_INLINE_ENTRY( table, hashkey, HASH_SIZEOFENTRY ); +#else + entry = MM_HASH_ENTRY( table, hashkey ); +#endif + cmpvalue = HASH_ENTRYCMP( entry, replaceentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + { +#ifdef HASH_SIZEOFENTRY + memcpy( entry, replaceentry, HASH_SIZEOFENTRY ); +#else + memcpy( entry, replaceentry, table->entrysize ); +#endif + return MM_HASH_SUCCESS; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + if( !( addflag ) ) + return MM_HASH_FAILURE; + + /* Store new entry */ +#ifdef HASH_SIZEOFENTRY + memcpy( entry, replaceentry, HASH_SIZEOFENTRY ); +#else + memcpy( entry, replaceentry, HASH_SIZEOFENTRY ); +#endif + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + return MM_HASH_SUCCESS; +} + +#endif + + +#ifdef HASH_DECLARE_DIRECTADDENTRY + +int HASH_DECLARE_DIRECTADDENTRY( void *hashtable, void *addentry, int nodupflag ) +{ + int cmpvalue; + uint32_t hashkey, entrycount; + void *entry; + mmHashTable *table; + + table = hashtable; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = HASH_ENTRYKEY( addentry ) & table->hashmask; + + /* Search an available entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { +#ifdef HASH_SIZEOFENTRY + entry = MM_HASH_INLINE_ENTRY( table, hashkey, HASH_SIZEOFENTRY ); +#else + entry = MM_HASH_ENTRY( table, hashkey ); +#endif + /* Do we allow duplicate entries? */ + if( nodupflag ) + { + cmpvalue = HASH_ENTRYCMP( entry, addentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + return MM_HASH_FAILURE; + } + else + { + if( !( HASH_ENTRYVALID( entry ) ) ) + break; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + /* Store new entry */ +#ifdef HASH_SIZEOFENTRY + memcpy( entry, addentry, HASH_SIZEOFENTRY ); +#else + memcpy( entry, addentry, table->entrysize ); +#endif + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + return MM_HASH_SUCCESS; +} + +#endif + + +#ifdef HASH_DECLARE_DIRECTADDRETENTRY + +void *HASH_DECLARE_DIRECTADDRETENTRY( void *hashtable, void *addentry, int nodupflag ) +{ + int cmpvalue; + uint32_t hashkey, entrycount; + void *entry; + mmHashTable *table; + + table = hashtable; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = HASH_ENTRYKEY( addentry ) & table->hashmask; + + /* Search an available entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { +#ifdef HASH_SIZEOFENTRY + entry = MM_HASH_INLINE_ENTRY( table, hashkey, HASH_SIZEOFENTRY ); +#else + entry = MM_HASH_ENTRY( table, hashkey ); +#endif + /* Do we allow duplicate entries? */ + if( nodupflag ) + { + cmpvalue = HASH_ENTRYCMP( entry, addentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + return 0; + } + else + { + if( !( HASH_ENTRYVALID( entry ) ) ) + break; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + /* Store new entry */ +#ifdef HASH_SIZEOFENTRY + memcpy( entry, addentry, HASH_SIZEOFENTRY ); +#else + memcpy( entry, addentry, table->entrysize ); +#endif + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + return entry; +} + +#endif + + +#ifdef HASH_DECLARE_DIRECTREADORADDENTRY + +int HASH_DECLARE_DIRECTREADORADDENTRY( void *hashtable, void *readaddentry, int *retreadflag ) +{ + int cmpvalue; + uint32_t hashkey, entrycount; + void *entry; + mmHashTable *table; + + table = hashtable; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = HASH_ENTRYKEY( readaddentry ) & table->hashmask; + + /* Search an available entry */ + *retreadflag = 0; + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { +#ifdef HASH_SIZEOFENTRY + entry = MM_HASH_INLINE_ENTRY( table, hashkey, HASH_SIZEOFENTRY ); +#else + entry = MM_HASH_ENTRY( table, hashkey ); +#endif + cmpvalue = HASH_ENTRYCMP( entry, readaddentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + break; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + { +#ifdef HASH_SIZEOFENTRY + memcpy( readaddentry, entry, HASH_SIZEOFENTRY ); +#else + memcpy( readaddentry, entry, table->entrysize ); +#endif + *retreadflag = 1; + return MM_HASH_SUCCESS; + } +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + /* Store new entry */ +#ifdef HASH_SIZEOFENTRY + memcpy( entry, readaddentry, HASH_SIZEOFENTRY ); +#else + memcpy( entry, readaddentry, table->entrysize ); +#endif + + /* Increment count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, 1 ); + if( entrycount >= table->highcount ) + table->status = MM_HASH_STATUS_MUSTGROW; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + int statentrycount = mmAtomicAddReadL( &table->statentrycount, 1 ); + if( statentrycount > table->entrycountmax ) + table->entrycountmax = statentrycount; +#endif + + return MM_HASH_SUCCESS; +} + +#endif + + +#ifdef HASH_DECLARE_DIRECTDELETEENTRY + +int HASH_DECLARE_DIRECTDELETEENTRY( void *hashtable, void *deleteentry, int readflag ) +{ + int cmpvalue; + uint32_t hashkey, srckey, srcpos, targetpos = 0, targetkey, entrycount; + uint32_t delbase; + void *entry, *srcentry, *targetentry; + mmHashTable *table; + + table = hashtable; + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->accesscount, 1 ); +#endif + + /* Hash key of entry */ + hashkey = HASH_ENTRYKEY( deleteentry ) & table->hashmask; + + /* Search the entry */ + for( ; ; hashkey = ( hashkey + 1 ) & table->hashmask ) + { +#ifdef HASH_SIZEOFENTRY + entry = MM_HASH_INLINE_ENTRY( table, hashkey, HASH_SIZEOFENTRY ); +#else + entry = MM_HASH_ENTRY( table, hashkey ); +#endif + cmpvalue = HASH_ENTRYCMP( entry, deleteentry ); + if( cmpvalue == MM_HASH_ENTRYCMP_INVALID ) + return MM_HASH_FAILURE; + else if( cmpvalue == MM_HASH_ENTRYCMP_FOUND ) + break; +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->collisioncount, 1 ); +#endif + } + + if( readflag ) + { +#ifdef HASH_SIZEOFENTRY + memcpy( deleteentry, entry, HASH_SIZEOFENTRY ); +#else + memcpy( deleteentry, entry, table->entrysize ); +#endif + } + + for( delbase = hashkey ; ; ) + { + delbase = ( delbase - 1 ) & table->hashmask; +#ifdef HASH_SIZEOFENTRY + if( !( HASH_ENTRYVALID( MM_HASH_INLINE_ENTRY( table, delbase, HASH_SIZEOFENTRY ) ) ) ) + break; +#else + if( !( HASH_ENTRYVALID( MM_HASH_ENTRY( table, delbase ) ) ) ) + break; +#endif + } + delbase = ( delbase + 1 ) & table->hashmask; + + /* Entry found, delete it */ + for( ; ; ) + { + /* Find replacement target */ + targetkey = hashkey; + targetentry = 0; + for( srcpos = hashkey ; ; ) + { + srcpos = ( srcpos + 1 ) & table->hashmask; + + /* Try next entry */ +#ifdef HASH_SIZEOFENTRY + srcentry = MM_HASH_INLINE_ENTRY( table, srcpos, HASH_SIZEOFENTRY ); +#else + srcentry = MM_HASH_ENTRY( table, srcpos ); +#endif + if( !( HASH_ENTRYVALID( srcentry ) ) ) + break; + srckey = HASH_ENTRYKEY( srcentry ) & table->hashmask; + + if( targetkey >= delbase ) + { + if( ( srckey < delbase ) || ( srckey > targetkey ) ) + continue; + } + else if( ( srckey > targetkey ) && ( srckey < delbase ) ) + continue; + targetentry = srcentry; + targetkey = srckey; + targetpos = srcpos; + } + + /* No replacement found, just clear it */ +#ifdef HASH_SIZEOFENTRY + entry = MM_HASH_INLINE_ENTRY( table, hashkey, HASH_SIZEOFENTRY ); +#else + entry = MM_HASH_ENTRY( table, hashkey ); +#endif + if( !( targetentry ) ) + { + HASH_CLEARENTRY( entry ); + break; + } + + /* Move entry in place and continue the repair process */ +#ifdef HASH_SIZEOFENTRY + memcpy( entry, targetentry, HASH_SIZEOFENTRY ); +#else + memcpy( entry, targetentry, table->entrysize ); +#endif + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->relocationcount, 1 ); +#endif + + hashkey = targetpos; + } + + /* Decrement count of entries in table */ + if( !( table->flags & MM_HASH_FLAGS_NO_COUNT ) ) + { + entrycount = MM_HASH_ENTRYCOUNT_ADD_READ( table, -1 ); + if( entrycount < table->lowcount ) + table->status = MM_HASH_STATUS_MUSTSHRINK; + } + +#ifdef MM_HASH_DEBUG_STATISTICS + mmAtomicAddL( &table->statentrycount, -1 ); +#endif + + return MM_HASH_SUCCESS; +} + +#endif + + +//// + + diff --git a/ecere/src/gfx/newFonts/cc/mmhashinternal.h b/ecere/src/gfx/newFonts/cc/mmhashinternal.h new file mode 100644 index 0000000000..530a701ab8 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mmhashinternal.h @@ -0,0 +1,131 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2015 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ + +#ifndef MMHASHINTERNAL_H +#define MMHASHINTERNAL_H + + +//// + +#if !defined(MM_ATOMIC_SUPPORT) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) + #warning WARNING: Compiling mmhash without atomic support +#endif + + +typedef struct +{ +#ifdef MM_ATOMIC_SUPPORT + /* Powerful atomic read/write lock */ + mmAtomicLock32 lock; + mmAtomicP owner; +#else + /* Mutex, or how to ruin performance */ + mtMutex mutex; + void *owner; +#endif +} mmHashPage MM_CACHE_ALIGN; + +typedef struct +{ + uint32_t status; + uint32_t flags; + size_t entrysize; + + /* Page locks */ + uint32_t pageshift; + uint32_t pagecount; + uint32_t pagemask; + mmHashPage *page; + + /* Hash size */ + uint32_t minhashbits; + uint32_t hashbits; + uint32_t hashsize; + uint32_t hashmask; + + /* Entry count tracking if hash table is dynamic */ +#ifdef MM_ATOMIC_SUPPORT + mmAtomic32 entrycount; +#else + mtMutex countmutex; + uint32_t entrycount; +#endif + uint32_t lowcount; + uint32_t highcount; + + /* Global lock as the final word to resolve fighting between threads trying to access the same entry */ + char paddingA[64]; +#ifdef MM_ATOMIC_SUPPORT + mmAtomic32 globallock; +#else + mtMutex globalmutex; +#endif + char paddingB[64]; + +#ifdef MM_HASH_DEBUG_STATISTICS + long entrycountmax; + mmAtomicL statentrycount; + mmAtomicL accesscount; + mmAtomicL collisioncount; + mmAtomicL relocationcount; +#endif + +} mmHashTable; + + +#define MM_HASH_ATOMIC_TRYCOUNT (16) + +#define MM_HASH_SIZEOF_ALIGN16(x) ((sizeof(x)+0xF)&~0xF) +#define MM_HASH_SIZEOF_ALIGN64(x) ((sizeof(x)+0x3F)&~0x3F) +#define MM_HASH_ALIGN64(x) ((x+0x3F)&~0x3F) +#define MM_HASH_ENTRYLIST(table) (void *)ADDRESS(table,MM_HASH_SIZEOF_ALIGN64(mmHashTable)) +#define MM_HASH_ENTRY(table,index) (void *)ADDRESS(table,MM_HASH_SIZEOF_ALIGN64(mmHashTable)+((index)*(table)->entrysize)) +#define MM_HASH_HASHKEY(table,entry) (((size_t)((char *)entry - (char *)table) - MM_HASH_SIZEOF_ALIGN64(mmHashTable))/(table)->entrysize) +#define MM_HASH_PAGELIST(table) (void *)ADDRESS(table,MM_HASH_ALIGN64(MM_HASH_SIZEOF_ALIGN64(mmHashTable)+((table)->hashsize*(table)->entrysize))) + +#define MM_HASH_INLINE_ENTRY(table,index,size) (void *)ADDRESS(table,MM_HASH_SIZEOF_ALIGN64(mmHashTable)+((index)*(size))) + + +//// + + +#ifdef MM_ATOMIC_SUPPORT + + #define MM_HASH_LOCK_TRY_READ(t,p) (mmAtomicLockTryRead32(&t->page[p].lock,MM_HASH_ATOMIC_TRYCOUNT)) + #define MM_HASH_LOCK_TRY_WRITE(t,p) (mmAtomicLockTryWrite32(&t->page[p].lock,MM_HASH_ATOMIC_TRYCOUNT)) + #define MM_HASH_LOCK_DONE_READ(t,p) (mmAtomicLockDoneRead32(&t->page[p].lock)) + #define MM_HASH_LOCK_DONE_WRITE(t,p) (mmAtomicLockDoneWrite32(&t->page[p].lock)) + #define MM_HASH_GLOBAL_LOCK(t) (mmAtomicSpin32(&t->globallock,0x0,0x1)) + #define MM_HASH_GLOBAL_UNLOCK(t) (mmAtomicWrite32(&t->globallock,0x0)) + #define MM_HASH_ENTRYCOUNT_ADD_READ(t,c) (mmAtomicAddRead32(&table->entrycount,c)) + +#else + + #define MM_HASH_LOCK_TRY_READ(t,p) (mtMutexTryLock(&t->page[p].mutex)) + #define MM_HASH_LOCK_TRY_WRITE(t,p) (mtMutexTryLock(&t->page[p].mutex)) + #define MM_HASH_LOCK_DONE_READ(t,p) mtMutexUnlock(&t->page[p].mutex) + #define MM_HASH_LOCK_DONE_WRITE(t,p) mtMutexUnlock(&t->page[p].mutex) + #define MM_HASH_GLOBAL_LOCK(t) mtMutexLock(&t->globalmutex) + #define MM_HASH_GLOBAL_UNLOCK(t) mtMutexUnlock(&t->globalmutex) + +static inline uint32_t MM_HASH_ENTRYCOUNT_ADD_READ( mmHashTable *t, int32_t c ) +{ + uint32_t entrycount; + mtMutexLock( &t->countmutex ); + t->entrycount += c; + entrycount = t->entrycount; + mtMutexUnlock( &t->countmutex ); + return entrycount; +} + +#endif + + +//// + + +#endif + diff --git a/ecere/src/gfx/newFonts/cc/mmthread.h b/ecere/src/gfx/newFonts/cc/mmthread.h new file mode 100644 index 0000000000..2b980a6941 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mmthread.h @@ -0,0 +1,912 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +/** + * @file + * + * Thread interface, POSIX threads and Windows threads implementations. + */ + + +#ifndef MMTHREAD_H +#define MMTHREAD_H + +#if defined(__EMSCRIPTEN__) || defined(__UWP__) + #define MT_NOTHREADS 1 +#endif + +//// + + +#if !MT_PTHREADS && !MT_WINTHREADS && !MT_NOTHREADS + #if defined(__linux__) || defined(__gnu_linux__) || defined(__linux) || defined(__linux) || defined(__APPLE__) || defined(__unix__) || defined(__unix) || defined(unix) + #undef MT_PTHREADS + #define MT_PTHREADS (1) + #elif defined(_WIN64) || defined(__WIN64__) || defined(WIN64) || defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + #undef MT_WINTHREADS + #define MT_WINTHREADS (1) + #else + #undef MT_NOTHREADS + #define MT_NOTHREADS (1) + #warning We have no threading implementation for the platform. + #endif +#endif + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) + #define MT_GNUC (1) +#elif defined(_MSC_VER) + #define MT_MSVC (1) +#else + #if MT_WINTHREADS + #error Can not target Windows threads with an unrecognized compiler! Need atomics and stuff. + #endif +#endif + + +///// + + +#define MT_THREAD_FLAGS_JOINABLE (0x1) +#define MT_THREAD_FLAGS_SETSTACK (0x2) + +#ifdef MT_DEBUG + #ifndef MT_DEBUG_WARNING() + #define MT_DEBUG_WARNING() ({printf( "\nMMTHREAD WARNING : %s %s %d\n", __FILE__, __FUNCTION__, __LINE__ ); fflush( stdout );}) + #endif +#endif + + +//// + + +#if MT_PTHREADS + + +/* POSIX threads */ + + #ifndef _GNU_SOURCE + #define _GNU_SOURCE + #endif + #include + #include + #include + #include + #include + + +static inline void mtYield() +{ + sched_yield(); + return; +} + + +typedef struct +{ + pthread_t pthread; +} mtThread; + +static inline void mtThreadCreate( mtThread *thread, void *(*threadmain)( void *value ), void *value, int flags, void *stack, size_t stacksize ) +{ + pthread_attr_t attr; + + pthread_attr_init( &attr ); + #if defined(__linux__) || defined(__gnu_linux__) || defined(__linux) || defined(__linux) || defined(__APPLE__) || defined(__unix__) || defined(__unix) || defined(unix) + if( flags & MT_THREAD_FLAGS_SETSTACK ) + pthread_attr_setstack( &attr, stack, stacksize ); +#endif + if( flags & MT_THREAD_FLAGS_JOINABLE ) + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); + else + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED ); + pthread_create( &thread->pthread, &attr, threadmain, value ); + pthread_attr_destroy( &attr ); + + return; +} + +static inline void mtThreadExit() +{ + pthread_exit( 0 ); + return; +} + +static inline void mtThreadJoin( mtThread *thread ) +{ + void *ret; + pthread_join( thread->pthread, &ret ); + return; +} + +static inline mtThread mtThreadSelf() +{ + mtThread thread; + thread.pthread = pthread_self(); + return thread; +} + +static inline int mtThreadCmpEqual( mtThread *thread0, mtThread *thread1 ) +{ + return ( pthread_equal( thread0->pthread, thread1->pthread ) != 0 ? 1 : 0 ); +} + +static inline size_t mtGetMinimumStackSize() +{ + #ifdef PTHREAD_STACK_MIN + return PTHREAD_STACK_MIN; + #elif defined(MM_H) + return ( mmcontext.pagesize ? 4*mmcontext.pagesize : 16384 ); + #else + return 16384; + #endif +} + + +//// + + +typedef struct +{ +#ifdef MT_DEBUG + unsigned char *function; + unsigned char *file; + int line; +#endif + pthread_mutex_t pmutex; +} mtMutex; + +static inline void mtMutexInit( mtMutex *mutex ) +{ + pthread_mutex_init( &mutex->pmutex, 0 ); + return; +} + +static inline void mtMutexDestroy( mtMutex *mutex ) +{ + pthread_mutex_destroy( &mutex->pmutex ); + return; +} + + #ifdef MT_DEBUG + + #define mtMutexLock(a) mtMutexLockDebug(a,__FUNCTION__,__FILE__,__LINE__) + #define mtMutexUnlock(a) mtMutexUnlockDebug(a,__FUNCTION__,__FILE__,__LINE__) + #define mtMutexTryLock(a) mtMutexTryLockDebug(a,__FUNCTION__,__FILE__,__LINE__) + +static inline void mtMutexLockDebug( mtMutex *mutex, const unsigned char *function, const unsigned char *file, const int line ) +{ + printf( "Mutex lock : Thread %d on %p from %s() in %s:%d\n", (int)pthread_self(), mutex, function, file, line ); + fflush( stdout ); + if( pthread_mutex_trylock( &mutex->pmutex ) ) + { + printf( " Mutex %p locked by %s() in %s:%d\n", mutex, mutex->function, mutex->file, mutex->line ); + fflush( stdout ); + if( pthread_mutex_lock( &mutex->pmutex ) ) + MT_DEBUG_WARNING(); + } + mutex->function = (unsigned char *)function; + mutex->file = (unsigned char *)file; + mutex->line = line; + return; +} + +static inline void mtMutexUnlockDebug( mtMutex *mutex, const unsigned char *function, const unsigned char *file, const int line ) +{ + mutex->function = (unsigned char *)function; + mutex->file = (unsigned char *)file; + mutex->line = line; + printf( "Mutex Unlock : Thread %d on %p from %s() in %s:%d\n", (int)pthread_self(), mutex, function, file, line ); + fflush( stdout ); + if( pthread_mutex_unlock( &mutex->pmutex ) ) + MT_DEBUG_WARNING(); + return; +} + +static inline int mtMutexTryLockDebug( mtMutex *mutex, const unsigned char *function, const unsigned char *file, const int line ) +{ + printf( "Mutex Trylock : Thread %d on %p from %s() in %s:%d\n", (int)pthread_self(), mutex, function, file, line ); + fflush( stdout ); + if( !( pthread_mutex_trylock( &mutex->pmutex ) ) ) + { + mutex->function = (unsigned char *)function; + mutex->file = (unsigned char *)file; + mutex->line = line; + return 1; + } + else + return 0; +} + + #else + +static inline void mtMutexLock( mtMutex *mutex ) +{ + pthread_mutex_lock( &mutex->pmutex ); + return; +} + +static inline void mtMutexUnlock( mtMutex *mutex ) +{ + pthread_mutex_unlock( &mutex->pmutex ); + return; +} + +static inline int mtMutexTryLock( mtMutex *mutex ) +{ + return !( pthread_mutex_trylock( &mutex->pmutex ) ); +} + + #endif + + +//// + + +typedef struct +{ + pthread_cond_t pcond; +} mtSignal; + +static inline void mtSignalInit( mtSignal *signal ) +{ + pthread_cond_init( &signal->pcond, 0 ); + return; +} + +static inline void mtSignalDestroy( mtSignal *signal ) +{ + pthread_cond_destroy( &signal->pcond ); + return; +} + +static inline void mtSignalWake( mtSignal *signal ) +{ + #ifdef MT_DEBUG + if( pthread_cond_signal( &signal->pcond ) ) + MT_DEBUG_WARNING(); + #else + pthread_cond_signal( &signal->pcond ); + #endif + return; +} + +static inline void mtSignalBroadcast( mtSignal *signal ) +{ + #ifdef MT_DEBUG + if( pthread_cond_broadcast( &signal->pcond ) ) + MT_DEBUG_WARNING(); + #else + pthread_cond_broadcast( &signal->pcond ); + #endif + return; +} + +static inline void mtSignalWait( mtSignal *signal, mtMutex *mutex ) +{ + #ifdef MT_DEBUG + if( pthread_cond_wait( &signal->pcond, &mutex->pmutex ) ) + MT_DEBUG_WARNING(); + #else + pthread_cond_wait( &signal->pcond, &mutex->pmutex ); + #endif + return; +} + +static inline int mtSignalWaitTimeout( mtSignal *signal, mtMutex *mutex, long milliseconds ) +{ + unsigned long long microsecs; + struct timespec ts; + struct timeval tp; + gettimeofday( &tp, NULL ); + ts.tv_sec = tp.tv_sec + ( milliseconds / 1000 ); + microsecs = tp.tv_usec + ( ( milliseconds % 1000 ) * 1000 ); + if( microsecs >= 1000000 ) + { + ts.tv_sec++; + microsecs -= 1000000; + } + ts.tv_nsec = (long)(microsecs * 1000); + return ( pthread_cond_timedwait( &signal->pcond, &mutex->pmutex, &ts ) == 0 ); +} + +static inline unsigned long long mtSignalTime( long milliseconds ) +{ + unsigned long long millitime; + struct timeval lntime; + gettimeofday( &lntime, 0 ); + millitime = ( (unsigned long long)lntime.tv_sec * 1000 ) + ( (unsigned long long)lntime.tv_usec / 1000 ) + (unsigned long long)milliseconds; + return millitime; +} + +static inline int mtSignalWaitTime( mtSignal *signal, mtMutex *mutex, unsigned long long millitime ) +{ + struct timespec ts; + ts.tv_sec = (long)(millitime / 1000); + ts.tv_nsec = (long)(( millitime % 1000 ) * 1000000); + return ( pthread_cond_timedwait( &signal->pcond, &mutex->pmutex, &ts ) == 0 ); +} + + #ifdef MT_DEBUG + #define MT_MUTEX_INITIALIZER { 0, 0, 0, PTHREAD_MUTEX_INITIALIZER } + #else + #define MT_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER } + #endif + + +#elif MT_WINTHREADS + + +/* Windows threads */ + + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + #if MT_MSVC + #include + #endif + +static inline void mtYield() +{ + SwitchToThread(); + return; +} + + +typedef struct +{ + HANDLE winthread; + DWORD threadidentifier; +} mtThread; + +typedef struct +{ + void *(*threadmain)( void *value ); + void *value; +} mtWinThreadLaunch; + +static DWORD WINAPI mtWinThreadMain( void *launchdata ) +{ + mtWinThreadLaunch launch; + launch = *(mtWinThreadLaunch *)launchdata; + free( (void *)launchdata ); + launch.threadmain( launch.value ); + return 0; +} + +static inline void mtThreadCreate( mtThread *thread, void *(*threadmain)( void *value ), void *value, int flags, void *stack, size_t stacksize ) +{ + mtWinThreadLaunch *launch; + launch = (mtWinThreadLaunch *)malloc( sizeof(mtWinThreadLaunch) ); + launch->threadmain = threadmain; + launch->value = value; + thread->winthread = CreateThread( (LPSECURITY_ATTRIBUTES)0, stacksize, mtWinThreadMain, (void *)launch, 0, &thread->threadidentifier ); + return; +} + +static inline void mtThreadExit() +{ + ExitThread( 0 ); + return; +} + +static inline void mtThreadJoin( mtThread *thread ) +{ + WaitForSingleObject( thread->winthread, INFINITE ); + CloseHandle( thread->winthread ); + return; +} + +static inline mtThread mtThreadSelf() +{ + mtThread thread; + thread.winthread = 0; + thread.threadidentifier = GetCurrentThreadId(); + return thread; +} + +static inline int mtThreadCmpEqual( mtThread *thread0, mtThread *thread1 ) +{ + return ( thread0->threadidentifier == thread1->threadidentifier ); +} + +static inline size_t mtGetMinimumStackSize() +{ + return 16384; +} + + +//// + + +typedef struct +{ + CRITICAL_SECTION critsection; +} mtMutex; + +static inline void mtMutexInit( mtMutex *mutex ) +{ + InitializeCriticalSection( &mutex->critsection ); + return; +} + +static inline void mtMutexDestroy( mtMutex *mutex ) +{ + DeleteCriticalSection( &mutex->critsection ); + return; +} + + +static inline void mtMutexLock( mtMutex *mutex ) +{ + EnterCriticalSection( &mutex->critsection ); + return; +} + +static inline void mtMutexUnlock( mtMutex *mutex ) +{ + LeaveCriticalSection( &mutex->critsection ); + return; +} + +static inline int mtMutexTryLock( mtMutex *mutex ) +{ + return TryEnterCriticalSection( &mutex->critsection ); +} + + +//// + + +typedef struct +{ + volatile long waitcount; + /* Signal:0, broadcast:1 */ + HANDLE events[2]; +} mtSignal; + +static inline void mtSignalInit( mtSignal *signal ) +{ + signal->waitcount = 0; + signal->events[0] = CreateEvent( 0, 0, 0, 0 ); + signal->events[1] = CreateEvent( 0, 1, 0, 0 ); + return; +} + +static inline void mtSignalDestroy( mtSignal *signal ) +{ + CloseHandle( signal->events[0] ); + CloseHandle( signal->events[1] ); + return; +} + +static inline void mtSignalWake( mtSignal *signal ) +{ + if( signal->waitcount ) + SetEvent( signal->events[0] ); + return; +} + +static inline void mtSignalBroadcast( mtSignal *signal ) +{ + if( signal->waitcount ) + SetEvent( signal->events[1] ); + return; +} + +static inline void mtSignalWait( mtSignal *signal, mtMutex *mutex ) +{ + int waitresult; + long remwaitcount; + +#if MT_GNUC + __sync_add_and_fetch( &signal->waitcount, (long)1 ); +#elif MT_MSVC + _InterlockedIncrement( &signal->waitcount ); +#endif + LeaveCriticalSection( &mutex->critsection ); + waitresult = WaitForMultipleObjects( 2, signal->events, FALSE, INFINITE ); + EnterCriticalSection( &mutex->critsection ); +#if MT_GNUC + remwaitcount = __sync_sub_and_fetch( &signal->waitcount, (long)1 ); +#elif MT_MSVC + remwaitcount = _InterlockedDecrement( &signal->waitcount ); +#endif + if( ( waitresult == ( WAIT_OBJECT_0 + 1 ) ) && !remwaitcount ) + ResetEvent( signal->events[1] ); + return; +} + +static inline int mtSignalWaitTimeout( mtSignal *signal, mtMutex *mutex, long milliseconds ) +{ + int waitresult; + long remwaitcount; + +#if MT_GNUC + __sync_add_and_fetch( &signal->waitcount, (long)1 ); +#elif MT_MSVC + _InterlockedIncrement( &signal->waitcount ); +#endif + LeaveCriticalSection( &mutex->critsection ); + waitresult = WaitForMultipleObjects( 2, signal->events, FALSE, milliseconds ); + EnterCriticalSection( &mutex->critsection ); +#if MT_GNUC + remwaitcount = __sync_sub_and_fetch( &signal->waitcount, (long)1 ); +#elif MT_MSVC + remwaitcount = _InterlockedDecrement( &signal->waitcount ); +#endif + if( ( waitresult == ( WAIT_OBJECT_0 + 1 ) ) && !remwaitcount ) + ResetEvent( signal->events[1] ); + return ( waitresult != WAIT_TIMEOUT ); +} + +static inline int mtGetTimeOfDay( struct timeval *tv ) +{ + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL + FILETIME ft; + unsigned long long curtime; + if( tv ) + { + GetSystemTimeAsFileTime( &ft ); + curtime = ( ( ( (unsigned long long)ft.dwHighDateTime << 32 ) | (unsigned long long)ft.dwLowDateTime ) / 10 ) - DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long)( curtime / 1000000UL ); + tv->tv_usec = (long)( curtime % 1000000UL ); + } + return 0; +} + +static inline unsigned long long mtSignalTime( long milliseconds ) +{ + unsigned long long millitime; + struct timeval lntime; + mtGetTimeOfDay( &lntime ); + millitime = ( (unsigned long long)lntime.tv_sec * 1000 ) + ( (unsigned long long)lntime.tv_usec / 1000 ) + (unsigned long long)milliseconds; + return millitime; +} + +static inline int mtSignalWaitTime( mtSignal *signal, mtMutex *mutex, unsigned long long millitime ) +{ + unsigned long long currenttime; + long timeout; + struct timeval lntime; + mtGetTimeOfDay( &lntime ); + currenttime = ( (unsigned long long)lntime.tv_sec * 1000 ) + ( (unsigned long long)lntime.tv_usec / 1000 ); + timeout = (long)( millitime - currenttime ); + if( timeout < 0 ) + timeout = 0; + return mtSignalWaitTimeout( signal, mutex, timeout ); +} + + +#else + + +/* No threads */ + + #ifndef MT_DISABLED + #define MT_DISABLED (1) + #endif + +typedef struct mtMutex { int foo; } mtMutex; +typedef struct mtSpin { int foo; } mtSpin; +typedef struct mtSignal { int foo; } mtSignal; + + #define mtMutexInit(a) + #define mtMutexDestroy(a) + #define mtMutexLock(a) + #define mtMutexUnlock(a) + #define mtMutexTryLock(a) 1 + + #define mtSpinInit(a) + #define mtSpinDestroy(a) + #define mtSpinLock(a) + #define mtSpinUnlock(a) + #define mtSpinTryLock(a) + + #define mtSignalInit(a) + #define mtSignalDestroy(a) + #define mtSignalWake(a) + #define mtSignalBroadcast(a) + #define mtSignalMutexWait(a,b) + #define mtSignalTime(a) (0) + #define mtSignalWaitTime(a) (1) + + +#endif + + +//// + + +#if MT_GNUC && !MT_NOTHREADS + + +/* Spin locks, GNUC/clang/ICC implementation */ + +typedef struct mtSpin +{ + volatile unsigned int atomicspin; +} mtSpin; + +static inline void mtSpinInit( mtSpin *spin ) +{ + __asm__ __volatile__( "":::"memory" ); + spin->atomicspin = 0; + return; +} + +static inline void mtSpinDestroy( mtSpin *spin ) +{ + return; +} + +static inline void mtSpinLock( mtSpin *spin ) +{ + for( ; __sync_val_compare_and_swap( &spin->atomicspin, 0x0, 0x1 ) != 0x0 ; ) + { + for( ; spin->atomicspin != 0x0 ; ) + { + #if defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) || defined(__amd64) || defined(__i386__) || defined(__i386) || defined(i386) + __asm__ __volatile__( "rep ; nop" :::"memory" ); + #else + __asm__ __volatile__( "":::"memory" ); + #endif + } + } + return; +} + +static inline void mtSpinUnlock( mtSpin *spin ) +{ + __asm__ __volatile__( "":::"memory" ); + spin->atomicspin = 0; + return; +} + +static inline int mtSpinTryLock( mtSpin *spin ) +{ + return ( __sync_val_compare_and_swap( &spin->atomicspin, 0x0, 0x1 ) == 0x0 ); +} + + +#elif MT_MSVC && !MT_NOTHREADS + + +/* Spin locks, MSVC implementation */ + +typedef struct mtSpin +{ + volatile long atomicspin; +} mtSpin; + +static inline void mtSpinInit( mtSpin *spin ) +{ + _ReadWriteBarrier(); + spin->atomicspin = 0; + return; +} + +static inline void mtSpinDestroy( mtSpin *spin ) +{ + return; +} + +static inline void mtSpinLock( mtSpin *spin ) +{ + for( ; _InterlockedCompareExchange( &spin->atomicspin, 0x1, 0x0 ) != 0x0 ; ) + { + for( ; spin->atomicspin != 0x0 ; ) + { + YieldProcessor(); + _ReadWriteBarrier(); + } + } + return; +} + +static inline void mtSpinUnlock( mtSpin *spin ) +{ + _ReadWriteBarrier(); + spin->atomicspin = 0; + return; +} + +static inline int mtSpinTryLock( mtSpin *spin ) +{ + return ( _InterlockedCompareExchange( &spin->atomicspin, 0x1, 0x0 ) == 0x0 ); +} + + + #elif !MT_NOTHREADS + + +typedef struct mtMutex mtSpin; + + #define mtSpinInit(a) mtMutexInit(a) + #define mtSpinDestroy(a) mtMutexDestroy(a) + #define mtSpinLock(a) mtMutexLock(a) + #define mtSpinUnlock(a) mtMutexUnlock(a) + #define mtSpinTryLock(a) mtMutexTryLock(a) + + + #endif + + +//// + + +#if MT_GNUC && !MT_NOTHREADS + + +typedef struct +{ + volatile int counter __attribute__((aligned(64))); + volatile unsigned int state __attribute__((aligned(64))); + volatile unsigned int stateref __attribute__((aligned(64))); + int resetvalue __attribute__((aligned(64))); +} mtBarrier; + +static inline void mtBarrierBuild( mtBarrier *barrier, int waitcount ) +{ + barrier->counter = waitcount; + barrier->state = 0; + barrier->stateref = 0; + barrier->resetvalue = waitcount; + __asm__ __volatile__( "":::"memory" ); + return; +} + +static inline unsigned int mtBarrierWait( mtBarrier *barrier, unsigned int spinwaitcounter ) +{ + unsigned int stateref, nextstateref; + + stateref = barrier->stateref; + if( __sync_sub_and_fetch( &barrier->counter, 1 ) == 0 ) +{ + nextstateref = stateref + 1; + /* Barrier is cleared, that was the last thread waiting on it */ + barrier->stateref = nextstateref; + __sync_add_and_fetch( &barrier->counter, barrier->resetvalue ); + /* Ensure stateref and counter are updated and visible before releasing all spinning threads */ + __sync_synchronize(); + barrier->state = nextstateref; +} + else + { + /* Spin-wait */ + for( ; ( barrier->state == stateref ) && ( spinwaitcounter ) ; spinwaitcounter-- ) +{ + #if defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) || defined(__amd64) || defined(__i386__) || defined(__i386) || defined(i386) + __asm__ __volatile__( "rep ; nop" :::"memory" ); +#else + __asm__ __volatile__( "":::"memory" ); +#endif +} + /* Spin-wait counter exhausted, yield thread */ + if( !spinwaitcounter ) + { + __asm__ __volatile__( "":::"memory" ); + for( ; barrier->state == stateref ; ) + mtYield(); + } + } + + return spinwaitcounter; +} + + +#elif MT_MSVC + + +typedef struct +{ + __declspec(align(64)) volatile long counter; + __declspec(align(64)) volatile unsigned long state; + __declspec(align(64)) volatile unsigned long stateref; + __declspec(align(64)) int resetvalue; +} mtBarrier; + +static inline void mtBarrierBuild( mtBarrier *barrier, int waitcount ) +{ + barrier->counter = waitcount; + barrier->state = 0; + barrier->stateref = 0; + barrier->resetvalue = waitcount; + _ReadWriteBarrier(); + return; +} + +static inline unsigned int mtBarrierWait( mtBarrier *barrier, unsigned int spinwaitcounter ) +{ + unsigned int stateref, nextstateref; + + stateref = barrier->stateref; + if( _InterlockedDecrement( &barrier->counter ) == 0 ) + { + nextstateref = stateref + 1; + /* Barrier is cleared, that was the last thread waiting on it */ + barrier->stateref = nextstateref; + _InterlockedExchangeAdd( &barrier->counter, barrier->resetvalue ); + /* Ensure stateref and counter are updated and visible before releasing all spinning threads */ + _ReadWriteBarrier(); + barrier->state = nextstateref; + } + else + { + /* Spin-wait */ + for( ; ( barrier->state == stateref ) && ( spinwaitcounter ) ; spinwaitcounter-- ) + { + YieldProcessor(); + _ReadWriteBarrier(); + } + /* Spin-wait counter exhausted, yield thread */ + if( !spinwaitcounter ) + { + _ReadWriteBarrier(); + for( ; barrier->state == stateref ; ) + mtYield(); + } + } + + return spinwaitcounter; +} + + +#endif + + +//// + + +#if defined(__linux__) || defined(__gnu_linux__) || defined(__linux) || defined(__linux) + + #ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif + #include + + #if ( !defined(CPU_ZERO) || !defined(CPU_SET) ) && !defined(MM_H) + #warning Headers were included without #define _GNU_SOURCE, mtBindThreadToCpu() is not available +#endif + +static inline void mtBindThreadToCpu( int cpuindex ) +{ + #if defined(CPU_ZERO) && defined(CPU_SET) + cpu_set_t cpuset; + CPU_ZERO( &cpuset ); + CPU_SET( cpuindex, &cpuset ); + sched_setaffinity( 0, sizeof(cpu_set_t), &cpuset ); + #elif defined(MM_H) + mmThreadBindToCpu( cpuindex ); +#endif + return; +} + +#elif defined(_WIN64) || defined(__WIN64__) || defined(WIN64) || defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + + #include + +static inline void mtBindThreadToCpu( int cpuindex ) +{ + HANDLE handle; + handle = GetCurrentThread(); + SetThreadAffinityMask( handle, (size_t)1 << cpuindex ); + return; +} + +#else + +static inline void mtBindThreadToCpu( int cpuindex ) +{ + return; +} + +#endif + + +//// + + +#endif diff --git a/ecere/src/gfx/newFonts/drawManager.ec b/ecere/src/gfx/newFonts/drawManager.ec new file mode 100644 index 0000000000..c300072b28 --- /dev/null +++ b/ecere/src/gfx/newFonts/drawManager.ec @@ -0,0 +1,516 @@ +#undef __BLOCKS__ + +import "OpenGLDisplayDriver" +import "textureManager" + +#include "gl123es.h" + +namespace gfx; + +#define _Noreturn +#include +#include +#include "cc.h" +#define OFFSET(s, m) ((uint)(uintptr) (&((s *) 0)->m)) + +// TODO: Indices (2/3 data) +// TODO: glMapBufferRange() with GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT +// TODO: movnti to write to VBO: _mm_stream_ps(), _mm_stream_si32(), _mm_stream_si64() +// TODO: When the state changes, it should keep filling up a same VBO, then when the VBO is full (or when we are done), perform all the Draw() calls on that same VBO +// TODO: VAO + +static struct DMDrawVertex { short vx, vy, tx, ty; }; + +struct DMDrawBuffer +{ + GLAB vbo; + int glType; + int vertexCount; + int vertexAlloc; + void *vertexBuffer; +}; + +class DMImageFlags : uint16 +{ + bool empty:1; // Image is empty, do not draw + bool blending:1; // Must draw image with blending + SwizzleMode swizzle:2; +} + +#define DM_BARRIER_ORDER_BITS 5 + +class DMOrderMask : uint32 +{ + int imageOrder:8; + bool blend:1; + int texture:10; + int layer:4; + int barrier:DM_BARRIER_ORDER_BITS; +}; + +struct DMImage +{ +private: + Texture texture; + DMImageFlags flags; + short programIndex; + short srcx, srcy; + short sizex, sizey; + + // Computed order for sorted rendering, in defineImage() + DMOrderMask orderMask; + +public: + void clear() + { + this = { flags = { empty = true } }; + } +}; + +struct DMImageBuffer +{ + int image; + short offsetx, offsety; + short sizex, sizey; + short angcos, angsin; + ColorAlpha color; + uint32 orderIndex; +}; + +define DRAW_BUFFER_COUNT = 64; // TOFIX: Can't make static? +define DRAW_BUFFER_VERTEX_ALLOC = 1024; + +define DM_LAYER_COUNT = 1<orderIndex, ix2 = draw0->orderIndex; + if(ix1 < ix2) return 1; + if(ix1 == ix2 && draw1->color < draw0->color) return 1; + return 0; +} + +#define HSORT_MAIN dmSortImages +#define HSORT_CMP dmSortImagesCmp +#define HSORT_TYPE DMImageBuffer +#include "cchybridsort.h" +#undef HSORT_MAIN +#undef HSORT_CMP +#undef HSORT_TYPE + +// TOFIX: Make this private, have a property +public class DrawManager +{ + bool renderingFlipped; + + int imageCount, imageAlloc; + DMImage *imageList; + imageAlloc = 512; + imageList = new DMImage[imageAlloc]; + + void resetImages() + { + imageCount = 0; + } + + void clearImages() + { + delete imageList; + imageCount = 0; + imageAlloc = 512; + imageList = new DMImage[imageAlloc]; + } + + int imageBufferCount, imageBufferSize; + DMImageBuffer *imageBuffers, *imageBuffersTmp; + + // Buffers for drawimages() batching + DMDrawBuffer drawBuffers[DRAW_BUFFER_COUNT]; + int drawBufferIndex, drawBarrierIndex; + DMOrderMask orderBarrierMask; + + // Counter to track program uniforms and such + int64 updateCount; + + int defineImage( Texture texture, int offsetx, int offsety, int sizex, int sizey, bool blending, int layer ) + { + int ordx = offsetx >> 6; + int ordy = offsety >> 6; + int imageIndex = imageCount++; + if( imageCount > imageAlloc ) + { + imageAlloc <<= 1; + imageList = renew imageList DMImage[imageAlloc]; + } + imageList[ imageIndex ] = + { + texture = texture; + srcx = (short)offsetx; + srcy = (short)offsety; + sizex = (short)sizex; + sizey = (short)sizey; + flags = { blending = blending, swizzle = texture.swizzle }; + orderMask = + { + imageOrder = ccMortonNumber32( ordx, ordy ) & 0xFF, + blend = blending, texture = texture.orderMask, layer = layer + }; + }; + return imageIndex; + } + + static void flushRenderDrawBuffer( DMDrawBuffer drawBuffer, int vertexCount ) + { + glBindBuffer( GL_ARRAY_BUFFER, drawBuffer.vbo.buffer ); + + GLVertexPointer (2, GL_SHORT, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex, vx) ); + GLTexCoordPointer(2, GL_SHORT, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex, tx) ); + + GLFlushMatrices(); + glDrawArrays( GL_TRIANGLES, 0, vertexCount ); + } + + void flushImages() + { + drawBarrierIndex = 0; + orderBarrierMask = 0; + + if(imageBufferCount) + { + bool stateBlend = true; +#if ENABLE_GL_SHADERS + SwizzleMode swizzleMode = off; +#endif + int vertexCount = 0; + int index; + DMImageBuffer *imageBuffer = imageBuffers; + DMImage *bindImage = null; + Texture bindTexture = null; + DMDrawBuffer *drawBuffer; + DMDrawVertex *vboVertex = null; + ColorAlpha color = 0; + #if !ENABLE_GL_MAPBUF + DMDrawVertex *vboStorage = null; + #endif + + ERRORCHECK(); + + glabCurArrayBuffer = 0; + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + GLSetupFog(false); + GLSetupTexturing(true); + GLSetupLighting(false); + + GLEnableClientState(VERTICES); + GLEnableClientState(TEXCOORDS); + glEnable(GL_BLEND); + + // Sort by image type and texture, minimize state changes + dmSortImages( imageBuffers, imageBuffersTmp, imageBufferCount, (uint32)( (uintptr)imageBuffers >> 4 ) ); + + // Fill a drawBuffer, write vertex and texcoords + drawBuffer = &drawBuffers[drawBufferIndex++]; + drawBufferIndex %= DRAW_BUFFER_COUNT; + glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo.buffer ); + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); + +#if ENABLE_GL_MAPBUF + #ifdef _GLES3 + vboVertex = glMapBufferRange( GL_ARRAY_BUFFER, 0, drawBuffer->vertexAlloc * sizeof(DMDrawVertex), GL_MAP_WRITE_BIT); + #else + vboVertex = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY ); + #endif +#else + vboVertex = vboStorage = new DMDrawVertex[drawBuffer->vertexAlloc]; +#endif + + for(index = 0; index < imageBufferCount; index++, imageBuffer++) + { + DMImage * image = &imageList[imageBuffer->image]; + Texture texture = image->texture; + short tx0 = (short)( (int)image->srcx * texture.widthinv * DM_TEXCOORD_NORMFACTOR); + short ty0 = (short)( (int)image->srcy * texture.heightinv * DM_TEXCOORD_NORMFACTOR); + short tx1 = (short)(( (int)image->srcx + image->sizex ) * texture.widthinv * DM_TEXCOORD_NORMFACTOR); + short ty1 = (short)(( (int)image->srcy + image->sizey ) * texture.heightinv * DM_TEXCOORD_NORMFACTOR); + short angsin = imageBuffer->angsin, angcos = imageBuffer->angcos; + short sizex = imageBuffer->sizex, sizey = imageBuffer->sizey; + short vx0 = imageBuffer->offsetx; + short vy0 = imageBuffer->offsety; + short vx1 = (short)(vx0 + ((int)angcos * sizex * DM_VERTEX_NORMFACTOR / DM_IMAGE_ROTATION_NORMFACTOR)); + short vy1 = (short)(vy0 + ((int)angsin * sizex * DM_VERTEX_NORMFACTOR / DM_IMAGE_ROTATION_NORMFACTOR)); + short vx2 = (short)(vx0 - ((int)angsin * sizey * DM_VERTEX_NORMFACTOR / DM_IMAGE_ROTATION_NORMFACTOR)); + short vy2 = (short)(vy0 + ((int)angcos * sizey * DM_VERTEX_NORMFACTOR / DM_IMAGE_ROTATION_NORMFACTOR)); + short vx3 = (short)(vx0 + (((int)angcos * sizex - (int)angsin * sizey) * DM_VERTEX_NORMFACTOR / DM_IMAGE_ROTATION_NORMFACTOR)); + short vy3 = (short)(vy0 + (((int)angsin * sizex + (int)angcos * sizey) * DM_VERTEX_NORMFACTOR / DM_IMAGE_ROTATION_NORMFACTOR)); + bool flushFlag = + (imageBuffer->color != color) || + (image != bindImage && (stateBlend != image->flags.blending || texture != bindTexture +#if ENABLE_GL_SHADERS + || (glCaps_shaders && swizzleMode != image->flags.swizzle) +#endif + )) || + (vertexCount >= drawBuffer->vertexAlloc - 6); + + if(flushFlag) + { + if(vertexCount) + { +#if ENABLE_GL_MAPBUF + glUnmapBuffer( GL_ARRAY_BUFFER ); +#else + glBufferSubData( GL_ARRAY_BUFFER, 0, vertexCount * sizeof(DMDrawVertex), vboStorage ); +#endif + // Flush font manager texture updates + flush(); + + // Render buffered images + if(color) + flushRenderDrawBuffer( drawBuffer, vertexCount ); + drawBuffer = &drawBuffers[drawBufferIndex++]; + drawBufferIndex %= DRAW_BUFFER_COUNT; + glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo.buffer ); +#if ENABLE_GL_MAPBUF + #ifdef _GLES3 + vboVertex = glMapBufferRange( GL_ARRAY_BUFFER, 0, drawBuffer->vertexAlloc * sizeof(DMDrawVertex), GL_MAP_WRITE_BIT ); + #else + vboVertex = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY ); + #endif +#else + vboVertex = vboStorage; +#endif + vertexCount = 0; + } + + if(color != imageBuffer->color) + { + color = imageBuffer->color; + GLColor4ub(color.color.r, color.color.g, color.color.b, color.a); + } + if( stateBlend != image->flags.blending) + { + stateBlend = image->flags.blending; + ( stateBlend ? glEnable : glDisable )( GL_BLEND ); + } +#if ENABLE_GL_SHADERS + if( swizzleMode != image->flags.swizzle) + { + swizzleMode = image->flags.swizzle; + if(glCaps_shaders) + defaultShader.swizzle( swizzleMode ); + } +#endif + if( texture != bindTexture ) + { + bindTexture = texture; + glBindTexture( GL_TEXTURE_2D, bindTexture.glTex ); + } + bindImage = image; + } + + // Write data to VBO + // TODO: write vertex/texcoord all at once with SSE + vboVertex[0] = { vx3, vy3, tx1, ty1 }; + vboVertex[1] = { vx1, vy1, tx1, ty0 }; + vboVertex[2] = { vx2, vy2, tx0, ty1 }; + vboVertex[3] = { vx0, vy0, tx0, ty0 }; + vboVertex[4] = { vx2, vy2, tx0, ty1 }; + vboVertex[5] = { vx1, vy1, tx1, ty0 }; + vboVertex += 6; + vertexCount += 6; + + glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo.buffer ); + } + +#if ENABLE_GL_MAPBUF + glUnmapBuffer( GL_ARRAY_BUFFER ); +#else + glBufferSubData( GL_ARRAY_BUFFER, 0, vertexCount * sizeof(DMDrawVertex), vboStorage ); + delete vboStorage; +#endif + + // Flush font manager texture updates + flush(); + + // Render buffered images + if(color) + flushRenderDrawBuffer( drawBuffer, vertexCount ); + imageBufferCount = 0; + + ERRORCHECK(); + + GLDisableClientState(TEXCOORDS); + GLSetupTexturing(false); +#if ENABLE_GL_SHADERS + if(glCaps_shaders) + defaultShader.swizzle(off); +#endif + + if(glCaps_vertexBuffer) + { + glBindBuffer( GL_ARRAY_BUFFER, 0 ); + glabCurArrayBuffer = 0; + } + } + } + +public: + + property bool renderingFlipped { set { renderingFlipped = value; } } + + virtual void flush(); + + bool init() + { + int drawBufferIndex; + + imageBufferCount = 0; + imageBufferSize = 4096; + imageBuffers = new DMImageBuffer[imageBufferSize]; + imageBuffersTmp = new DMImageBuffer[imageBufferSize]; + + for( drawBufferIndex = 0 ; drawBufferIndex < DRAW_BUFFER_COUNT ; drawBufferIndex++ ) + { + DMDrawBuffer *drawBuffer = &drawBuffers[drawBufferIndex]; + drawBuffer->glType = GL_FLOAT; + drawBuffer->vertexCount = 0; + drawBuffer->vertexAlloc = DRAW_BUFFER_VERTEX_ALLOC; + drawBuffer->vbo.allocate(drawBuffer->vertexAlloc * sizeof(DMDrawVertex), null, dynamicDraw); + drawBuffer->vertexBuffer = new byte[drawBuffer->vertexAlloc * sizeof(DMDrawVertex)]; + } + + updateCount = 0; + return true; + } + + ~DrawManager() + { + end(); + } + + void end() + { + int i; + + for( i = 0 ; i < DRAW_BUFFER_COUNT ; i++ ) + { + DMDrawBuffer *db = &drawBuffers[i]; + if(db->vbo.buffer) + db->vbo.free(); + delete db->vertexBuffer; + } + delete imageBuffers; + delete imageBuffersTmp; + delete imageList; + } + + void ready( int viewportWidth, int viewportHeight ) + { + float norminv = 1.0f / DM_VERTEX_NORMFACTOR; + float texinv = 1.0f / DM_TEXCOORD_NORMFACTOR; + + GLMatrixMode(MatrixMode::texture); + GLPushMatrix(); + GLScalef(texinv,texinv,1); + + GLMatrixMode(MatrixMode::projection); + GLPushMatrix(); + GLLoadIdentity(); + if(renderingFlipped) + GLOrtho( 0.0, (float)viewportWidth, 0.0, (float)viewportHeight, -1.0f, 1.0 ); + else + GLOrtho( 0.0, (float)viewportWidth, (float)viewportHeight, 0.0, -1.0f, 1.0 ); + GLScalef(norminv, norminv, norminv); + + GLMatrixMode(MatrixMode::modelView); + GLPushMatrix(); + GLLoadIdentity(); + GLScalef(1,1,1); + + drawBarrierIndex = 0; + orderBarrierMask = 0; + + updateCount++; + } + + void drawImage( int index, float offsetx, float offsety, float angsin, float angcos, ColorAlpha color ) + { + DMImage * image = &imageList[index]; + float sizex = image->sizex, sizey = image->sizey; + if(!image->flags.empty && sizex > 0 && sizey > 0) + { + if( imageBufferCount >= imageBufferSize ) + { + imageBufferSize <<= 1; + imageBuffers = renew imageBuffers DMImageBuffer[imageBufferSize]; + imageBuffersTmp = renew imageBuffersTmp DMImageBuffer[imageBufferSize]; + } + imageBuffers[ imageBufferCount++ ] = + { + image = index; + offsetx = (short)roundf( offsetx * DM_VERTEX_NORMFACTOR ); + offsety = (short)roundf( offsety * DM_VERTEX_NORMFACTOR ); + sizex = (short)sizex; + sizey = (short)sizey; + angsin = (short)roundf( angsin * DM_IMAGE_ROTATION_NORMFACTOR ); + angcos = (short)roundf( angcos * DM_IMAGE_ROTATION_NORMFACTOR ); + color = color; + orderIndex = image->orderMask | orderBarrierMask; + }; + } + } + + void finish() + { + flushImages(); + + GLMatrixMode(MatrixMode::texture); + GLPopMatrix(); + GLMatrixMode(MatrixMode::projection); + GLPopMatrix(); + GLMatrixMode(MatrixMode::modelView); + GLPopMatrix(); + } + + void drawBarrier( ) + { + drawBarrierIndex++; + if(drawBarrierIndex >= ( 1 << DM_BARRIER_ORDER_BITS )) + flushImages( ); + orderBarrierMask = { barrier = (1 << DM_BARRIER_ORDER_BITS)-1 }; + } + + void clear() + { + imageBufferCount = 0; + } +} diff --git a/ecere/src/gfx/newFonts/fmFontManager.ec b/ecere/src/gfx/newFonts/fmFontManager.ec new file mode 100644 index 0000000000..3e5bb5456f --- /dev/null +++ b/ecere/src/gfx/newFonts/fmFontManager.ec @@ -0,0 +1,1601 @@ +/* ***************************************************************************** + * Original Version Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +#undef __BLOCKS__ + +import "LinkList" +import "File" +import "FontResource" +import "fontRenderer" + +import "atlasBuilder" +import "imgDistMap" + + +#define Alignment Alignment_ +#include "gl123es.h" +#undef Alignment + +namespace gfx; + +#include + +#define _Noreturn + +#include "cc.h" +#include "ccstr.h" + +static inline uint32 decodeUTF8( uint32 b, uint32 *state, unichar *retCodePoint ) { return ccUtf8ToUnicode(b, state, (uint *)retCodePoint); } + +//// + +static void buildCursorGlyph( byte *dst, int glyphwidth, int glyphheight, int dststride ) +{ + int x, y, hbarheight, hgap, hline, vline; + byte *dstrow; + + if( glyphwidth >= 3 ) + { + hbarheight = 1 + ( glyphheight >> 6 ); + hgap = ( glyphwidth - ( 1 + ( glyphheight >> 6 ) ) ) >> 1; + hline = hgap + glyphwidth - ( hgap << 1 ); + vline = glyphheight - ( hbarheight << 1 ); + for( y = 0 ; y < hbarheight ; y++ ) + { + dstrow = dst; + for( x = 0 ; x < glyphwidth ; x++ ) + dstrow[x] = 255; + dst += dststride; + } + for( y = 0 ; y < vline ; y++ ) + { + dstrow = dst; + for( x = 0 ; x < hgap ; x++ ) + dstrow[x] = 0; + for( ; x < hline ; x++ ) + dstrow[x] = 255; + for( ; x < glyphwidth ; x++ ) + dstrow[x] = 0; + dst += dststride; + } + for( y = 0 ; y < hbarheight ; y++ ) + { + dstrow = dst; + for( x = 0 ; x < glyphwidth ; x++ ) + dstrow[x] = 255; + dst += dststride; + } + } + else + { + for( y = 0 ; y < glyphheight ; y++ ) + { + dstrow = dst; + for( x = 0 ; x < glyphwidth ; x++ ) + dstrow[x] = 255; + dst += dststride; + } + } + + return; +} + +static inline void addGlyphAdvance( int *x, int *subpixel, FMGlyph *glyph ) +{ + *subpixel += glyph->advance; +#if FM_SUBPIXEL_ROUNDING_RANGE + *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1); +#endif + *x += *subpixel >> 6; + *subpixel &= 0x3f; +} + +public class FontManagerRenderer +{ +public: + FontManager fm; + + virtual bool init(int channelCount); + + // Create, resize or update texture data ; rect is [minx,maxx,miny,maxy] + virtual int createTexture( int width, int height ); + virtual int resizeTexture( int width, int height ); + virtual void updateTexture( int *rect, const byte *data ); + + // If the renderer does any buffering of drawImage(), flush it all right now (texture may change immediately after) + virtual void flush( ); + + // If the renderer records per-image data, return an imageIndex passed to drawImage() + virtual int registerImage( int offsetx, int offsety, int width, int height ); + // Draw an image, imageIndex passed as the value previously returned by registerImage() + virtual void drawImage( int targetx, int targety, int imageIndex, bool useExtColor ); + // Draw an image, called instead of drawImage() for text cursors, can point to exactly the same function + virtual void drawImageCursor( int targetx, int targety, int imageIndex ); + // If drawImage is zero, then this alternate function is called, passing everything required to render the glyph + virtual void drawImageAlt( byte *texdata, int targetx, int targety, int offsetx, int offsety, int width, int height ); + // Draw a non-aligned image, imageIndex passed as the value previously returned by registerImage() + virtual void drawImageFloat( float targetx, float targety, float angsin, float angcos, int imageIndex, bool useExtColor ); + + // The renderer must flush all recorded images, registerImage() will be called for new images + virtual void resetImages( ); + + virtual void setLayer( uint32 layerIndex ); +}; + + +//// + + +#include + +#include FT_FREETYPE_H +#include FT_ADVANCES_H + + +//// + + +#define FM_SUPPORT_GLYPH_ROTATION (1) + + +//// + + +#define FM_ENABLE_HINTING (1) +#define FM_SUBPIXEL_ROUNDING_RANGE (16) + + +define FM_HASH_TABLE_SIZE = 4096; +define FM_INIT_GLYPHS = 16384; //1024; // FIXME: allocGlyph() has been gone for a while in C code? hash table now? +define FM_INIT_ATLAS_NODES = 512; + +define FM_MAX_STATES = 16; + +//// + +struct FMFreeTypeFont +{ + FT_Face face; + + static inline int ::init() + { + FT_Error ftError; + ftError = FT_Init_FreeType( &ftLibrary2 ); + return ftError == 0; + } + + static inline int loadFont( byte *data, int dataSize ) + { + FT_Error ftError = FT_New_Memory_Face( ftLibrary2, (const FT_Byte*)data, dataSize, 0, &face ); + return ftError == 0; + } + + void free() + { + FT_Done_Face( face ); + } + + static inline void getFontVMetrics( float *ascent, float *descent, float *lineHeight, float *limitminy, float *limitmaxy ) + { + *ascent = (float)face->ascender; + *descent = (float)face->descender; + *lineHeight = (float)face->height / (float)face->units_per_EM; + *limitminy = (float)face->bbox.yMin / (float)face->units_per_EM; + *limitmaxy = (float)face->bbox.yMax / (float)face->units_per_EM; + } + + static inline int getGlyphIndex( unichar codepoint ) + { + return FT_Get_Char_Index( this.face, codepoint ); + } + + static int buildGlyphBitmap( int glyph, int size, int subpixel, int *advance, int *x0, int *y0, int *x1, int *y1 ) + { + FT_Error ftError; + FT_GlyphSlot glyphslot; + FT_Vector subvector; + + ftError = FT_Set_Pixel_Sizes( this.face, 0, (FT_UInt)size ); + if( ftError ) + return 0; + + subvector.x = subpixel; + subvector.y = 0; + FT_Set_Transform( face, 0, &subvector ); + + #if FM_ENABLE_HINTING + ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER ); + #else + ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER | FT_LOAD_NO_HINTING ); + #endif + if( ftError ) + return 0; + + glyphslot = face->glyph; + *advance = (int)glyphslot->metrics.horiAdvance; + *x0 = glyphslot->bitmap_left; + *x1 = glyphslot->bitmap_left + glyphslot->bitmap.width; + *y0 = -glyphslot->bitmap_top; + *y1 = -glyphslot->bitmap_top + glyphslot->bitmap.rows; + + return 1; + } + + static inline int getGlyphKernAdvance( int glyph1, int glyph2 ) + { + FT_Vector ftKerning; + FT_Get_Kerning( face, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning ); + return (int)ftKerning.x; + } + + static inline byte *getGlyphBitmap( int glyphindex ) + { + FT_GlyphSlot glyphslot; + glyphslot = face->glyph; + return glyphslot->bitmap.buffer; + } +}; + +class FMDefBits : uint64 +{ +public: + uint32 codePoint:32; + int size:11, subPixel:6; + bool outline:1; +} + +static define FM_SIZE_MAX = (1<<11)-1; +static define FM_BLUR_RADIUS_MAX = (1<<6)-1; +static define FM_BLUR_SCALE_MAX = (1<<6)-1; + +static define FM_GLYPH_CODEPOINT_CURSOR = 0x1; +static define FM_GLYPH_CODEPOINT_REPLACEMENT = 0xfffd; + +public enum FMVerticalAlignment { baseline, top, middle, bottom }; + +public class FMTextAlignment : uint16 +{ +public: + Alignment horzAlignment:2; + FMVerticalAlignment vertAlignment:2; +}; + +public struct FMPathDraw +{ + int prevGlyphIndex; + float middleAlign; +}; + +struct FMQuad { int x0, y0, x1, y1; }; + +struct FMGlyph +{ + int glyphindex; + uint64 glyphdef; + short x0, y0, x1, y1; + short advance, offsetx, offsety; + int imageIndex; + int listnext; + + static void getQuad( float x, float y, FMQuad q ) + { + int rx = (int)(x + offsetx); + int ry = (int)(y + offsety); + q = { rx, ry, rx + x1 - x0, ry + y1 - y0 }; + } +}; + +public class FMFont : struct +{ + public LinkElement link; + FMFreeTypeFont ftFont; + void *fontdata; + float ascender; + float descender; + float middleAlign; + float lineHeight; + float limitminy, limitmaxy; + FMGlyph *glyphs; + int glyphalloc; + int glyphcount; + int hashtable[FM_HASH_TABLE_SIZE]; + int glyphPaddingWidth; + + void (*processImage)( void *opaquecontext, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, int pass); + void *processImageContext; + + /* + public void setFontImageProcessing( + void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingsize, void *opaquecontext ), + void *opaquecontext ) + { + this.processImage = processImage; + this.processImageContext = opaquecontext; + } + */ + + float outlineRadius; + float outlineAlphaFactor; + float outlineIntensityFactor; + + static void ::outlineProcessGlyphImage( FMFont font, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, int isOutline ) + { + int x, y; + byte *src, *dst, *dstrow; + float intensityfactor, alphafactor, range, alpha, intensity, rangeinv, rangebase; + float *distancemap, *dmap; + + distancemap = new float[width * height]; + + src = &image[0]; + imgDistMapBuild( distancemap, src, width, height, bytesperpixel, bytesperline ); + + alphafactor = font.outlineAlphaFactor; //2.0f; + intensityfactor = font.outlineIntensityFactor; // 0.2f; + range = (float)font.outlineRadius; + rangeinv = 1.0f / range; + + dmap = distancemap; + dst = &image[0]; + for( y = 0 ; y < height ; y++ ) + { + dstrow = dst; + for( x = 0 ; x < width ; x++ ) + { + rangebase = ( range - dmap[ x ] ) * rangeinv; + alpha = alphafactor * rangebase; + intensity = fmaxf( (float)dstrow[0] * (1.0f/255.0f), intensityfactor * rangebase ); + if(bytesperpixel == 2) + { + /* Alpha channel */ + dstrow[0] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, alpha * 255.0f ) ) ); + /* Intensity channel */ + dstrow[1] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, intensity * 255.0f ) ) ); + } + else + { + if(isOutline) + dstrow[0] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, alpha * 255.0f ) ) ); + else + dstrow[0] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, intensity * 255.0f ) ) ); + } + dstrow += bytesperpixel; + } + dst += bytesperline; + dmap += width; + } + delete distancemap; + } + + public void setOutline(float size, float fade) + { + outlineIntensityFactor = 1.0f / (0.2f + size); + outlineAlphaFactor = 1.0f / (0.2f + fade); + outlineRadius = size; + processImage = outlineProcessGlyphImage; + processImageContext = this; + } + + ~FMFont() + { + ftFont.free(); + delete fontdata; + delete glyphs; + } + + static FMGlyph *allocGlyph( ) + { + if( glyphcount >= glyphalloc ) + { + glyphalloc <<= 1; + if( !(glyphs = renew glyphs FMGlyph[glyphalloc]) ) + return null; + } + return &glyphs[ glyphcount++ ]; + } + + //// + + static float getVertAlign( FMTextAlignment align, int size ) + { + float sizef = size; + if( align.vertAlignment == top ) + return ascender * sizef; + else if( align.vertAlignment == middle ) + return middleAlign * sizef; + else if( align.vertAlignment == bottom ) + return descender * sizef; + return 0.0f; + } + + static inline void addKerning( int prevGlyphIndex, FMGlyph *glyph, int *x, int *subpixel ) + { + if( prevGlyphIndex != -1 ) + { + *subpixel += ftFont.getGlyphKernAdvance( prevGlyphIndex, glyph->glyphindex ); + #if FM_SUBPIXEL_ROUNDING_RANGE + *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1); + #endif + *x += *subpixel >> 6; + *subpixel &= 0x3f; + } + } + +}; + +struct FMState +{ + FMFont font; + uint16 size; + FMTextAlignment align; +}; + +//// + + +static FT_Library ftLibrary2; + +static void copyGlyphBitmap1( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride ) +{ + int x, y; + for( y = 0 ; y < glyphheight ; y++ ) + { + byte *dstrow = &dst[ y * dststride ]; + for( x = 0 ; x < glyphwidth ; x++ ) + dstrow[ x ] = src[ x ]; + src += glyphwidth; + } +} + +static void copyGlyphBitmap2( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride ) +{ + int x, y; + for( y = 0 ; y < glyphheight ; y++ ) + { + byte *dstrow = &dst[ y * dststride ]; + for( x = 0 ; x < glyphwidth ; x++ ) + dstrow[ x << 1 ] = src[ x ]; + src += glyphwidth; + } +} + +static void copyGlyphBitmap4( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride ) +{ + int x, y; + for( y = 0 ; y < glyphheight ; y++ ) + { + byte *dstrow = &dst[ y * dststride ]; + for( x = 0 ; x < glyphwidth ; x++ ) + dstrow[ x << 2 ] = src[ x ]; + src += glyphwidth; + } +} + +public class FontManager +{ + FontManagerRenderer renderer; + int width, height; + float widthinv, heightinv; + int bytesperpixel; + int bytesperline; + int channelindex; + + AtlasBuilder atlas { }; + byte *texdata; + int dirtyrect[4]; + + LinkList fontList { }; + + FMState states[FM_MAX_STATES]; + int nstates; + + void (*copyGlyphBitmap)( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride ); + + + static FMGlyph *getGlyph( FMFont font, unichar codepoint, int size, int subpixel, bool outlinePass ) + { + int i, glyphindex, advance, x0, y0, x1, y1, gx, gy; + int glyphwidth, glyphheight, glyphareawidth, glyphareaheight; + uint64 glyphdef; + FMGlyph *glyph; + uint32 hashindex; + int padding, added; + // byte *bdst; + byte *dst, *src; + + glyph = 0; + if( size < 0.2 ) + return 0; + padding = font.glyphPaddingWidth; + + // Find code point and size. + glyphdef = FMDefBits { codepoint, size, subpixel, outlinePass }; + hashindex = ccHash32Int64Inline( glyphdef ) & ( FM_HASH_TABLE_SIZE - 1 ); + i = font.hashtable[hashindex]; + while( i != -1 ) + { + if( glyphdef == font.glyphs[i].glyphdef ) + return &font.glyphs[i]; + i = font.glyphs[i].listnext; + } + + /* Could not find glyph, create it */ + if( codepoint == FM_GLYPH_CODEPOINT_CURSOR ) + { + glyphindex = -1; + advance = 0; + #if 0 + x0 = 0; + x1 = 1; + #else + x0 = -2; + x1 = 3; + #endif + y0 = -(int)ceilf( font.limitmaxy * (float)size ); + y1 = -(int)floorf( font.limitminy * (float)size ); + i = ( (y1 - y0) - size ) / 3; + y0 += i; + y1 -= i; + } + else + { + glyphindex = font.ftFont.getGlyphIndex( codepoint ); + if( !( font.ftFont.buildGlyphBitmap( glyphindex, size, subpixel, &advance, &x0, &y0, &x1, &y1 ) ) ) + { + if( codepoint != FM_GLYPH_CODEPOINT_REPLACEMENT ) + return getGlyph(font, FM_GLYPH_CODEPOINT_REPLACEMENT, size, subpixel, outlinePass ); + return 0; + } + } + glyphwidth = ( x1 - x0 ); + glyphheight = ( y1 - y0 ); + glyphareawidth = glyphwidth + (padding << 1); + glyphareaheight = glyphheight + (padding << 1); + + // Find free spot for the rect in the atlas + added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy ); + if( !( added ) && ( onAtlasFull ) ) + { + /* Atlas is full, let the user to resize the atlas (or not), and try again. */ + onAtlasFull(); + added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy ); + } + if( !( added ) ) + return 0; + + /* Build the glyph */ + glyph = font.allocGlyph(); + glyph->glyphdef = glyphdef; + glyph->glyphindex = glyphindex; + glyph->x0 = (short)gx; + glyph->y0 = (short)gy; + glyph->x1 = (short)( glyph->x0 + glyphareawidth ); + glyph->y1 = (short)( glyph->y0 + glyphareaheight ); + glyph->advance = (short)advance; + glyph->offsetx = (short)( x0 - padding ); + glyph->offsety = (short)( y0 - padding ); + glyph->listnext = 0; + glyph->imageIndex = -1; + if( renderer.registerImage ) + { + renderer.setLayer(outlinePass ? 3 : 6); + glyph->imageIndex = renderer.registerImage( gx, gy, glyphareawidth, glyphareaheight); + } + + // Add char to hash table + glyph->listnext = font.hashtable[hashindex]; + font.hashtable[hashindex] = font.glyphcount - 1; + + // Clear glyph image area (TODO: wasteful when single channel without prepare callback?) + dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ]; + for( i = 0 ; i < glyphareaheight ; i++, dst += bytesperline ) + memset( dst, 0, glyphareawidth * bytesperpixel ); + + /* Rasterize */ + dst = &texdata[ ( glyph->x0 + padding ) * bytesperpixel + ( ( glyph->y0 + padding ) * bytesperline ) + channelindex]; + if( codepoint == FM_GLYPH_CODEPOINT_CURSOR ) + { + src = new byte[ glyphwidth * glyphheight ]; + buildCursorGlyph( src, glyphwidth, glyphheight, glyphwidth ); + copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline ); + delete src; + } + else + { + src = font.ftFont.getGlyphBitmap(glyphindex); + copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline ); + } + + // User custom font image processing + if(font.processImage) + { + dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ]; + font.processImage( font.processImageContext, dst, glyphareawidth, glyphareaheight, bytesperpixel, bytesperline, font.glyphPaddingWidth, outlinePass ); + } + + dirtyrect[0] = Min( dirtyrect[0], glyph->x0 ); + dirtyrect[1] = Min( dirtyrect[1], glyph->y0 ); + dirtyrect[2] = Max( dirtyrect[2], glyph->x1 ); + dirtyrect[3] = Max( dirtyrect[3], glyph->y1 ); + + return glyph; + } + + static inline void drawTextGlyph( FMFont font, FMGlyph *glyph, int x, int y, bool useExtColor ) + { + int ptx = x + glyph->offsetx; + int pty = y + glyph->offsety; + if( renderer.drawImage ) + renderer.drawImage( ptx, pty, glyph->imageIndex, useExtColor ); + else if( renderer.drawImageAlt ) + renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 ); + } + + static inline void drawTextCursorGlyph( FMFont font, FMGlyph *glyph, int x, int y ) + { + int ptx, pty; + ptx = x + glyph->offsetx; + pty = y + glyph->offsety; + if( renderer.drawImageCursor ) + renderer.drawImageCursor( ptx, pty, glyph->imageIndex ); + else if( renderer.drawImageAlt ) + renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 ); + } + + static inline void drawTextGlyphFloat( FMFont font, FMGlyph *glyph, float x, float y, float vectorx, float vectory, float offsetx, float offsety, bool useExtColor ) + { + float vectx = (float)glyph->offsetx + offsetx; + float vecty = (float)glyph->offsety + offsety; + float ptx = x + ( vectorx * vectx ) - ( vectory * vecty ); + float pty = y + ( vectorx * vecty ) + ( vectory * vectx ); + renderer.drawImageFloat( ptx, pty, vectory, vectorx, glyph->imageIndex, useExtColor ); + } + +public: + + property FontManagerRenderer renderer { set { renderer = value; } get { return renderer; } } + + // When notified of a full atlas, you should call fmExpandAtlas() or fmResetAtlas() within the callback + virtual void onAtlasFull() + { + renderer.flush(); + atlas.reset(atlas.width, atlas.height); + } + + // Create and destroy font manager + bool create( int width, int height, int channelCount, int channelIndex, FontManagerRenderer renderer) + { + bool result = false; + + if( ( channelCount != 1 ) && ( channelCount != 2 ) && ( channelCount != 4 ) ) + return 0; + if( ( width <= 0 ) || ( height <= 0 ) ) + return 0; + + this.renderer = renderer; + incref renderer; + renderer.init(channelCount); + + // Initialize implementation library + if(FMFreeTypeFont::init() ) + { + this.width = width; + this.height = height; + bytesperpixel = channelCount; + bytesperline = width * bytesperpixel; + this.channelindex = channelIndex; + if(renderer.createTexture( width, height )) + { + if((atlas.create( this.width, this.height, FM_INIT_ATLAS_NODES ))) + { + // Create texture for the cache. + widthinv = 1.0f / width; + heightinv = 1.0f / height; + texdata = new byte[width * height * bytesperpixel]; + if(texdata) + { + memset( texdata, 0, height * bytesperline ); + + dirtyrect[0] = this.width; + dirtyrect[1] = this.height; + dirtyrect[2] = 0; + dirtyrect[3] = 0; + + if( bytesperpixel == 1 ) + copyGlyphBitmap = copyGlyphBitmap1; + else if( bytesperpixel == 2 ) + copyGlyphBitmap = copyGlyphBitmap2; + else + copyGlyphBitmap = copyGlyphBitmap4; + + pushState(); + clearState(); + + result = true; + } + } + } + } + return result; + } + + ~FontManager() + { + delete renderer; + + fontList.Free(); + + delete atlas; + delete texdata; + } + + + //// + + // State setting + void setState( FMFont font, int size, int align) // TODO: fix this 'align' + { + FMState *state; + if( size >= FM_SIZE_MAX ) + size = FM_SIZE_MAX; + state = &states[ nstates - 1 ]; + state->font = font; + state->size = (uint16)size; + state->align = (uint16)align; + } + + void setFont( FMFont font ) + { + states[ nstates - 1 ].font = font; + } + + void setSize( int size ) + { + if( size >= FM_SIZE_MAX ) + size = FM_SIZE_MAX; + + states[ nstates - 1 ].size = (uint16)size; + } + + void setAlign( int align ) + { + states[ nstates - 1 ].align = (uint16)align; + } + + // Set image manipuation callback + void setFontImageProcessing( FMFont font, void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, void *opaquecontext ), void *opaquecontext ) + { + + } + + // State handling + void pushState( ) + { + if(nstates < FM_MAX_STATES) + { + if( nstates > 0 ) + memcpy( &states[nstates], &states[nstates-1], sizeof(FMState) ); + nstates++; + } + } + + void popState() + { + if(nstates > 1) + nstates--; + } + + void clearState() + { + FMState *state = &states[ nstates - 1 ]; + state->size = 12; + state->font = 0; + state->align = { left, baseline }; + } + + + //// + + static void freeFont(FMFont font) + { + if(font) + { + fontList.Remove((IteratorPointer)font); + delete font; + } + } + + // Add font from FontResource + FMFont getFont(FontResource fontResource) + { + FMFont font = null; + + Array infos = ResolveFont(fontResource.faceName, fontResource.size, fontResource.flags); + if(infos) + { + for(i : infos) + { + font = addFont(i.fileName, Max(2, (int)(1+fontResource.outlineSize))); + if(font) + { + font.setOutline(fontResource.outlineSize, fontResource.outlineFade); + break; + } + } + infos.Free(); + delete infos; + } + return font; + } + + // Add font from file + FMFont addFont(const String path, int glyphPaddingWidth ) + { + FMFont font = null; + File f = FileOpen(path, read); + if(f) + { + // Read in the font data + int dataSize = (int)f.GetSize(); + byte *data = new byte[dataSize]; + if(data) + { + f.Read(data, 1, dataSize); + font = addFontData(data, dataSize, glyphPaddingWidth); + if(!font) + delete data; + } + delete f; + } + return font; + } + + // Add font from data ; do not free( data ), the font manager will do that when removing the font + FMFont addFontData( byte *data, int dataSize, int glyphPaddingWidth ) + { + FMFont font + { + glyphs = new FMGlyph[FM_INIT_GLYPHS]; + glyphalloc = FM_INIT_GLYPHS; + glyphPaddingWidth = glyphPaddingWidth; + }; + if(font) + { + int i; + float ascent, descent, fontHeight; + + fontList.Add(font); + + // Init hash lookup + for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ ) + font.hashtable[i] = -1; + + // Init font + if(!font.ftFont.loadFont(data, dataSize)) + { + freeFont(font ); + return 0; + } + + // Store normalized line height. The real line height is got by multiplying by font size. + font.ftFont.getFontVMetrics( &ascent, &descent, &font.lineHeight, &font.limitminy, &font.limitmaxy ); + fontHeight = ascent - descent; + font.ascender = ascent / fontHeight; + font.descender = descent / fontHeight; + font.middleAlign = 0.5f * ( font.ascender + font.descender ); + font.fontdata = data; + } + return font; + } + + // Free font + void removeFont( FMFont font ) + { + freeFont(font ); + } + + // Draw text + int drawText( int x, int y, const char *string, int stringlength ) + { + int subpixel, index; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMGlyph *glyph; + int prevGlyphIndex; + FMFont font; + + state = &states[ nstates - 1 ]; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + if( !( state->font ) ) + return x; + font = state->font; + if( !( stringlength ) ) + stringlength = strlen( string ); + + // Align horizontally + if( state->align.horzAlignment == right ) + x -= getTextWidth(string, stringlength ); + else if( state->align.horzAlignment == center ) + x -= getTextWidth(string, stringlength ) >> 1; + + // Align vertically + y += roundf( font.getVertAlign(state->align, state->size ) ); + + subpixel = 0; + for( index = 0 ; index < stringlength ; index++ ) + { + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, false ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &x, &subpixel ); + if(font.processImage) + { + FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, true ); + if(outlineGlyph) + drawTextGlyph(font, outlineGlyph, x, y, true ); + } + drawTextGlyph(font, glyph, x, y, false ); + addGlyphAdvance( &x, &subpixel, glyph ); + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + + return x + ( subpixel >= 32 ); + } + + int drawTextWithCursor( int x, int y, const char *string, int stringlength, int cursoroffset ) + { + int subpixel, index; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMGlyph *glyph; + int prevGlyphIndex; + FMFont font; + + state = &states[ nstates - 1 ]; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + if( !( state->font ) ) + return x; + font = state->font; + if( !( stringlength ) ) + stringlength = strlen( string ); + + // Align horizontally + if( state->align.horzAlignment == right ) + x -= getTextWidth(string, stringlength ); + else if( state->align.horzAlignment == center ) + x -= getTextWidth(string, stringlength ) >> 1; + + // Align vertically + y += roundf( font.getVertAlign(state->align, state->size ) ); + + subpixel = 0; + for( index = 0 ; ; index++ ) + { + if( index == cursoroffset ) + { + glyph = getGlyph(font, FM_GLYPH_CODEPOINT_CURSOR, state->size, subpixel, false ); + if( glyph ) + drawTextCursorGlyph(font, glyph, x, y ); + } + if( index >= stringlength ) + break; + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, false ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &x, &subpixel ); + if(font.processImage) + { + FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, true ); + if(outlineGlyph) + drawTextGlyph(font, outlineGlyph, x, y, true ); + } + drawTextGlyph(font, glyph, x, y, false ); + addGlyphAdvance( &x, &subpixel, glyph ); + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + + return x + ( subpixel >= 32 ); + } + + int drawTextTruncate( int x, int y, int truncatewidth, const char *string, int stringlength, char *extstring, int extwidth ) + { + int subpixel, index, textwidth, truncatepoint; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMGlyph *glyph; + int prevGlyphIndex; + FMFont font; + + state = &states[ nstates - 1 ]; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + if( !( state->font ) ) + return x; + font = state->font; + if( !( stringlength ) ) + stringlength = strlen( string ); + textwidth = getTextWidthTruncate(string, stringlength, truncatewidth ); + + truncatepoint = x + truncatewidth; + if( textwidth <= truncatewidth ) + extstring = 0; + else + { + if( extwidth >= truncatewidth ) + return x; + truncatepoint -= extwidth; + } + + // Align horizontally + if( state->align.horzAlignment == right ) + x -= textwidth; + else if( state->align.horzAlignment == center ) + x -= textwidth >> 1; + + // Align vertically + y += roundf( font.getVertAlign(state->align, state->size ) ); + + subpixel = 0; + for( index = 0 ; index < stringlength ; index++ ) + { + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, false ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &x, &subpixel ); + if(font.processImage) + { + FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, true ); + if(outlineGlyph) + drawTextGlyph(font, outlineGlyph, x, y, true ); + } + drawTextGlyph(font, glyph, x, y, false ); + addGlyphAdvance( &x, &subpixel, glyph ); + if( x > truncatepoint ) + break; + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + x += ( subpixel >= 32 ); + if( extstring ) + drawText(x, y, extstring, 0 ); + + return x; + } + + + //// + + + // Measure text + int getTextWidth( const char *string, int stringlength ) + { + return getTextWidthTruncate(string, stringlength, ( (unsigned)1 << ( ( sizeof(int) * CHAR_BIT ) - 1 ) ) - 1 ); + } + + + int getTextWidthTruncate( const char *string, int stringlength, int truncatewidth ) + { + int subpixel, index, advance; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMGlyph *glyph; + int prevGlyphIndex; + FMFont font; + + state = &states[ nstates - 1 ]; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + if( !( state->font ) ) + return 0; + font = state->font; + if( !( stringlength ) ) + stringlength = strlen( string ); + + advance = 0; + subpixel = 0; + for( index = 0 ; index < stringlength ; index++ ) + { + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, false ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel ); + addGlyphAdvance( &advance, &subpixel, glyph ); + if( advance > truncatewidth ) + break; + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + + return advance + ( subpixel >= 32 ); + } + + int getTextBounds( int x, int y, const char *string, int stringlength, int *bounds ) + { + int subpixel, index; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMQuad q; + FMGlyph *glyph; + int prevGlyphIndex; + FMFont font; + int startx, advance; + int minx, miny, maxx, maxy; + + state = &states[ nstates - 1 ]; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + if( !( state->font ) ) + return x; + font = state->font; + if( !( stringlength ) ) + stringlength = strlen( string ); + + // Align vertically + y += font.getVertAlign(state->align, state->size ); + + minx = maxx = x; + miny = maxy = y; + startx = x; + + subpixel = 0; + for( index = 0 ; index < stringlength ; index++ ) + { + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, false ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &x, &subpixel ); + glyph->getQuad(x, y, q); + if( q.x0 < minx ) + minx = q.x0; + if( q.x1 > maxx ) + maxx = q.x1; + if( q.y0 < miny ) + miny = q.y0; + if( q.y1 > maxy ) + maxy = q.y1; + addGlyphAdvance( &x, &subpixel, glyph ); + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + + advance = x - startx; + + /* Align horizontally */ + if( state->align.horzAlignment == right ) + { + minx -= advance; + maxx -= advance; + } + else if( state->align.horzAlignment == center ) + { + minx -= advance * 0.5f; + maxx -= advance * 0.5f; + } + + if( bounds ) + { + bounds[0] = minx; + bounds[1] = maxx; + bounds[2] = miny; + bounds[3] = maxy; + } + + return advance + ( subpixel >= 32 ); + } + + // Find text offset up to truncatewidth + int getTextTruncateOffset( int truncatewidth, const char *string, int stringlength, int extwidth, int *retextflag, int *retfullwidth ) + { + int subpixel, index, advance, truncatewidthext, truncateindex, extflag, fullwidth = 0; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMGlyph *glyph; + int prevGlyphIndex; + FMFont font; + + state = &states[ nstates - 1 ]; + if( retextflag ) + *retextflag = 0; + if( retfullwidth ) + *retfullwidth = 0; + if( extwidth >= truncatewidth ) + return 0; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + if( !( state->font ) ) + return 0; + font = state->font; + if( stringlength <= 0 ) + stringlength = strlen( string ); + + truncatewidthext = truncatewidth - extwidth; + + advance = 0; + subpixel = 0; + truncateindex = 0; + extflag = 0; + for( index = 0 ; ; index++ ) + { + if( index >= stringlength ) + { + truncateindex = index; + fullwidth = advance + ( subpixel >= 32 ); + break; + } + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, false ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel ); + addGlyphAdvance( &advance, &subpixel, glyph ); + if( advance > truncatewidth ) + { + extflag = 1; + break; + } + if( advance <= truncatewidthext ) + { + truncateindex = index + 1; + fullwidth = advance + ( subpixel >= 32 ); + } + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + + if( retfullwidth ) + { + if( extflag ) + *retfullwidth = fullwidth; + else + *retfullwidth = fullwidth; + } + if( retextflag ) + *retextflag = extflag; + + return truncateindex; + } + + // Find text offset nearest to the given width + int getTextNearestOffset( int targetwidth, const char *string, int stringlength ) + { + int subpixel, index, advance, truncateindex, distance, bestdistance; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMGlyph *glyph; + int prevGlyphIndex; + FMFont font; + + state = &states[ nstates - 1 ]; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + if( !( state->font ) ) + return 0; + font = state->font; + if( stringlength <= 0 ) + stringlength = strlen( string ); + + advance = 0; + subpixel = 0; + truncateindex = 0; + bestdistance = abs( targetwidth ); + for( index = 0 ; index < stringlength ; index++ ) + { + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, false ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel ); + addGlyphAdvance( &advance, &subpixel, glyph ); + distance = abs( targetwidth - ( advance + ( subpixel >= 32 ) ) ); + if( distance > bestdistance ) + break; + bestdistance = distance; + truncateindex = index + 1; + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + + return truncateindex; + } + + //// + + static void flush( bool rendererFlush ) + { + // Flush texture updates + if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) ) + { + renderer.updateTexture( dirtyrect, texdata ); + // Reset dirty rect + dirtyrect[0] = width; + dirtyrect[1] = height; + dirtyrect[2] = 0; + dirtyrect[3] = 0; + } + // Flush buffered glyphs + if( rendererFlush ) renderer.flush( ); + } + + // Flush buffered texture updates, renderer->updateTexture() + void flushUpdate( ) + { + flush(false ); + } + + //// + + // Text metrics + + void getFontExtent( int *retascent, int *retdescent ) + { + FMFont font; + FMState *state = &states[ nstates - 1 ]; + if(state->font) + { + font = state->font; + + if( retascent ) + *retascent = -(int)ceilf( font.ascender * (float)state->size ); + if( retdescent ) + *retdescent = -(int)floorf( font.descender * (float)state->size ); + } + } + + void getFontLimits( int *retlimitminy, int *retlimitmaxy ) + { + FMFont font; + FMState *state; + + state = &states[ nstates - 1 ]; + if( !( state->font ) ) + return; + font = state->font; + + if( retlimitminy ) + *retlimitminy = -(int)ceilf( font.limitmaxy * state->size ); + if( retlimitmaxy ) + *retlimitmaxy = -(int)floorf( font.limitminy * state->size ); + + return; + } + + int getFontLineHeight( ) + { + FMFont font; + FMState *state; + + state = &states[ nstates - 1 ]; + if( !( state->font ) ) + return 0 ; + font = state->font; + return (int)ceilf( font.lineHeight * state->size ); + } + + + //// + + // Pull texture changes + const byte *getTextureData( int *width, int *height ) + { + if( width ) + *width = this.width; + if( height ) + *height = this.height; + return texdata; + } + + // Retrieve the dirty rectangle, telling fontmanager you will manually perform the update, returns 0 if no part of the texture requires an update + bool validateTexture( int *retdirtyrect ) + { + if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) ) + { + retdirtyrect[0] = dirtyrect[0]; + retdirtyrect[1] = dirtyrect[1]; + retdirtyrect[2] = dirtyrect[2]; + retdirtyrect[3] = dirtyrect[3]; + // Reset dirty rect + dirtyrect[0] = width; + dirtyrect[1] = height; + dirtyrect[2] = 0; + dirtyrect[3] = 0; + return true; + } + return false; + } + + // Returns current atlas size + void getAtlasSize( int *retwidth, int *retheight ) + { + *retwidth = this.width; + *retheight = this.height; + return; + } + + // Expands the atlas size + bool expandAtlas( int width, int height ) + { + width = Max( width, this.width ); + height = Max( height, this.height ); + + if( ( width == this.width ) && ( height == this.height ) ) + return true; + + // Flush all pending glyphs + flush(true); + + // Create new texture + if( renderer.resizeTexture( width, height ) ) + { + byte *data; + int i; + + // Copy old texture data over. + if( !( data = new byte[width * bytesperline] ) ) + return false; + for( i = 0 ; i < this.height ; i++ ) + { + byte * dst = &data[ (i * width) * bytesperpixel ]; + byte * src = &this.texdata[ i * this.bytesperline ]; + memcpy( dst, src, bytesperline); + if( width > this.width ) + memset( dst+bytesperline, 0, (width - this.width) * bytesperpixel ); + } + if( height > this.height ) + memset( &data[ width * this.height * bytesperpixel], 0, ( height - this.height ) *bytesperline ); + + delete this.texdata; + texdata = data; + + // Increase atlas size + atlas.expand( width, height ); + + // Add existing data as dirty. + dirtyrect[0] = 0; + dirtyrect[1] = 0; + dirtyrect[2] = this.width; + dirtyrect[3] = atlas.getAtlasMaxHeight(); + + this.width = width; + this.height = height; + this.bytesperline = this.width * bytesperpixel; + widthinv = 1.0f / this.width; + heightinv = 1.0f / this.height; + + return true; + } + return false; + } + + // Reset the whole fm + bool resetAtlas( int width, int height ) + { + // Flush all pending glyphs + flush(true); + + // Create new texture + if(renderer.resizeTexture( width, height ) ) + { + // Reset atlas + atlas.reset( width, height ); + + // Clear texture data. + texdata = renew texdata byte[width * height * bytesperpixel]; + if(!texdata) return 0; + memset( this.texdata, 0, width * height ); + + // Reset dirty rect + dirtyrect[0] = width; + dirtyrect[1] = height; + dirtyrect[2] = 0; + dirtyrect[3] = 0; + + // Reset cached glyphs + for(font : fontList) + { + int i; + font.glyphcount = 0; + for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ ) + font.hashtable[i] = -1; + } + renderer.resetImages( ); + + this.width = width; + this.height = height; + this.bytesperline = width * bytesperpixel; + this.widthinv = 1.0f / this.width; + this.heightinv = 1.0f / this.height; + + return true; + } + return false; + } + + //// + bool initPathDraw( FMPathDraw pathdraw ) + { + FMState *state = &states[ nstates - 1 ]; + FMFont font = state->font; + if(font) + { + pathdraw.prevGlyphIndex = -1; + pathdraw.middleAlign = font.getVertAlign(state->align, state->size); // font.middleAlign * (float)state->size; + return true; + } + return false; + } + + float pathDrawCharacter( FMPathDraw pathdraw, float x, float y, float vectorx, float vectory, int unicode ) + { + int subpixel; + FMState *state = &states[ nstates - 1 ]; + FMFont font = state->font; + FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, false ); + if( glyph ) + { + subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex ); + if(font.processImage) + { + FMGlyph *outlineGlyph = getGlyph(font, unicode, state->size, 0, true ); + if(outlineGlyph) + drawTextGlyphFloat(font, outlineGlyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign, true ); + } + drawTextGlyphFloat(font, glyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign, false ); + + subpixel += glyph->advance; + pathdraw.prevGlyphIndex = glyph->glyphindex; + } + else + { + subpixel = 0; + pathdraw.prevGlyphIndex = -1; + } + + return (float)subpixel * (1.0f/64.0f); + } + + float pathDrawPredictAdvance( FMPathDraw pathdraw, unichar unicode ) + { + int subpixel = 0; + FMState *state = &states[ nstates - 1 ]; + FMFont font = state->font; + FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, false ); + if( glyph ) + { + subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex ); + subpixel += glyph->advance; + } + return (float)subpixel * (1.0f/64.0f); + } +} diff --git a/ecere/src/gfx/newFonts/fontRenderer.ec b/ecere/src/gfx/newFonts/fontRenderer.ec new file mode 100644 index 0000000000..5d261f1cf3 --- /dev/null +++ b/ecere/src/gfx/newFonts/fontRenderer.ec @@ -0,0 +1,227 @@ +/* ***************************************************************************** + * Original Version Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +#undef __BLOCKS__ + +import "Color" + +#define _Noreturn + +#define set _set +#include +#include + +#include "gl123es.h" + +namespace gfx; + +#define Size Size_ +#define String String_ +#define Alignment Alignment_ +#include "cc.h" +#include "mm.h" +#undef Size +#undef String +#undef Alignment + +#undef set + +import "fmFontManager" +import "textureManager" +import "drawManager" + +public class FontRenderer : FontManagerRenderer +{ + DrawManager dm; + Texture texture; + int textureWidth, textureHeight; + int channelcount; + + ColorAlpha stateColor; + ColorAlpha stateExtColor; + ColorAlpha stateCursorColor; + uint32 stateLayer; + + stateColor = white; + stateCursorColor = white; + stateLayer = DM_LAYER_ABOVE; + +public: + + property DrawManager drawManager { set { dm = value; } } + + bool init(int channelCount) + { + this.channelcount = channelCount; + return true; + } + + ~FontRenderer() + { + delete texture; + } + + bool createTexture( int width, int height ) + { + IMGImage image + { + format = { width = width, height = height, type = grayScale, bytesPerPixel = channelcount, bytesPerLine = width }; + }; + + delete texture; + + texture = { }; + texture.build(image, 0, 0.0, 0 ); + + textureWidth = width; + textureHeight = height; + return true; + } + + int resizeTexture( int width, int height ) + { + dm.clearImages(); + // Reuse create to resize too. + return createTexture( width, height ); + } + + void updateTexture( int *rect, const byte* data ) + { + if(texture) + { +#if defined(_GLES) || defined(_GLES2) + int glformat = GL_ALPHA; +#else + int glformat = + #if ENABLE_GL_LEGACY + glCaps_legacyFormats ? GL_ALPHA : + #endif + GL_RED; +#endif + int w = rect[2] - rect[0]; + int h = rect[3] - rect[1]; + + + if( channelcount == 1 ); + else if( channelcount == 2 ) + { +#if !ENABLE_GL_LEGACY && !defined(_GLES) && !defined(_GLES2) + glformat = glCaps_legacyFormats ? GL_LUMINANCE_ALPHA : GL_RG; +#else + glformat = GL_LUMINANCE_ALPHA; +#endif + } + else if( channelcount == 3 ) + glformat = GL_RGB; + else if( channelcount == 4 ) + glformat = GL_RGBA; + + // FIXME: no glPushAttrib() in core profile +#if ENABLE_GL_LEGACY + if(glCaps_legacy) + { + glPushClientAttrib( GL_CLIENT_PIXEL_STORE_BIT ); + glPushAttrib( GL_TEXTURE_BIT ); + } +#endif + + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glBindTexture( GL_TEXTURE_2D, texture.glTex ); +#if ENABLE_GL_LEGACY + if(glCaps_legacy) + { + glPixelStorei( GL_UNPACK_ROW_LENGTH, textureWidth ); + glTexSubImage2D( GL_TEXTURE_2D, 0, rect[0], rect[1], w, h, glformat, GL_UNSIGNED_BYTE, data + (rect[1] * textureWidth + rect[0]) * channelcount); + } + else +#endif + { + int row = w * channelcount; + byte * tmp = new byte[h * row]; + int y; + for(y = 0; y < h; y++) + memcpy(tmp + y * row, data + textureWidth * (y + rect[1]) + rect[0], row); + glTexSubImage2D( GL_TEXTURE_2D, 0, rect[0], rect[1], w, h, glformat, GL_UNSIGNED_BYTE, tmp); + delete tmp; + } + +#if ENABLE_GL_LEGACY + if(glCaps_legacy) + { + glPopAttrib(); + glPopClientAttrib(); + } + else +#endif + glPixelStorei( GL_UNPACK_ALIGNMENT, 4 ); + + #if 0 + IMGImage image; + image.format.width = textureWidth; + image.format.height = textureHeight; + image.format.type = channelcount == 4 ? IMG_FORMAT_TYPE_RGBA32 : IMG_FORMAT_TYPE_GRAYSCALE; + image.format.bytesPerPixel = channelcount; + image.format.bytesPerLine = image.format.width * image.format.bytesPerPixel; + image.data = data; + imgWritePngFile( "zzz2.png", &image, 1.0 ); + #endif + } + } + + void flush( ) + { + dm.flushImages( ); + } + + int registerImage( int offsetx, int offsety, int width, int height ) + { + return dm.defineImage( texture, offsetx, offsety, width, height, true, stateLayer ); + } + + void drawImage( int targetx, int targety, int imageIndex, bool useExtColor ) + { + dm.drawImage( imageIndex, targetx, targety, 0,1, useExtColor ? stateExtColor : stateColor ); + } + + void drawImageCursor( int targetx, int targety, int imageIndex ) + { + dm.drawImage( imageIndex, targetx, targety, 0,1, stateCursorColor ); + } + + void drawImageAlt( byte *texdata, int targetx, int targety, int offsetx, int offsety, int width, int height ) + { + + } + + void drawImageFloat( float targetx, float targety, float angsin, float angcos, int imageIndex, bool useExtColor ) + { + dm.drawImage( imageIndex, targetx, targety, angsin, angcos, useExtColor ? stateExtColor : stateColor ); + } + + void resetImages( ) + { + dm.resetImages(); + } + + void setColor( ColorAlpha color ) + { + stateColor = color; + } + + void setExtColor( ColorAlpha color ) + { + stateExtColor = color; + } + + void setCursorColor( ColorAlpha color ) + { + stateCursorColor = color; + } + + void setLayer( uint32 layerIndex ) + { + stateLayer = layerIndex; + } +} diff --git a/ecere/src/gfx/newFonts/textureManager.ec b/ecere/src/gfx/newFonts/textureManager.ec new file mode 100644 index 0000000000..f13f448f2b --- /dev/null +++ b/ecere/src/gfx/newFonts/textureManager.ec @@ -0,0 +1,170 @@ +import "Display" + +#include +#include + +#include "gl123es.h" + +namespace gfx; + +#include "cc.h" + +struct IMGFormat +{ + int width; + int height; + IMGFormatType type; + int bytesPerPixel; + int bytesPerLine; +}; + +enum IMGFormatType { any, rgb24, bfr24, rgba32, bgra32, grayScale }; + +struct IMGImage +{ + IMGFormat format; + void *data; +}; + +class TextureFlags : uint32 { bool invalid:1; } + +class Texture : struct +{ + GLuint glTex; + int width; + int height; + float widthinv; + float heightinv; + TextureFlags flags; + SwizzleMode swizzle; + uint32 orderMask; + + flags = { invalid = true }; + +public: + + property uint32 orderMask { set { orderMask = value; } } + + static bool setData( IMGImage image, int internalformat, int mipmapmode, float anisotropy, int maxresolution ) + { + int width, height; + int glformat; + SwizzleMode swizzle = 0; + + if( image.format.bytesPerPixel == 1 ) + { +#if defined(_GLES) || defined(_GLES2) + glformat = GL_ALPHA, swizzle = 1; +#else + + #if ENABLE_GL_LEGACY + if(glCaps_legacyFormats) + glformat = GL_ALPHA, swizzle = 1; + else + #endif + glformat = GL_RED, swizzle = 2; +#endif + } + else if( image.format.bytesPerPixel == 2 ) + { +#if defined(_GLES) || defined(_GLES2) + glformat = GL_LUMINANCE_ALPHA; +#else + #if ENABLE_GL_LEGACY + if(glCaps_legacyFormats) + glformat = GL_LUMINANCE_ALPHA; + else + #endif + glformat = GL_RG; +#endif + } + else if( image.format.bytesPerPixel == 3 ) + glformat = GL_RGB; + else if( image.format.bytesPerPixel == 4 ) + glformat = GL_RGBA; + else + { + fprintf( stderr, "ERROR: Bad image format.\n" ); + return false; + } + if( internalformat == -1 ) + internalformat = glformat; + + width = image.format.width; + height = image.format.height; + if(!glCaps_nonPow2Textures) + { + if( !( ccIsPow2Int32( width ) ) || !( ccIsPow2Int32( height ) ) ) + { + fprintf( stderr, "ERROR: Non-power of two texture used and GL_ARB_texture_non_power_of_two not supported.\n" ); + return false; + } + } + + glGenTextures( 1, &this.glTex ); + glBindTexture( GL_TEXTURE_2D, this.glTex ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + if(image.data) + memset(image.data, 0, image.format.width * image.format.height); + glTexImage2D( GL_TEXTURE_2D, 0, internalformat, image.format.width, image.format.height, 0, glformat, GL_UNSIGNED_BYTE, image.data ); + +#if GL_TEXTURE_MAX_ANISOTROPY_EXT + if(anisotropy > 1.0) + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy ); +#endif + + this.width = width; + this.height = height; + this.swizzle = swizzle; + widthinv = 1.0f / (float)width; + heightinv = 1.0f / (float)height; + + return true; + } + + bool build( IMGImage image, int mipmapmode, float anisotropy, int maxresolution ) + { + if(!flags.invalid) + glDeleteTextures( 1, &glTex ); + if( !setData( image, -1, mipmapmode, anisotropy, maxresolution ) ) + { + fprintf( stderr, "ERROR: Failed to create texture.\n" ); + return false; + } + flags.invalid = false; + + return true; + } + + bool load( const String path, int mipmapmode, float anisotropy, int maxresolution ) + { + bool result = false; + IMGImage image; + + #if TEXMG_ENABLE_PNG_SUPPORT + if( !( imgReadPngFile( &image, path, 0 ) ) ) + { + fprintf( stderr, "ERROR: Loading texture %s failed.\n", path ); + return false; + } + #else + fprintf( stderr, "ERROR: File support disabled, %s:%d\n", __FILE__, __LINE__ ); + return false; + #endif + + result = build( image, mipmapmode, anisotropy, maxresolution ); + if( !result ) + fprintf( stderr, "ERROR: Bad format for texture %s.\n", path ); + delete image.data; + + return result; + } + + ~Texture() + { + if( !flags.invalid ) + glDeleteTextures( 1, &glTex ); + } +} diff --git a/ecere/src/gui/Anchor.ec b/ecere/src/gui/Anchor.ec index a2267521b1..1b12b080bc 100644 --- a/ecere/src/gui/Anchor.ec +++ b/ecere/src/gui/Anchor.ec @@ -27,7 +27,7 @@ public struct AnchorValue get { return (double) percent; } } - const char * OnGetString(char * stringOutput, void * fieldData, bool * needClass) + const char * OnGetString(char * stringOutput, void * fieldData, ObjectNotationType * onType) { if(type == offset) { @@ -56,7 +56,7 @@ public struct AnchorValue } } } - if(needClass) *needClass = false; + if(onType) *onType = none; // TODO: Better document how OnGetString can modify this... return stringOutput; } @@ -119,7 +119,7 @@ public struct MiddleAnchorValue get { return (double) percent; } } - const char * OnGetString(char * stringOutput, void * fieldData, bool * needClass) + const char * OnGetString(char * stringOutput, void * fieldData, ObjectNotationType * onType) { if(type == middleRelative) { @@ -148,7 +148,7 @@ public struct MiddleAnchorValue { sprintf(stringOutput, "%d", distance); } - if(needClass) *needClass = false; + if(onType) *onType = none; return stringOutput; } @@ -176,11 +176,11 @@ public struct Anchor union { AnchorValue top; MiddleAnchorValue vert; }; AnchorValue right, bottom; - const char * OnGetString(char * stringOutput, void * fieldData, bool * needClass) + const char * OnGetString(char * stringOutput, void * fieldData, ObjectNotationType * onType) { char tempString[256]; const char * anchorValue; - bool subNeedClass; + ObjectNotationType subNeedClass = none; stringOutput[0] = 0; tempString[0] = '\0'; @@ -250,8 +250,8 @@ public struct Anchor { char tempString[MAX_F_STRING] = ""; - bool needClass = false; - const char * result = OnGetString(tempString, null, &needClass); + ObjectNotationType onType = none; + const char * result = OnGetString(tempString, null, &onType); if(result) string = result; } comboBox.contents = string; @@ -368,8 +368,8 @@ private class AnchorButton : Button { char tempString[1024] = ""; - bool needClass = false; - const char * string = anchor.OnGetString(tempString, null, &needClass); + ObjectNotationType onType = none; + const char * string = anchor.OnGetString(tempString, null, &onType); anchorDropBox.contents = string; } @@ -491,8 +491,8 @@ private class AnchorRelButton : Button { char tempString[1024] = ""; - bool needClass = false; - const char * string = anchor.OnGetString(tempString, null, &needClass); + ObjectNotationType onType = none; + const char * string = anchor.OnGetString(tempString, null, &onType); anchorDropBox.contents = string; } @@ -607,8 +607,8 @@ private class AnchorDropBox : DropBox else { char tempString[1024] = ""; - bool needClass = false; - const char * string = anchor.OnGetString(tempString, null, &needClass); + ObjectNotationType onType = none; + const char * string = anchor.OnGetString(tempString, null, &onType); dropBox.contents = string; } return true; diff --git a/ecere/src/gui/Cursor.ec b/ecere/src/gui/Cursor.ec index b1a880cb59..0336a11992 100644 --- a/ecere/src/gui/Cursor.ec +++ b/ecere/src/gui/Cursor.ec @@ -20,6 +20,31 @@ private: char * bitmapName; SystemCursor systemCursor; public: - bool Load(const char * name, int hotSpotX, int hotSpotY, byte * paletteShades) { return false; } + bool Load(const char * name, int hotSpotX, int hotSpotY, byte * paletteShades, DisplaySystem ds) + { + Bitmap bitmap { }; + if(bitmap.LoadT(name, null, ds)) + { + this.bitmap = bitmap; + this.hotSpotX = hotSpotX; + this.hotSpotY = hotSpotY; + this.paletteShades = paletteShades; + return true; + } + delete bitmap; + return false; + } + void Free() + { + if(bitmap) + { + bitmap.Free(); + delete bitmap; + } + } + ~Cursor() + { + Free(); + } property SystemCursor systemCursor { set { systemCursor = value; } } }; diff --git a/ecere/src/gui/Desktop3D.ec b/ecere/src/gui/Desktop3D.ec index 45085a8516..6b731051d0 100644 --- a/ecere/src/gui/Desktop3D.ec +++ b/ecere/src/gui/Desktop3D.ec @@ -42,6 +42,7 @@ static bool Window3D_Setup(Window window, bool positionChildren) if(window.parent == virtualDesktop) { window.display = Display { }; + incref window.display; if(window.display) { int x,y,w,h; diff --git a/ecere/src/gui/GuiApplication.ec b/ecere/src/gui/GuiApplication.ec index a0b0cd199a..18a019c2d3 100644 --- a/ecere/src/gui/GuiApplication.ec +++ b/ecere/src/gui/GuiApplication.ec @@ -47,7 +47,7 @@ namespace gui; #define SOCKLEN_TYPE int #define WIN32_LEAN_AND_MEAN #define String _String -#include +#include #undef String #elif defined(__unix__) || defined(__APPLE__) @@ -330,76 +330,74 @@ public class GuiApplication : Application void PreserveAndDrawCursor() { - /* - if(!acquiredWindow && cursorUpdate && currentCursor && currentCursor->bitmap) + if(!acquiredWindow && cursorUpdate && currentCursor && currentCursor.bitmap) { + Bitmap bitmap = currentCursor.bitmap; int mouseX, mouseY; Surface surface; - Box against = {0,0, desktop.w-1,desktop.h-1}; - Box box = {0, 0, currentCursor->bitmap->width,currentCursor->bitmap->height}; + Box against = {0,0, desktop.size.w-1,desktop.size.h-1}; + Box box = {0, 0, bitmap.width, bitmap.height}; + Display display = desktop.display; + DisplayFlags flags = display.flags; - interfaceDriver->GetMousePosition(&mouseX, &mouseY); + interfaceDriver.GetMousePosition(&mouseX, &mouseY); - mouseX -= currentCursor->hotSpotX; - mouseY -= currentCursor->hotSpotY; + mouseX -= currentCursor.hotSpotX; + mouseY -= currentCursor.hotSpotY; // Preserve Background - if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING)) + if(!flags.flipping) { cursorBackgroundX = mouseX; cursorBackgroundY = mouseY; - cursorBackgroundW = currentCursor->bitmap->width; - cursorBackgroundH = currentCursor->bitmap->height; - eDisplay_Grab(desktop.display, cursorBackground, - mouseX, mouseY, cursorBackgroundW, cursorBackgroundH); + cursorBackgroundW = bitmap.width; + cursorBackgroundH = bitmap.height; + display.Grab(cursorBackground, mouseX, mouseY, cursorBackgroundW, cursorBackgroundH); } - eBox_ClipOffset(&box, &against, mouseX, mouseY); + box.ClipOffset(&against, mouseX, mouseY); - if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING)) - eDisplay_StartUpdate(desktop.display); + if(!flags.flipping) + display.StartUpdate(); // Display Cursor - surface = eDisplay_GetSurface(desktop.display, mouseX, mouseY, &box); + surface = display.GetSurface(mouseX, mouseY, box); if(surface) { - eSurface_SetForeground(surface, WHITE); - eSurface_Blit(surface, currentCursor->bitmap, 0,0, 0,0, - currentCursor->bitmap->width,currentCursor->bitmap->height); - eInstance_Delete(surface); + surface.foreground = white; + surface.Blit(bitmap, 0,0, 0,0, + bitmap.width, bitmap.height); + delete surface; - if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING)) + if(!flags.flipping) { box.left += mouseX; box.right += mouseX; box.top += mouseY; box.bottom += mouseY; - eDisplay_Update(desktop.display, &box); + display.Update(box); } } - if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING)) - eDisplay_EndUpdate(desktop.display); + if(!flags.flipping) + display.EndUpdate(); } - */ } void RestoreCursorBackground() { - /* // Restore Cursor Background if(cursorBackground && desktop.active) { Box box = {0, 0, cursorBackgroundW-1,cursorBackgroundH-1}; - Box against = {0,0, desktop.w-1,desktop.h-1}; + Box against = {0,0, desktop.size.w-1,desktop.size.h-1}; Surface surface; - eBox_ClipOffset(&box, &against, cursorBackgroundX, cursorBackgroundY); - if((surface = eDisplay_GetSurface(desktop.display, cursorBackgroundX,cursorBackgroundY, &box))) + box.ClipOffset(against, cursorBackgroundX, cursorBackgroundY); + if((surface = desktop.display.GetSurface(cursorBackgroundX, cursorBackgroundY, &box))) { - eSurface_Blit(surface, cursorBackground, 0, 0, 0,0, cursorBackgroundW,cursorBackgroundH); - eInstance_Delete(surface); + surface.Blit(cursorBackground, 0, 0, 0,0, cursorBackgroundW, cursorBackgroundH); + delete surface; } } - */ } bool IsModeSwitching() @@ -759,13 +757,13 @@ public: } } - for(child = desktop.children.first; child; child = child.next) - if(child.created && child.visible && !child.interim) + for(child = *&desktop.children.first; child; child = *&child.next) + if(*&child.created && *&child.visible && !(*&child.style).interim) break; if(!child) break; #if !defined(__EMSCRIPTEN__) - for(window = desktop.children.first; window; window = window.next) + for(window = *&desktop.children.first; window; window = *&window.next) if(window.mutex) window.mutex.Wait(); #endif UpdateDisplay(); @@ -816,6 +814,17 @@ public: void Wait(void) { + static Time lastTime = 0; + + Time time = GetTime(); + if(!lastTime) lastTime = time; + + if((double)(time - lastTime) > 1.0 / Max(18.2, (double)timerResolution)) + { + lastTime = time; + return; + } + #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) if(xGlobalDisplay) XUnlockDisplay(xGlobalDisplay); @@ -840,6 +849,7 @@ public: if(xGlobalDisplay) XLockDisplay(xGlobalDisplay); #endif + lastTime = time; } bool ProcessInput(bool useProcessAll) @@ -923,7 +933,7 @@ public: { Window window; - for(window = desktop.children.first; window; window = window.next) + for(window = *&desktop.children.first; window; window = *&window.next) { #if !defined(__EMSCRIPTEN__) if(window.mutex) window.mutex.Wait(); @@ -934,10 +944,6 @@ public: interfaceDriver.Lock(window); if(window.display) { - if(window.display.current) - { - printf("bug"); - } window.display.Lock(true); window.UpdateDisplay(); window.display.Unlock(); @@ -1618,3 +1624,14 @@ private void emscripten_main_loop_callback() guiApp.UpdateDisplay(); } #endif + +#if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D) +import "Quaternion" + +Euler compass; + +public void QueryCompass(Euler value) +{ + value = compass; +} +#endif diff --git a/ecere/src/gui/Key.ec b/ecere/src/gui/Key.ec index 74ac8a9ddb..1cde441c35 100644 --- a/ecere/src/gui/Key.ec +++ b/ecere/src/gui/Key.ec @@ -40,22 +40,22 @@ public: } }; - const char * OnGetString(char * stringOutput, void * fieldData, bool * needClass) + const char * OnGetString(char * stringOutput, void * fieldData, ObjectNotationType * onType) { const char * string; - if((string = ((PredefinedKey)this).class::OnGetString(stringOutput, null, needClass /*null*/))) + if((string = ((PredefinedKey)this).class::OnGetString(stringOutput, null, onType /*null*/))) { - if(needClass) *needClass = false; + if(onType) *onType = none; return string; } - else if((string = ((KeyCode)this).class::OnGetString(stringOutput, null, needClass /*null*/))) + else if((string = ((KeyCode)this).class::OnGetString(stringOutput, null, onType /*null*/))) { - if(needClass) *needClass = false; + if(onType) *onType = none; return string; } else { - return class::OnGetString(stringOutput, fieldData, needClass); + return class::OnGetString(stringOutput, fieldData, onType); } } @@ -252,14 +252,44 @@ public enum KeyCode : Key menu2 = f3, menu3 = f4, menu4 = f5, - select = enter + select = enter, + + + bassBoost = 0x214, + bassDown = 0x215, + bassUp = 0x216, + browserBackward = 0x217, + browserFavorites = 0x218, + browserForward = 0x219, + browserHome = 0x21A, + browserRefresh = 0x21B, + browserSearch = 0x21C, + browserStop = 0x21D, + //delete = 0x0, + //flip3D = 0x0, + launchApp1 = 0x21E, + launchApp2 = 0x21F, + launchMail = 0x220, + launchMediaSelect = 0x221, + //mediaNextTrack = 0x0, + //mediaPlayPause = 0x0, + //mediaPreviousTrack = 0x0, + //mediaStop = 0x0, + micVolumeDown = 0x222, + micMute = 0x223, + micVolumeUp = 0x224, + trebleDown = 0x225, + trebleUp = 0x226 + //volumeDown = 0x0, + //volumeMute = 0x0, + //volumeUp = 0x0 }; public class Modifiers { - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { - if(needClass) *needClass = false; + if(onType) *onType = none; return 0; }; diff --git a/ecere/src/gui/Timer.ec b/ecere/src/gui/Timer.ec index 27cc9fdb00..55b40cba9c 100644 --- a/ecere/src/gui/Timer.ec +++ b/ecere/src/gui/Timer.ec @@ -40,7 +40,7 @@ private: Timer prev, next; public Seconds _delay; // NOTE: Made public for Emscripten alignment issues Time lastTime; - Window window; + void * window; bool dispatched; bool started; }; diff --git a/ecere/src/gui/Window.ec b/ecere/src/gui/Window.ec index 075a212ecd..0491816cc3 100644 --- a/ecere/src/gui/Window.ec +++ b/ecere/src/gui/Window.ec @@ -33,7 +33,7 @@ import "Win32Interface" import "Desktop3D" #endif -#if !defined(ECERE_VANILLA) && !defined(ECERE_NOTRUETYPE) +#if !defined(ECERE_VANILLA) && !defined(ECERE_NOTRUETYPE) && !defined(__EMSCRIPTEN__) import "FormDesigner" #endif @@ -759,7 +759,7 @@ private: } //#if !defined(ECERE_VANILLA) - const char * OnGetString(char * stringOutput, void * fieldData, bool * needClass) + const char * OnGetString(char * stringOutput, void * fieldData, ObjectNotationType * onType) { if(this == activeDesigner) return "(Desktop)"; @@ -771,7 +771,7 @@ private: } //#endif -#if !defined(ECERE_VANILLA) && !defined(ECERE_NOTRUETYPE) +#if !defined(ECERE_VANILLA) && !defined(ECERE_NOTRUETYPE) && !defined(__EMSCRIPTEN__) bool OnGetDataFromString(const char * string) { FormDesigner designer = (FormDesigner)activeDesigner.classDesigner; @@ -1695,8 +1695,8 @@ private: } // Adjust all children - for(child = children.first; child; child = child.next) - child.SetPosition(child.position.x, child.position.y, child.size.w, child.size.h, false, true, true); + for(child = *&children.first; child; child = *&child.next) + child.SetPosition((&child.position)->x, (&child.position)->y, (&child.size)->w, (&child.size)->h, false, true, true); UpdateCaret(false, false); } @@ -1763,7 +1763,7 @@ private: // windowResized = realResized || force; windowResized = size.w != w || size.h != h || force; - if(rootWindow != this && display && !display.flags.flipping && scrolledPos.x != MININT) + if(rootWindow != this && display && !display.flags.flipping && scrolledPos.x != MININT && this.box.left != MAXINT) { if(style.nonClient) { @@ -1865,7 +1865,7 @@ private: #if !defined(__EMSCRIPTEN__) !guiApp.fullScreenMode && #endif - this != guiApp.desktop && (windowResized || windowMoved)) + this != guiApp.desktop && (windowResized || windowMoved) && visible) for(child = parent.children.first; child && child != this; child = child.next) if(child.rootWindow) guiApp.interfaceDriver.UpdateRootWindow(child.rootWindow); @@ -2615,7 +2615,7 @@ private: Window document = cycle.data; if(!document.style.nonClient && document.style.isActiveClient && document.visible) { - char name[2048], caption[2048]; + char name[2060], caption[2048]; document.FigureCaption(caption); sprintf(name, "%d %s", id+1, caption); windowMenu.AddDynamic(MenuItem @@ -2682,6 +2682,9 @@ private: void _ShowDecorations(Box box, bool post) { +#if defined(__LUMIN__) + return; +#endif if(rootWindow == this && nativeDecorations && !is3D) return; if(visible && this != guiApp.desktop) { @@ -2725,6 +2728,7 @@ private: surface.DrawingChar(' '); if(this == rootWindow) { +#if !defined(__LUMIN__) if(style.drawBehind || background.a) { int a = background.a; @@ -2733,6 +2737,7 @@ private: surface.Clear(colorBuffer); surface.SetBackground(background); } +#endif } else if(background.a) { @@ -2762,7 +2767,7 @@ private: // Default Settings surface.TextFont(usedFont.font); surface.TextOpacity(false); - surface.outlineColor = black; + surface.outlineColor = background.color; //black; OnRedraw(surface); @@ -2791,7 +2796,7 @@ private: surface.TextFont(usedFont.font); surface.TextOpacity(false); - surface.outlineColor = black; + surface.outlineColor = background.color; //black; OnDrawOverChildren(surface); @@ -4917,18 +4922,29 @@ private: if(sbv && !guiApp.windowScrolling) result = sbv.Action((key == ctrlUp) ? up : down, 0, key); break; - case wheelUp: case wheelDown: - if(sbv && !guiApp.windowScrolling) - { - result = sbv.Action((key == wheelUp) ? wheelUp : wheelDown, 0, key); - // Do we want to do a consequential move regardless of result in this case? - ConsequentialMouseMove(false); - } - break; case ctrlPageUp: case ctrlPageDown: if(sbh && !guiApp.windowScrolling) result = sbh.Action((key == ctrlPageUp) ? up : down, 0, key); break; + default: + switch(key.code) + { + case wheelUp: case wheelDown: + if(!key.shift && sbv && !guiApp.windowScrolling) + { + result = sbv.Action((key.code == wheelUp) ? wheelUp : wheelDown, 0, key); + // Do we want to do a consequential move regardless of result in this case? + ConsequentialMouseMove(false); + } + else if(key.shift && sbh && !guiApp.windowScrolling) + { + result = sbh.Action((key.code == wheelUp) ? wheelUp : wheelDown, 0, key); + // Do we want to do a consequential move regardless of result in this case? + ConsequentialMouseMove(false); + } + break; + } + break; } if(result) { @@ -5077,6 +5093,7 @@ private: if(!displaySystem) { displaySystem = DisplaySystem { glCapabilities = glCapabilities }; + incref displaySystem; if(!displaySystem.Create(dDriver.name, guiApp.fullScreenMode ? windowHandle : windowHandle /*null*/, guiApp.fullScreenMode)) { delete displaySystem; @@ -5085,10 +5102,14 @@ private: if(displaySystem) { display = Display { alphaBlend = alphaBlend, useSharedMemory = useSharedMemory, glCapabilities = glCapabilities, windowDriverData = windowData }; + incref display; if(display.Create(displaySystem, windowHandle)) result = true; else { +#ifdef _DEBUG + PrintLn("failed to create display!"); +#endif delete display; } } @@ -5562,6 +5583,12 @@ private: if(interimWindow && interimWindow.master) interimMaster = interimWindow.master.rootWindow; + if(active == false && activateRoot && !swap) + { + if(guiApp.interimWindow) + guiApp.interimWindow.ActivateEx(false, false, false, false, window, swap); + } + if(active && state == minimized && window.parent) // && (!window.nativeDecorations || window.rootWindow != window) // SetState(normal, false, 0); SetState(lastState, false, 0); @@ -5601,6 +5628,7 @@ private: // Testing & FindModal() here: broke reactivating when a modal dialog is up (didn't root activate dialog) result = ActivateEx(active, active, false, activateRoot /*&& FindModal()*/, window, swap); + // TOCHECK: This logic should have de-activated interim windows, causing their destruction? if(interimWindow == this && interimMaster && !active) { while(interimMaster && interimMaster.interim && interimMaster.master) @@ -5876,7 +5904,7 @@ private: ReleaseCapture(); //guiApp.windowCaptured = null; - if(rootWindow != this && rootWindow) + if(rootWindow != this && rootWindow && !noConsequential) rootWindow.ConsequentialMouseMove(false); rootWindow = null; @@ -6226,7 +6254,7 @@ private: return Redraw((box == null) ? this.box : box); } - void SetMousePosition(int x, int y) + public void SetMousePosition(int x, int y) { guiApp.interfaceDriver.SetMousePosition(x + absPosition.x + clientStart.x, y + absPosition.y + clientStart.y); } @@ -6594,6 +6622,11 @@ public: if(this == rootWindow) Flash(); } + else + { + if(style.interim && !guiApp.interimWindow) + guiApp.interimWindow = this; + } } if(!destroyed && !noConsequential) @@ -7556,7 +7589,7 @@ public: if(closing) return false; - if(terminateX > 1) + if(terminateX > 1 || destroyed) return true; closing = true; @@ -9419,7 +9452,8 @@ public: Update(null); // rootWindow. - ConsequentialMouseMove(false); + if(!noConsequential) + ConsequentialMouseMove(false); } firewatchers; @@ -9640,7 +9674,7 @@ public: set { style.showInTaskBar = value; -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) Win32UpdateStyle(this); #endif } @@ -9761,6 +9795,7 @@ public: property int documentID { get { return documentID; } }; property Window previous { get { return prev; } } property Window next { get { return next; } } + // NOTE: This property is really slow and should not be used in iteration, iteration should be done with link property Window nextSlave { get { OldLink link = master ? master.slaves.FindLink(this) : null; return (link && link.next) ? link.next.data : null; } } property PopupMenu menuBar { get { return menuBar; } } property ScrollBar sbv { get { return sbv; } } @@ -9819,7 +9854,7 @@ public: { get { return (bool)nativeDecorations; } set { nativeDecorations = value; } -#if !defined(ECERE_VANILLA) && !defined(ECERE_NOTRUETYPE) +#if !defined(ECERE_VANILLA) && !defined(ECERE_NOTRUETYPE) && !defined(__EMSCRIPTEN__) isset { //return (nativeDecorations && (rootWindow == this || (formDesigner && activeDesigner && ((FormDesigner)activeDesigner.classDesigner).form && parent == ((FormDesigner)activeDesigner.classDesigner).form.parent))) != style.fixed; @@ -9967,7 +10002,7 @@ private: void * windowData; CreationActivationOption creationActivation; GLCapabilities glCapabilities; - glCapabilities = { true, true, true, true, true, true, true, true, true /*false*/, true, true, true, true, true, true, true }; + glCapabilities = { true, true, true, true, true, true, true, true, true /*false*/, true, true, true, true, true, true, true, true, true, ms16 }; struct { bool active:1; // true if window and ancestors are active @@ -10047,7 +10082,7 @@ public class CommonControl : Window if(created) CommonControl::OnDestroy(); delete toolTip; toolTip = value ? ToolTip { tip = value; } : null; - incref toolTip; + if(toolTip) incref toolTip; if(created) CommonControl::OnCreate(); } get { return toolTip ? toolTip.tip : null; } @@ -10076,7 +10111,7 @@ public class CommonControl : Window public class Percentage : float { - const char * OnGetString(char * string, float * fieldData, bool * needClass) + const char * OnGetString(char * string, float * fieldData, ObjectNotationType * onType) { int c; int last = 0; diff --git a/ecere/src/gui/controls/Button.ec b/ecere/src/gui/controls/Button.ec index 6d0f0295c8..016a7c276c 100644 --- a/ecere/src/gui/controls/Button.ec +++ b/ecere/src/gui/controls/Button.ec @@ -792,7 +792,7 @@ public class Button : CommonControl switch(key) { case left: case up: - if(parent.CycleChildren(false, false, false, !(buttonStyle.bevelOver && buttonStyle.radio))) + if(parent.CycleChildren(false, parent._class == class(ScrollBar), false, !(buttonStyle.bevelOver && buttonStyle.radio))) { if(buttonStyle.bevelOver && buttonStyle.radio) { @@ -807,7 +807,7 @@ public class Button : CommonControl } break; case right: case down: - if(parent.CycleChildren(true, false, false, !(buttonStyle.bevelOver && buttonStyle.radio))) + if(parent.CycleChildren(true, parent._class == class(ScrollBar), false, !(buttonStyle.bevelOver && buttonStyle.radio))) { if(buttonStyle.bevelOver && buttonStyle.radio) { diff --git a/ecere/src/gui/controls/DropBox.ec b/ecere/src/gui/controls/DropBox.ec index a276011065..95615a287a 100644 --- a/ecere/src/gui/controls/DropBox.ec +++ b/ecere/src/gui/controls/DropBox.ec @@ -458,9 +458,10 @@ private: { if(pulledWindow) { - int lx = absPosition.x - guiApp.desktop.absPosition.x; - int ly = absPosition.y - guiApp.desktop.absPosition.y; - int availHeight = pulledWindow.parent.clientSize.h; + Window pwParent = pulledWindow.parent; + int lx = absPosition.x - pwParent.absPosition.x; + int ly = absPosition.y - pwParent.absPosition.y; + int availHeight = pwParent.clientSize.h; int height = pulledWindow.clientSize.h; // If it won't fit below but fits above, place it above... diff --git a/ecere/src/gui/controls/EditBox.ec b/ecere/src/gui/controls/EditBox.ec index c4376e1a6d..b3f8b915ba 100644 --- a/ecere/src/gui/controls/EditBox.ec +++ b/ecere/src/gui/controls/EditBox.ec @@ -2660,6 +2660,23 @@ private: if(x + (((half && len == 1) ? (w / 2) : w)) >= position) { int lastW; + + { + int loLen = 0, hiLen = len; + + while(hiLen - loLen > 2) + { + int oh, sw; + int tLen = (loLen + hiLen) >> 1; + FontExtent(display, font, line.buffer + start, tLen, &sw, null, 0, null, &oh); + + if(x + sw > position) + hiLen = tLen; + else + loLen = tLen; + } + len = hiLen; + } while(len > 0) { int a = start + len; @@ -3241,7 +3258,7 @@ private: #ifdef _DEBUG //PrintLn("OnKeyHit: code = ", key.code, ", mods = ", key.modifiers, ", ch = ", (int)ch); #endif - if(!ch && !key.alt && !key.ctrl) + if(!ch && !key.alt /*&& !key.ctrl*/) { key.code = (SmartKey)key.code; } @@ -5394,7 +5411,7 @@ public: { if(x + space.w >= this.viewX + clientSize.w && clientSize.w >= space.w) viewX = x - clientSize.w+space.w; - if(x < this.viewX + clientSize.w/2 - space.w) + if(x < this.viewX + (selX > this.x ? clientSize.w/2 - space.w : space.w)) viewX = Max(0, x - clientSize.w/2 + space.w); } } diff --git a/ecere/src/gui/controls/ListBox.ec b/ecere/src/gui/controls/ListBox.ec index c19b35bfec..316e66b849 100644 --- a/ecere/src/gui/controls/ListBox.ec +++ b/ecere/src/gui/controls/ListBox.ec @@ -34,6 +34,12 @@ class ListBoxBits bool sortable:1, noDragging:1, fillLastField:1, expandOnAdd:1; }; +public struct DataFieldSort +{ + DataField field; + int order; +}; + public class DataDisplayFlags { public bool selected:1, fullRow:1, current:1, active:1, dropBox:1, header:1, firstField:1; @@ -429,7 +435,7 @@ public: } // TESTING THIS HERE... if(listBox.created) - listBox.Sort(listBox.sortField, listBox.sortField.sortOrder); + listBox.MultiSort(listBox.sortFields); { int headerSize = ((listBox.style.header) ? listBox.rowHeight : 0); @@ -857,55 +863,61 @@ private: return !this || (!collapsed && (!parent || parent.IsExpanded())); } - int Compare(DataRow b, DataField sortField) + int Compare(DataRow b, Array sortFields) { - int result = 0; - ListBoxCell cell1, cell2; - uint index; - for(index = 0, cell1 = cells.first, cell2 = b.cells.first; - index != sortField.index; - index++, cell1 = cell1.next, cell2 = cell2.next); - if(noneRow && !b.noneRow) return -1; - else if(!noneRow && b.noneRow) return 1; - else if(noneRow && b.noneRow) return 0; - - if(!cell1.isSet && !cell2.isSet) - result = 0; - else if(!cell1.isSet) - result = -1; - else if(!cell2.isSet) - result = 1; - else if(sortField.dataType._vTbl[__ecereVMethodID_class_OnCompare]) + Iterator it { sortFields }; + while(it.Prev()) { - if(sortField.dataType.type == normalClass || sortField.dataType.type == noHeadClass) - { - result = ((int (*)(void *, void *, void *))(void *)sortField.dataType._vTbl[__ecereVMethodID_class_OnCompare])(sortField.dataType, - cell1.isSet ? cell1.data[0] : null, - cell2.isSet ? cell2.data[0] : null); - } - else + int result = 0; + DataField sortField = it.data.field; + ListBoxCell cell1, cell2; + uint index; + for(index = 0, cell1 = cells.first, cell2 = b.cells.first; + index != sortField.index; + index++, cell1 = cell1.next, cell2 = cell2.next); + if(noneRow && !b.noneRow) return -1; + else if(!noneRow && b.noneRow) return 1; + else if(noneRow && b.noneRow) return 0; + + if(!cell1.isSet && !cell2.isSet) + result = 0; + else if(!cell1.isSet) + result = -1; + else if(!cell2.isSet) + result = 1; + else if(sortField.dataType._vTbl[__ecereVMethodID_class_OnCompare]) { - result = ((int (*)(void *, void *, void *))(void *)sortField.dataType._vTbl[__ecereVMethodID_class_OnCompare])(sortField.dataType, - cell1.isSet ? cell1.data : null, - cell2.isSet ? cell2.data : null); + if(sortField.dataType.type == normalClass || sortField.dataType.type == noHeadClass) + { + result = ((int (*)(void *, void *, void *))(void *)sortField.dataType._vTbl[__ecereVMethodID_class_OnCompare])(sortField.dataType, + cell1.isSet ? cell1.data[0] : null, + cell2.isSet ? cell2.data[0] : null); + } + else + { + result = ((int (*)(void *, void *, void *))(void *)sortField.dataType._vTbl[__ecereVMethodID_class_OnCompare])(sortField.dataType, + cell1.isSet ? cell1.data : null, + cell2.isSet ? cell2.data : null); + } } + return it.data.order * result; } - return sortField.sortOrder * result; + return 0; } - void _SortSubRows(DataField field, int order) + void _SortSubRows(Array fields) { DataRow search; for(search = subRows.first; search; search = search.next) - search._SortSubRows(field, order); - subRows.Sort(Compare, field); + search._SortSubRows(fields); + subRows.Sort(Compare, fields); } public void SortSubRows(bool scrollToCurrent) { - if(this && listBox && listBox.sortField) + if(this && listBox && listBox.sortFields) { - _SortSubRows(listBox.sortField, listBox.sortField.sortOrder); + _SortSubRows(listBox.sortFields); { DataRow search; @@ -1241,7 +1253,7 @@ public: delete field; } endBevel.visible = false; - sortField = null; + sortFields = null; } } @@ -1254,8 +1266,11 @@ public: int index = field.index; DataRow row; - if(sortField == field) - sortField = null; + if(sortFields) + { + for(f : sortFields; f.field == field) + sortFields = null; + } for(row = rows.first; row; ) { @@ -1599,8 +1614,8 @@ public: { void * data = row.GetData(field); char tempString[1024] = ""; - bool needClass = false; - const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass); + ObjectNotationType onType = none; + const char * string = ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &onType); if(string && string[0]) checkNextField = false; @@ -1633,8 +1648,8 @@ public: { void * data = row.GetData(field); char tempString[1024] = ""; - bool needClass = false; - const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass); + ObjectNotationType onType = none; + const char * string = ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &onType); if(string && string[0]) checkNextField = false; @@ -1671,8 +1686,8 @@ public: { void * data = row.GetData(field); char tempString[1024] = ""; - bool needClass = false; - const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass); + ObjectNotationType onType = none; + const char * string = ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &onType); if(string && string[0]) checkNextField = false; @@ -1720,8 +1735,8 @@ public: { void * data = row.GetData(field); char tempString[1024] = ""; - bool needClass = false; - const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass); + ObjectNotationType onType = none; + const char * string = ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &onType); if(string && string[0]) checkNextField = false; @@ -1793,7 +1808,7 @@ public: } } - void Sort(DataField field, int order) + void MultiSort(Array fields) { if(this) { @@ -1801,13 +1816,27 @@ public: int headerSize = ((style.header) ? rowHeight : 0); int height = clientSize.h + 1 - headerSize; - if(!field) field = fields.first; - sortField = field; - field.sortOrder = order ? order : 1; - rows.Sort(DataRow::Compare, field); + if(fields != sortFields) + { + delete sortFields; + sortFields = fields; + if(fields) + incref fields; + } + if(fields) + { + for(f : fields) + { + if(f.order) + f.field.sortOrder = f.order; + else + f.order = f.field.sortOrder; // TOCHECK: Or should we force 1 (ascending) here? + } + } + rows.Sort(DataRow::Compare, fields); for(search = rows.first; search; search = search.next) - search._SortSubRows(field, order); + search._SortSubRows(fields); { int index = 0; @@ -1827,6 +1856,39 @@ public: } } + void Sort(DataField field, int order) + { + MultiSort({ [ { field ? field : fields.first, order } ] }); + } + + void SortAlsoBy(DataField field, int order) + { + Iterator it { sortFields }; + if(sortFields) + { + while(it.Next()) + if(it.data.field == field) + break; + } + if(it.pointer) + { + if(!order && !sortFields.GetNext(it.pointer)) + it.data.order *= -1; + else + { + it.Remove(); + sortFields.Add({ field, order ? order : 1 }); + } + } + else + { + if(!sortFields) { sortFields = { }; incref sortFields; } + sortFields.Add({ field, order ? order : 1 }); + } + if(field) + MultiSort(sortFields); + } + void StopEditing(bool save) { HideEditBox(save, false, true); @@ -1892,6 +1954,8 @@ private: { DataField field; + delete sortFields; + delete editData; delete typedString; delete endBevel; @@ -2648,84 +2712,86 @@ private: surface.VLine(0, rowHeight - 1, position - scroll.x - 2); surface.VLine(0, rowHeight - 1, position - scroll.x); } - if(sortField && !style.clearHeader && style.header) + if(sortFields && !style.clearHeader && style.header) { - DataField field = sortField; - int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? - clientSize.w - field.x : (field.width + EXTRA_SPACE); - int tw = 0, th = 0; - if(field.header) - surface.TextExtent(field.header, strlen(field.header), &tw, &th); - if(tw < width - EXTRA_SPACE) + for(f : sortFields) { - bool up = field.sortOrder == 1; - int x = 4, y = 4; - Box clip = - { - field.x + 2 - scroll.x, 0, - field.x + width + EXTRA_SPACE - 1 - scroll.x, rowHeight - }; - surface.Clip(&clip); - if(field.alignment == left || field.alignment == center) + DataField field = f.field; + int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? + clientSize.w - field.x : (field.width + EXTRA_SPACE); + int tw = 0, th = 0; + if(field.header) + surface.TextExtent(field.header, strlen(field.header), &tw, &th); + if(tw < width - EXTRA_SPACE) { - if(field.alignment == center) - x = field.x + (width + EXTRA_SPACE - tw) / 2 + tw + EXTRA_SPACE + 4; - else - x = field.x + tw + EXTRA_SPACE + 4; + bool up = f.order == 1; + int x = 4, y = 4; + Box clip = + { + field.x + 2 - scroll.x, 0, + field.x + width + EXTRA_SPACE - 1 - scroll.x, rowHeight + }; + surface.Clip(&clip); + if(field.alignment == left || field.alignment == center) + { + if(field.alignment == center) + x = field.x + (width + EXTRA_SPACE - tw) / 2 + tw + EXTRA_SPACE + 4; + else + x = field.x + tw + EXTRA_SPACE + 4; - x = Min(x, field.x + width - 4); - } - else if(field.alignment == right) - { - x = field.x + width - tw - 2*EXTRA_SPACE - 4; - x = Max(x, field.x + 2); - } - x -= scroll.x; + x = Min(x, field.x + width - 4); + } + else if(field.alignment == right) + { + x = field.x + width - tw - 2*EXTRA_SPACE - 4; + x = Max(x, field.x + 2); + } + x -= scroll.x; - if(guiApp.textMode) - { - // surface.SetForeground((wmenu.selectedFlag == item) ? white : black); - // surface.WriteText(clientSize.w-8, y+(wmenu.rh - 8)/2, "\020", 1); - } - else - { - if(up) + if(guiApp.textMode) { - surface.SetForeground(Color { 128,128,128 } ); - surface.DrawLine(x + 3, y, x, y + 5); - surface.PutPixel(x + 1, y + 5); - surface.PutPixel(x + 1, y + 3); - surface.PutPixel(x + 2, y + 1); - - surface.SetForeground(white); - surface.DrawLine(x + 4, y, x + 7, y + 5); - surface.PutPixel(x + 6, y + 5); - surface.PutPixel(x + 6, y + 3); - surface.PutPixel(x + 5, y + 1); - - surface.DrawLine(x, y + 6, x + 7, y + 6); + // surface.SetForeground((wmenu.selectedFlag == item) ? white : black); + // surface.WriteText(clientSize.w-8, y+(wmenu.rh - 8)/2, "\020", 1); } else { - surface.SetForeground(Color { 128,128,128 }); - surface.DrawLine(x + 3, y+6, x, y+1); - surface.PutPixel(x + 1, y+1); - surface.PutPixel(x + 1, y+3); - surface.PutPixel(x + 2, y+5); - - surface.SetForeground(white); - surface.DrawLine(x + 4, y+6, x + 7, y+1); - surface.PutPixel(x + 6, y+1); - surface.PutPixel(x + 6, y+3); - surface.PutPixel(x + 5, y+5); - - surface.DrawLine(x, y, x + 7, y); + if(up) + { + surface.SetForeground(Color { 128,128,128 } ); + surface.DrawLine(x + 3, y, x, y + 5); + surface.PutPixel(x + 1, y + 5); + surface.PutPixel(x + 1, y + 3); + surface.PutPixel(x + 2, y + 1); + + surface.SetForeground(white); + surface.DrawLine(x + 4, y, x + 7, y + 5); + surface.PutPixel(x + 6, y + 5); + surface.PutPixel(x + 6, y + 3); + surface.PutPixel(x + 5, y + 1); + + surface.DrawLine(x, y + 6, x + 7, y + 6); + } + else + { + surface.SetForeground(Color { 128,128,128 }); + surface.DrawLine(x + 3, y+6, x, y+1); + surface.PutPixel(x + 1, y+1); + surface.PutPixel(x + 1, y+3); + surface.PutPixel(x + 2, y+5); + + surface.SetForeground(white); + surface.DrawLine(x + 4, y+6, x + 7, y+1); + surface.PutPixel(x + 6, y+1); + surface.PutPixel(x + 6, y+3); + surface.PutPixel(x + 5, y+5); + + surface.DrawLine(x, y, x + 7, y); + } } + surface.Clip(null); } - surface.Clip(null); } } - } void OnResize(int w, int h) @@ -3009,15 +3075,9 @@ private: if(style.header && !dropField && style.sortable) { DataField field = (DataField)(intptr)control.id; - if(sortField == field) - field.sortOrder *= -1; - else - sortField = field; + SortAlsoBy(field, 0); if(field) - { - Sort(sortField, field.sortOrder); NotifySort(master, this, field, mods); - } } return true; } @@ -4175,8 +4235,8 @@ private: { void * data = row.GetData(field); char tempString[1024] = ""; - bool needClass = false; - const char * string = data ? ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass) : null; + ObjectNotationType onType = none; + const char * string = data ? ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &onType) : null; if(string && string[0]) checkNextField = false; @@ -4197,14 +4257,14 @@ private: } looped = true; } - typingTimer.Stop(); - if(typingTimeOut && !keyHit) - typingTimer.Start(); - if(!result || !typingTimeOut || keyHit) - typedString[len-1] = '\0'; } if(!checkNextField || result) break; } + typingTimer.Stop(); + if(typingTimeOut && !keyHit) + typingTimer.Start(); + if(!result || !typingTimeOut || keyHit) + typedString[len-1] = '\0'; return result; } @@ -4631,7 +4691,8 @@ private: DataRow clickedRow; DataRow currentRow; int width; - DataField sortField; + // DataField sortField; + Array sortFields; int rowCount; int rowHeight; int fontH; diff --git a/ecere/src/gui/controls/Menu.ec b/ecere/src/gui/controls/Menu.ec index 971479533f..7a68d62435 100644 --- a/ecere/src/gui/controls/Menu.ec +++ b/ecere/src/gui/controls/Menu.ec @@ -105,9 +105,9 @@ public: else { Key accel = value.code; - bool needClass = false; + ObjectNotationType onType = none; char tempString[50]; - const char * result = accel.OnGetString(tempString, null, &needClass); + const char * result = accel.OnGetString(tempString, null, &onType); int len = strlen(accelString); if(result) strcpy(accelString + len, result); // accelString[len] = toupper(accelString[len]); diff --git a/ecere/src/gui/controls/PathBox.ec b/ecere/src/gui/controls/PathBox.ec index 4e858e82ad..9f7382b3a3 100644 --- a/ecere/src/gui/controls/PathBox.ec +++ b/ecere/src/gui/controls/PathBox.ec @@ -5,7 +5,7 @@ import "Array" default extern int __ecereVMethodID_class_OnGetDataFromString; default __attribute__((unused)) static void _workAround() { - int a; + int a = 0; a.OnGetDataFromString(0); } diff --git a/ecere/src/gui/controls/Stacker.ec b/ecere/src/gui/controls/Stacker.ec index e4137fc279..93dc874607 100644 --- a/ecere/src/gui/controls/Stacker.ec +++ b/ecere/src/gui/controls/Stacker.ec @@ -612,4 +612,21 @@ private: result = next; return result; } + + public void MoveControl(IteratorPointer ctrl, IteratorPointer after, bool fixCycle) + { + /* + * Facility to reorder the controls in the stacker: + * input 'ctrl' will become the successor in the list of input 'after'. + * If input 'fixCycle' is true, the cycling order is updated to match, + * otherwise it is left unchanged. + * */ + if(ctrl) + { + if(fixCycle) + // Fix the cycle first to avoid messing with containers pointers. + childrenCycle.Move((*((Window*)ctrl)).cycle, after ? (*((Window*)after)).cycle.prev : null); + controls.Move(ctrl, after); + } + } } diff --git a/ecere/src/gui/controls/StatusBar.ec b/ecere/src/gui/controls/StatusBar.ec index 8c78f5506a..23b1adde1d 100644 --- a/ecere/src/gui/controls/StatusBar.ec +++ b/ecere/src/gui/controls/StatusBar.ec @@ -177,7 +177,7 @@ public: { set { - if(this) + if(this && (!colorSet || color != value)) { color = value; colorSet = true; @@ -190,7 +190,7 @@ public: { set { - if(this) + if(this && (!colorSet || backColor != value)) { backColor = value; colorSet = true; @@ -203,7 +203,8 @@ public: { set { - if(this) this.bold = value; + if(this && bold != value) + bold = value; } } diff --git a/ecere/src/gui/controls/ToolTip.ec b/ecere/src/gui/controls/ToolTip.ec index dee2abc657..f9b1d98b89 100644 --- a/ecere/src/gui/controls/ToolTip.ec +++ b/ecere/src/gui/controls/ToolTip.ec @@ -76,6 +76,16 @@ public class ToolTip : Window return true; } + bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct) + { + if(!active) + { + Destroy(0); + return false; + } + return true; + } + bool OnMouseLeave(Modifiers mods) { closeTimer.Start(); @@ -171,13 +181,16 @@ public class ToolTip : Window ToolTip ::Find(Window window) { - Window w; - for(w = window.firstSlave; w; w = w.nextSlave) + OldLink link; + + // NOTE: Window::nextSlave is slow and not suitable for iteration + for(link = window.slaves.first; link; link = link.next) { + Window w = link.data; if(eClass_IsDerived(w._class, class(ToolTip))) - break; + return (ToolTip)w; } - return (ToolTip)w; + return null; } bool Window::OnMouseOverHandler(int x, int y, Modifiers mods) diff --git a/ecere/src/gui/dialogs/ColorPicker.ec b/ecere/src/gui/dialogs/ColorPicker.ec index e24b53b1d1..07dda56747 100644 --- a/ecere/src/gui/dialogs/ColorPicker.ec +++ b/ecere/src/gui/dialogs/ColorPicker.ec @@ -283,39 +283,39 @@ private: // HSV H.Clear(); - H.Printf("%d", (int)hsv.h); + H.Printf("%d", Max(0, Min(360, (int)(hsv.h + 0.5)))); H.SetModified(false); S.Clear(); - S.Printf("%d", (int)hsv.s); + S.Printf("%d", Max(0, Min(100, (int)(hsv.s + 0.5)))); S.SetModified(false); V.Clear(); - V.Printf("%d", (int)hsv.v); + V.Printf("%d", Max(0, Min(100, (int)(hsv.v + 0.5)))); V.SetModified(false); // Lab L.Clear(); - L.Printf("%d", (int)Lab.l); + L.Printf("%d", Max(0, Min(100, (int)(Lab.l + 0.5)))); L.SetModified(false); a.Clear(); - a.Printf("%d", (int)Lab.a); + a.Printf("%d", Max(-128, Min(127, (int)(Lab.a + 0.5)))); a.SetModified(false); b.Clear(); - b.Printf("%d", (int)Lab.b); + b.Printf("%d", Max(-128, Min(127, (int)(Lab.b + 0.5)))); b.SetModified(false); // CMYK C.Clear(); - C.Printf("%d", (int)cmyk.c); + C.Printf("%d", Max(0, Min(100, (int)(cmyk.c + 0.5)))); C.SetModified(false); M.Clear(); - M.Printf("%d", (int)cmyk.m); + M.Printf("%d", Max(0, Min(100, (int)(cmyk.m + 0.5)))); M.SetModified(false); Y.Clear(); - Y.Printf("%d", (int)cmyk.y); + Y.Printf("%d", Max(0, Min(100, (int)(cmyk.y + 0.5)))); Y.SetModified(false); K.Clear(); - K.Printf("%d", (int)cmyk.k); - H.SetModified(false); + K.Printf("%d", Max(0, Min(100, (int)(cmyk.k + 0.5)))); + K.SetModified(false); // Hexadecimal hexa.Clear(); @@ -550,7 +550,7 @@ private: EditBox a { - this, position = { 442, 30 }, size = { 32, 20 }; + this, position = { 442, 30 }, size = { 40, 20 }; bool NotifyModified(EditBox control) { @@ -569,7 +569,7 @@ private: EditBox b { - this, position = { 442, 50 }, size = { 32, 20 }; + this, position = { 442, 50 }, size = { 40, 20 }; bool NotifyModified(EditBox control) { diff --git a/ecere/src/gui/dialogs/FileDialog.ec b/ecere/src/gui/dialogs/FileDialog.ec index b009d019e5..d164986069 100644 --- a/ecere/src/gui/dialogs/FileDialog.ec +++ b/ecere/src/gui/dialogs/FileDialog.ec @@ -237,7 +237,7 @@ public struct FileName name = null; } - const char * OnGetString(char * string, void * fieldData, bool * needClass) + const char * OnGetString(char * string, void * fieldData, ObjectNotationType * onType) { return name; } @@ -314,7 +314,7 @@ public: }; // Stuff currently in config moving to FileDialog: - property const char * filePath { set { strcpy(filePath, value); } get { return (char *)filePath; } }; + property const char * filePath { set { strcpy(filePath, value ? value : ""); } get { return (char *)filePath; } }; property const char * currentDirectory { set diff --git a/ecere/src/gui/drivers/AndroidInterface.ec b/ecere/src/gui/drivers/AndroidInterface.ec index c4f6e74f32..265e9e5a62 100644 --- a/ecere/src/gui/drivers/AndroidInterface.ec +++ b/ecere/src/gui/drivers/AndroidInterface.ec @@ -8,6 +8,8 @@ import "Condition" #define uint _uint #define set _set +#include +#include #include #include #include @@ -102,6 +104,71 @@ private: return 0; } + void setupLocation() + { + JNIEnv * env = activity->env; + JavaVM * vm = activity->vm; + jclass cContext; + jclass cLocationManager; + jclass cCriteria; + jmethodID criteriaConstID; + jmethodID setAccuracyID; + jfieldID lsFID; + jfieldID fineFID; + jstring jstr; + jmethodID getSystemServiceID; + jobject lm = NULL; + jclass cActivityThread; + jobject at; + jmethodID currentActivityThreadID; + jmethodID getApplicationID; + jmethodID getBestProviderID; + jmethodID requestLocationUpdatesID; + jobject criteria; + jobject context; + int accuracyFine; + + //(*vm)->AttachCurrentThread(vm, &env, NULL); + cActivityThread = (*env)->FindClass(env,"android/app/ActivityThread"); + cCriteria = (*env)->FindClass(env,"android/location/Criteria"); + currentActivityThreadID = (*env)->GetStaticMethodID(env, cActivityThread, "currentActivityThread", "()Landroid/app/ActivityThread;"); + at = (*env)->CallStaticObjectMethod(env, cActivityThread, currentActivityThreadID); + getApplicationID = (*env)->GetMethodID(env, cActivityThread, "getApplication", "()Landroid/app/Application;"); + context = (*env)->CallObjectMethod(env, at, getApplicationID); + cLocationManager = (*env)->FindClass(env, "android/location/LocationManager"); + getBestProviderID = (*env)->GetMethodID(env, cLocationManager, "getBestProvider", "(Landroid/location/Criteria;Z)Ljava/lang/String;"); + requestLocationUpdatesID = (*env)->GetMethodID(env, cLocationManager, "requestLocationUpdates", "(Ljava/lang/String;JFLandroid/location/LocationListener;)V"); + + cContext = (*env)->FindClass(env, "android/content/Context"); + lsFID = (*env)->GetStaticFieldID(env, cContext, "LOCATION_SERVICE", "Ljava/lang/String;"); + jstr = (*env)->GetStaticObjectField(env, cContext, lsFID); + + criteriaConstID = (*env)->GetMethodID(env, cCriteria, "", "()V"); + criteria = (*env)->NewObject(env, cCriteria, criteriaConstID); + + //locationListener = (*env)->NewObject(env, cLocationListener, locationListenerConstID); + + fineFID = (*env)->GetStaticFieldID(env, cCriteria, "ACCURACY_FINE", "I"); + //fineFID = (*env)->GetStaticFieldID(env, cCriteria, "ACCURACY_COARSE", "I"); + accuracyFine = (*env)->GetStaticIntField(env, cCriteria, fineFID); + setAccuracyID = (*env)->GetMethodID(env, cCriteria, "setAccuracy", "(I)V"); + (*env)->CallVoidMethod(env, criteria, setAccuracyID, accuracyFine); + context = (*env)->CallObjectMethod(env, at, getApplicationID); + + getSystemServiceID = (*env)->GetMethodID(env, cContext, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); + lm = (*env)->CallObjectMethod(env, context, getSystemServiceID, jstr); + jstr = (*env)->CallObjectMethod(env, lm, getBestProviderID, criteria, (jboolean)0); + if(jstr) // Note: location system service will be null if location wasn't enabled in manifest + { + const char *s = (*env)->GetStringUTFChars(env, jstr, 0); + PrintLn("Requesting location from: ", s); + (*env)->ReleaseStringUTFChars(env, jstr, s); + (*env)->CallVoidMethod(env, lm, requestLocationUpdatesID, jstr, (jlong)1000, (jfloat)1.0f, activity->clazz); + } + + //(*vm)->DetachCurrentThread(vm); + } + void destroy() { free_saved_state(); @@ -463,6 +530,23 @@ static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) app.set_input(null); } +default dllexport const void * Android_getJNIEnv() +{ + const void * foo = androidActivity ? androidActivity.activity->env : null; + PrintLn("getJNIEnv returned ", (uintptr)foo); + return foo; +} + +default dllexport const void * Android_getJavaVM() +{ + return androidActivity ? androidActivity.activity->vm : null; +} + +default dllexport const void * Android_getActivity() +{ + return androidActivity ? androidActivity.activity->clazz : null; +} + default dllexport void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize) { AndroidAppGlue app; @@ -540,6 +624,45 @@ default dllexport void ANativeActivity_onCreate(ANativeActivity* activity, void* activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; activity->instance = app; app.Create(); + + { + JNIEnv * env = activity->env; + if(env) + { + jclass classNativeActivity = (*env)->FindClass(env, "android/app/NativeActivity"); + jclass classWindowManager = (*env)->FindClass(env, "android/view/WindowManager"); + jclass classDisplay = (*env)->FindClass(env, "android/view/Display"); + if(classWindowManager) + { + jmethodID idNativeActivity_getWindowManager = (*env)->GetMethodID(env, classNativeActivity, "getWindowManager", "()Landroid/view/WindowManager;"); + jmethodID idWindowManager_getDefaultDisplay = (*env)->GetMethodID(env, classWindowManager, "getDefaultDisplay", "()Landroid/view/Display;"); + jmethodID idWindowManager_getRotation = (*env)->GetMethodID(env, classDisplay, "getRotation", "()I"); + if(idWindowManager_getRotation) + { + jobject windowManager = (*env)->CallObjectMethod(env, activity->clazz, idNativeActivity_getWindowManager); + if(windowManager) + { + jobject display = (*env)->CallObjectMethod(env, windowManager, idWindowManager_getDefaultDisplay); + if(display) + { + int rotation = (*env)->CallIntMethod(env, display, idWindowManager_getRotation); + ((AndroidActivity)app).defaultRotation = rotation; + switch(rotation) + { + case 0: PrintLn("Default rotation is ROTATION_0"); break; + case 1: PrintLn("Default rotation is ROTATION_90"); break; + case 2: PrintLn("Default rotation is ROTATION_180"); break; + case 3: PrintLn("Default rotation is ROTATION_270"); break; + } + } + } + } + } + + } + } + + app.setupLocation(); } // *** END OF NATIVE APP GLUE ****** @@ -616,17 +739,91 @@ class AndroidInterface : Interface source.process(source.userData); // If a sensor has data, process it now. - /* if(androidActivity.ident == user) { - if(androidActivity.accelerometerSensor) + if(androidActivity.accelerometerSensor || androidActivity.compassSensor || androidActivity.dofSensor) { ASensorEvent event; while (ASensorEventQueue_getEvents(androidActivity.sensorEventQueue, &event, 1) > 0) - LOGI("accelerometer: x=%f y=%f z=%f", event.acceleration.x, event.acceleration.y, event.acceleration.z); + { + switch(event.type) + { + case ASENSOR_TYPE_ROTATION_VECTOR: + { + Matrix rm, tmp; + double values[3]; + Quaternion q1, q2, q3; + + // LOGI("raw: x=%.05f y=%.05f z=%.05f w=%.05f (%.05f)", event.vector.x, event.vector.y, event.vector.z, event.data[3], q.w); + + getRotationMatrixFromVector(rm, event.data); + + #define AXIS_MINUS_X 0x81 + #define AXIS_MINUS_Y 0x82 + #define AXIS_MINUS_Z 0x83 + #define AXIS_X 0x01 + #define AXIS_Y 0x02 + #define AXIS_Z 0x03 + + switch(androidActivity.defaultRotation) + { + case 0: break; // 0 + case 1: remapCoordinateSystem(rm, AXIS_Y, AXIS_MINUS_X, tmp); rm = tmp; break; // 90 + case 2: remapCoordinateSystem(rm, AXIS_MINUS_X, AXIS_MINUS_Y, tmp); rm = tmp; break; // 180 + case 3: remapCoordinateSystem(rm, AXIS_MINUS_Y, AXIS_X, tmp); rm = tmp; break; // 270 + } + getOrientation(rm, values); + compass.yaw = Radians { values[0] }; + compass.pitch = Radians { values[1] }; + compass.roll = Radians { values[2] }; + + q1 = compass; + q2.RotationYawPitchRoll({ 0, 90, 0 }); + q3.Multiply(q2, q1); + compass = q3; + compass.roll = -compass.roll; + + // PrintLn("Yaw: ", (double)compass.yaw, ", Pitch: ", (double)compass.pitch, ", Roll: ", (double)compass.roll); + break; + } + case ASENSOR_TYPE_ACCELEROMETER: + // LOGI("accelerometer: x=%.02f y=%.02f z=%.02f", event.acceleration.x, event.acceleration.y, event.acceleration.z); + break; + case ASENSOR_TYPE_POSE_6DOF: + { + Quaternion q { event.data[0], event.data[1], event.data[2], event.data[3] }; + Vector3D t { event.data[4], event.data[5], event.data[6] }; + Euler e; + + e.FromQuaternion(q, yxz); + + LOGI("orientation: yaw=%.02f pitch=%.02f roll=%.02f", (double)e.yaw, (double)e.pitch, (double)e.roll); + LOGI("translation: x=%.02f y=%.02f z=%.02f", (double)t.x, (double)t.y, (double)t.z); + LOGI("---"); + + /* + values[0]: x*sin(θ/2) + values[1]: y*sin(θ/2) + values[2]: z*sin(θ/2) + values[3]: cos(θ/2) + values[4]: Translation along x axis from an arbitrary origin. + values[5]: Translation along y axis from an arbitrary origin. + values[6]: Translation along z axis from an arbitrary origin. + values[7]: Delta quaternion rotation x*sin(θ/2) + values[8]: Delta quaternion rotation y*sin(θ/2) + values[9]: Delta quaternion rotation z*sin(θ/2) + values[10]: Delta quaternion rotation cos(θ/2) + values[11]: Delta translation along x axis. + values[12]: Delta translation along y axis. + values[13]: Delta translation along z axis. + values[14]: Sequence number + */ + break; + } + } + } } } - */ eventAvailable = true; if(androidActivity.destroyRequested) @@ -1134,13 +1331,18 @@ class AndroidActivity : AndroidAppGlue AndroidPollSource source; int events; LooperID ident; - /* + ASensorManager* sensorManager; - const ASensor* accelerometerSensor; ASensorEventQueue* sensorEventQueue; - */ + + const ASensor* accelerometerSensor; + const ASensor* compassSensor; + const ASensor* dofSensor; + SavedState state; + int defaultRotation; + int onInputEvent(AInputEvent* event) { static Time lastTime = 0; @@ -1154,7 +1356,7 @@ class AndroidActivity : AndroidAppGlue //uint index = (actionAndIndex & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; //uint flags = AMotionEvent_getFlags(event); uint meta = AMotionEvent_getMetaState(event); - //uint edge = AMotionEvent_getEdgeFlags(event); + uint edge = AMotionEvent_getEdgeFlags(event); //int64 downTime = AMotionEvent_getDownTime(event); // nanotime //int64 eventTime = AMotionEvent_getDownTime(event); //float axis; @@ -1319,19 +1521,26 @@ class AndroidActivity : AndroidAppGlue case gainedFocus: guiApp.desktop.Update(null); guiApp.SetAppFocus(true); - /* + if(accelerometerSensor) { ASensorEventQueue_enableSensor(sensorEventQueue, accelerometerSensor); ASensorEventQueue_setEventRate(sensorEventQueue, accelerometerSensor, (1000L/60)*1000); } - */ + if(compassSensor) + { + ASensorEventQueue_enableSensor(sensorEventQueue, compassSensor); + ASensorEventQueue_setEventRate(sensorEventQueue, compassSensor, (1000L/60)*1000); + } break; case lostFocus: - /* + if(accelerometerSensor) ASensorEventQueue_disableSensor(sensorEventQueue, accelerometerSensor); - */ + + if(compassSensor) + ASensorEventQueue_disableSensor(sensorEventQueue, compassSensor); + guiApp.SetAppFocus(false); guiApp.desktop.Update(null); break; @@ -1345,10 +1554,16 @@ class AndroidActivity : AndroidAppGlue void main() { androidActivity = this; - /* Let's have fun with sensors when we have an actual device to play with + // Let's have fun with sensors when we have an actual device to play with sensorManager = ASensorManager_getInstance(); - accelerometerSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER); sensorEventQueue = ASensorManager_createEventQueue(sensorManager, looper, LooperID::user, null, null); + + // accelerometerSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER); + compassSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ROTATION_VECTOR); + /* + dofSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_POSE_6DOF); + if(!dofSensor) + PrintLn("error obtaining ASENSOR_TYPE_POSE_6DOF"); */ if(savedState) @@ -1406,3 +1621,78 @@ class AndroidActivity : AndroidAppGlue } } } + +static void getRotationMatrixFromVector(Matrix R, const float * rotationVector) +{ + double q0; + double q1 = rotationVector[0]; + double q2 = rotationVector[1]; + double q3 = rotationVector[2]; + // if(0) q0 = rotationVector[3]; else + { + q0 = 1 - q1 * q1 - q2 * q2 - q3 * q3; + q0 = (q0 > 0) ? sqrt(q0) : 0; + } + double sq_q1 = 2 * q1 * q1; + double sq_q2 = 2 * q2 * q2; + double sq_q3 = 2 * q3 * q3; + double q1_q2 = 2 * q1 * q2; + double q3_q0 = 2 * q3 * q0; + double q1_q3 = 2 * q1 * q3; + double q2_q0 = 2 * q2 * q0; + double q2_q3 = 2 * q2 * q3; + double q1_q0 = 2 * q1 * q0; + + R.array[0] = 1 - sq_q2 - sq_q3; + R.array[1] = q1_q2 - q3_q0; + R.array[2] = q1_q3 + q2_q0; + R.array[3] = 0.0f; + R.array[4] = q1_q2 + q3_q0; + R.array[5] = 1 - sq_q1 - sq_q3; + R.array[6] = q2_q3 - q1_q0; + R.array[7] = 0.0f; + R.array[8] = q1_q3 - q2_q0; + R.array[9] = q2_q3 + q1_q0; + R.array[10] = 1 - sq_q1 - sq_q2; + R.array[11] = 0.0; + R.array[12] = R.array[13] = R.array[14] = 0.0; + R.array[15] = 1.0; +} + +static void getOrientation(const Matrix R, double * values) +{ + values[0] = atan2(R.array[1], R.array[5]); + values[1] = asin(-R.array[9]); + values[2] = atan2(-R.array[8], R.array[10]); +} + +static void remapCoordinateSystem(const Matrix inR, int X, int Y, Matrix outR) +{ + int Z = X ^ Y; + int x = (X & 0x3) - 1; + int y = (Y & 0x3) - 1; + int z = (Z & 0x3) - 1; + int axis_y = (z + 1) % 3; + int axis_z = (z + 2) % 3; + bool sx, sy, sz; + int j; + + if(((x ^ axis_y) | (y ^ axis_z)) != 0) + Z ^= 0x80; + sx = (X >= 0x80); + sy = (Y >= 0x80); + sz = (Z >= 0x80); + + for(j = 0; j < 3; j++) + { + int offset = j * 4, i; + for(i = 0; i < 3; i++) + { + if(x == i) outR.array[offset + i] = sx ? -inR.array[offset + 0] : inR.array[offset + 0]; + if(y == i) outR.array[offset + i] = sy ? -inR.array[offset + 1] : inR.array[offset + 1]; + if(z == i) outR.array[offset + i] = sz ? -inR.array[offset + 2] : inR.array[offset + 2]; + } + } + outR.array[3] = outR.array[7] = outR.array[11] = outR.array[12] = outR.array[13] = outR.array[14] = 0; + outR.array[15] = 1; +} diff --git a/ecere/src/gui/drivers/LuminInterface.ec b/ecere/src/gui/drivers/LuminInterface.ec new file mode 100644 index 0000000000..baa068ea20 --- /dev/null +++ b/ecere/src/gui/drivers/LuminInterface.ec @@ -0,0 +1,785 @@ +#define _Noreturn + +namespace gui::drivers; + +#include + +#include +#include +#include +#include +#include +#include +#include + +import "Window" +import "Interface" + +import "egl" + +static bool initialized; + +static int desktopW, desktopH; +static char * clipBoardData; +static float mouseX = 320, mouseY = 240; +static bool acquiredInputMode; +static MouseButtons buttonsState; +static Pointf lastButtonPressedPos[2]; +static float relMouseX, relMouseY; + +static MLHandle inputTracker; +static Pointf lastTouchPos[2]; +static bool touchPadDown[2]; +static bool wasActiveLastPass[2]; +static float lastTouchPadForce[2]; +static bool wasButtonPressedLastPass_trigger[2]; +static bool wasButtonPressedLastPass_bumper[2]; + +enum MouseEvent { OnTrigger, OnBumper, OnTouchpad }; +static MouseEvent prevMouseButtonEvent[2]; + +default: +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove; +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown; +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp; +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick; +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown; +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp; +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick; +private: + +const char application_name[] = "com.magicleap.ecereapp -- but not really"; // minor todo + +enum AppState { zero, paused, resumed }; + +struct ApplicationContext +{ + AppState state; +}; + +ApplicationContext application_context { }; + +MLLifecycleCallbacks lifecycle_callbacks = {0}; +MLGraphicsOptions graphics_options = { 0, MLSurfaceFormat_RGBA8UNorm, MLSurfaceFormat_D32Float }; +MLHandle opengl_context; +MLHandle head_tracker; + +// callbacks +static void onStop(void * appContext) +{ + ((ApplicationContext*)appContext)->state = zero; // ((struct application_context_t*)application_context)->dummy_value = 0; + ML_LOG(Info, "On stop called."); +} + +static void onPause(void * appContext) +{ + ((ApplicationContext*)appContext)->state = paused; // ((struct application_context_t*)application_context)->dummy_value = 1; + ML_LOG(Info, "On pause called."); +} + +static void onResume(void * appContext) +{ + ((ApplicationContext*)appContext)->state = resumed; // ((struct application_context_t*)application_context)->dummy_value = 2; + ML_LOG(Info, "On resume called."); +} + + +class LuminInterface : Interface +{ + class_property(name) = "Lumin"; + + // --- User Interface System --- + bool Initialize() + { + setlocale(LC_ALL, "en_US.UTF-8"); + initialized = false; + + lifecycle_callbacks.on_stop = onStop; + lifecycle_callbacks.on_pause = onPause; + lifecycle_callbacks.on_resume = onResume; + + application_context.state = resumed; + + if(MLResult_Ok != MLLifecycleInit(&lifecycle_callbacks, (void*)&application_context)) + { + ML_LOG(Error, "Failed to initialize lifecycle."); + return false; + } + + MLInputConfiguration config; + MLResult r; + int i; + for(i = 0; i < MLInput_MaxControllers; i++) + config.dof[i] = MLInputControllerDof_6; + r = MLInputCreate(&config, &inputTracker); + if(r != MLResult_Ok) //&mlContext->inputTracker + { + ML_LOG(Error, "Failed to create input tracker: %d:%s", r, MLGetResultString(r));//application_name + return false; + } + return true; + } + + void Terminate() + { + ML_LOG(Error, "LuminInterface::Terminate() -- some todo?"); + + // clean up system + MLGraphicsDestroyClient(&graphics_client); + MLPerceptionShutdown(); + + MLResult r = MLInputDestroy(inputTracker); + if(r != MLResult_Ok) + { + ML_LOG(Error, "Failed to destroy input tracker: %d:%s", r, MLGetResultString(r)); + } + } + + #define DBLCLICK_DELAY 300 // 0.3 second + #define DBLCLICK_DELTA 1 + + bool ProcessInput(bool processAll) + { + static Time lastTime, lastClickTime; + Time time = GetTime(); + bool eventAvailable = false; + int w = 640 * 2; + int h = 480 * 2; + MLResult r; + int controllerID = 1; //or 2 + Time diffTime; + + if(!initialized) + { + const char * loc = "LuminInterface::ProcessInput()/initialize"; + // initialize perception system + MLPerceptionSettings perception_settings = {0}; + if(MLResult_Ok != MLPerceptionInitSettings(&perception_settings)) + { + ML_LOG(Error, "Failed to initialize perception."); + //return false; // example didn't return here so we don't? + } + + if(MLResult_Ok != MLPerceptionStartup(&perception_settings)) + { + ML_LOG(Error, "Failed to startup perception."); + return false; + } + + // Get ready to connect our GL context to the MLSDK graphics API + MLGraphicsOptions graphics_options = { 0, MLSurfaceFormat_RGBA8UNorm, MLSurfaceFormat_D32Float }; + graphics_client = ML_INVALID_HANDLE; + + r = MLGraphicsCreateClientGL(&graphics_options, eglContext, &graphics_client); + if(MLResult_Ok != r) + ML_LOG(Error, "MLGraphicsCreateClientGL complained: %d:%s", r, MLGetResultString(r)); + ML_LOG(Info, "%s: graphics_client:%p", loc, graphics_client); + + // Now that graphics is connected, the app is ready to go + if(MLResult_Ok != MLLifecycleSetReadyIndication()) + { + ML_LOG(Error, "Failed to indicate lifecycle ready."); + return false; + } + + initialized = true; + + lastTime = time; + lastClickTime = time; + } + + int i = 0; + //MLResult r; + + diffTime = time - lastTime; + lastTime = time; + + MLInputControllerState inputStates[MLInput_MaxControllers]; + r = MLInputGetControllerState(inputTracker, inputStates); // + //if(result == MLResult_Ok) + for(i = 0; i < MLInput_MaxControllers; ++i) + { + MLInputControllerState * ctrlState = &inputStates[i]; // is this yielding position from having input only? + bool is_connected = ctrlState->is_connected; + if(is_connected) + { + //int myresult = MLResult_Ok; + //PrintLn("ok is ", myresult); + float trigger = ctrlState->trigger_normalized; + float bumper = ctrlState->button_state[MLInputControllerButton_Bumper] ? 1.0f : 0.0f; + float home = ctrlState->button_state[MLInputControllerButton_HomeTap] ? 1.0f : 0.0f; + bool isTouchActive = ctrlState->is_touch_active[i]; + MLVec3f * touchPosAndForce = &ctrlState->touch_pos_and_force[i]; + float triggerThreshPos = 0.2f; + float touchPadPressedForce = 0.05f; + float dxButtonPressed = 0, dyButtonPressed = 0; + bool triggerPressed = trigger > triggerThreshPos; + bool touchPadPressed = isTouchActive ? touchPosAndForce->z > touchPadPressedForce : false; + + Modifiers keyFlags = 0; + if(isTouchActive) + { + MLInputControllerTouchpadGesture gesture = ctrlState->touchpad_gesture; + MLInputControllerTouchpadGestureState gestureState = ctrlState->touchpad_gesture_state; + float tx = touchPosAndForce->x, ty = touchPosAndForce->y, dx = 0, dy = 0; + + if(wasActiveLastPass[i]) + { + dx = tx - lastTouchPos[i].x; + dy = ty - lastTouchPos[i].y; + } + + if(dx) dx = Sgn(dx) * pow(fabs(dx), 1.3); + if(dy) dy = Sgn(dy) * pow(fabs(dy), 1.3); + + // Non-Acquired Input Mode + if(acquiredInputMode == false) + { + float deltaMouseX = 0; + float deltaMouseY = 0; + + if(wasActiveLastPass[i] == true) + { + deltaMouseX = dx * 500; + deltaMouseY = dy * 500; + + //PrintLn("dx: ", deltaMouseX, ", dy: ", deltaMouseY); + } + + mouseX += deltaMouseX; + mouseY -= deltaMouseY; + + mouseX = Min(w-1, Max(0.0f, mouseX)); + mouseY = Min(h-1, Max(0.0f, mouseY)); + + dxButtonPressed = Abs(mouseX - lastButtonPressedPos[i].x); + dyButtonPressed = Abs(mouseY - lastButtonPressedPos[i].y); + + guiApp.desktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove, + (int)mouseX, (int)mouseY, &keyFlags, true, true); + } + else + { + relMouseX += dx; + relMouseY += dy; + } + lastTouchPos[i] = { tx, ty }; + lastTouchPadForce[i] = touchPosAndForce->z; + } + else + { + touchPadDown[i] = false; + lastTouchPadForce[i] = 0; + } + + if(acquiredInputMode == false) + { + dxButtonPressed = Abs(mouseX - lastButtonPressedPos[i].x); + dyButtonPressed = Abs(mouseY - lastButtonPressedPos[i].y); + + // on button up - touchpad + if(!touchPadPressed && touchPadDown[i]) + { + guiApp.desktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp, + (int)mouseX, (int)mouseY,&keyFlags,true, true); + + if(prevMouseButtonEvent[i] == OnTouchpad && (time - lastClickTime) < 0.4 && dxButtonPressed < 2 && dyButtonPressed < 2) + guiApp.desktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick, + mouseX, mouseY, &keyFlags, true, true); + + lastClickTime = time; + prevMouseButtonEvent[i] = OnTouchpad; + lastButtonPressedPos[i].x = mouseX; + lastButtonPressedPos[i].y = mouseY; + touchPadDown[i] = false; + + } + // on button down - touchpad + else if(touchPadPressed && !touchPadDown[i]) + { + touchPadDown[i] = true; + guiApp.desktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, + mouseX, mouseY, &keyFlags, true, true); + } + + // on button up - trigger + if(wasButtonPressedLastPass_trigger[i] && !triggerPressed) + { + guiApp.desktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp, + mouseX, mouseY, &keyFlags, true, true); + + if(prevMouseButtonEvent[i] == OnTrigger && (time - lastClickTime) < 0.4 && dxButtonPressed < 2 && dyButtonPressed < 2) + guiApp.desktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick, + mouseX, mouseY, &keyFlags, true, true); + + lastClickTime = time; + prevMouseButtonEvent[i] = OnTrigger; + } + // on button down - trigger + else if(!wasButtonPressedLastPass_trigger[i] && triggerPressed) + guiApp.desktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown, + mouseX, mouseY, &keyFlags, true, true); + + // on button up - bumper + if(wasButtonPressedLastPass_bumper[i] && !bumper) + { + guiApp.desktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp, + mouseX, mouseY, &keyFlags, true, true); + + if(prevMouseButtonEvent[i] == OnBumper && (time - lastClickTime) < 0.4 && dxButtonPressed < 2 && dyButtonPressed < 2) + guiApp.desktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick, + mouseX, mouseY, &keyFlags, true, true); + + prevMouseButtonEvent[i] = OnBumper; + lastClickTime = time; + } + // on button down - bumper + else if(!wasButtonPressedLastPass_bumper[i] && bumper) + guiApp.desktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, + mouseX, mouseY, &keyFlags, true, true); + } + wasButtonPressedLastPass_trigger[i] = triggerPressed; + wasButtonPressedLastPass_bumper[i] = bumper ? true : false; + wasActiveLastPass[i] = isTouchActive; + + buttonsState = + { + left = triggerPressed, + right = bumper ? true : false + }; + } + else + { + wasButtonPressedLastPass_trigger[i] = false; + wasButtonPressedLastPass_bumper[i] = false; + wasActiveLastPass[i] = false; + lastTouchPadForce[i] = 0; + touchPadDown[i] = false; + } + } + + if(desktopW != w || desktopH != h) + { + guiApp.SetDesktopPosition(0, 0, w, h, true); + desktopW = w; + desktopH = h; + guiApp.desktop.Update(null); + } + + return eventAvailable; + } + + void Wait() + { + // guiApp.WaitEvent(); + } + + void Lock(Window window) + { + ML_LOG(Error, "LuminInterface::Lock()"); + } + + void Unlock(Window window) + { + ML_LOG(Error, "LuminInterface::Unlock()"); + } + + const char ** GraphicsDrivers(int * numDrivers) + { + static const char *graphicsDrivers[] = { "OpenGL" }; + *numDrivers = sizeof(graphicsDrivers) / sizeof(char *); + return (const char **)graphicsDrivers; + } + + void GetCurrentMode(bool * fullScreen, int * resolution, int * colorDepth, int * refreshRate) + { + ML_LOG(Error, "LuminInterface::GetCurrentMode()"); + *fullScreen = true; + } + + void EnsureFullScreen(bool *fullScreen) + { + *fullScreen = true; + } + + bool ScreenMode(bool fullScreen, int resolution, int colorDepth, int refreshRate, bool * textMode) + { + bool result = true; + return result; + } + + // --- Window Creation --- + void * CreateRootWindow(Window window) + { + return (void *)1; + } + + void DestroyRootWindow(Window window) + { + ML_LOG(Error, "LuminInterface::DestroyRootWindow()"); + } + + // -- Window manipulation --- + + void SetRootWindowCaption(Window window, const char * name) + { + } + + void PositionRootWindow(Window window, int x, int y, int w, int h, bool move, bool resize) + { + } + + void OrderRootWindow(Window window, bool topMost) + { + ML_LOG(Error, "LuminInterface::OrderRootWindow()"); + } + + void SetRootWindowColor(Window window) + { + ML_LOG(Error, "LuminInterface::SetRootWindowColor()"); + } + + void OffsetWindow(Window window, int * x, int * y) + { + } + + void UpdateRootWindow(Window window) + { + ML_LOG(Error, "LuminInterface::UpdateRootWindow()"); + if(!window.parent || !window.parent.display) + { + if(window.visible) + { + Box box = window.box; + box.left -= window.clientStart.x; + box.top -= window.clientStart.y; + box.right -= window.clientStart.x; + box.bottom -= window.clientStart.y; + // Logf("Update root window %s\n", window.name); + window.Update(null); + box.left += window.clientStart.x; + box.top += window.clientStart.y; + box.right += window.clientStart.x; + box.bottom += window.clientStart.y; + window.UpdateDirty(box); + } + } + } + + + void SetRootWindowState(Window window, WindowState state, bool visible) + { + } + + void FlashRootWindow(Window window) + { + ML_LOG(Error, "LuminInterface::FlashRootWindow()"); + } + + void ActivateRootWindow(Window window) + { + ML_LOG(Error, "LuminInterface::ActivateRootWindow()"); + } + + // --- Mouse-based window movement --- + + void StartMoving(Window window, int x, int y, bool fromKeyBoard) + { + ML_LOG(Error, "LuminInterface::StartMoving()"); + } + + void StopMoving(Window window) + { + ML_LOG(Error, "LuminInterface::StopMoving()"); + } + + // -- Mouse manipulation --- + + void GetMousePosition(int *x, int *y) + { + *x = (int)mouseX; + *y = (int)mouseY; + } + + void SetMousePosition(int x, int y) + { + mouseX = x; + mouseY = y; + } + + void SetMouseRange(Window window, Box box) + { + } + + void SetMouseCapture(Window window) + { + } + + // -- Mouse cursor --- + + void SetMouseCursor(Window window, int cursor) + { + if(cursor == -1) + { + + } + } + + // --- Caret --- + + void SetCaret(int x, int y, int size) + { + Window caretOwner = guiApp.caretOwner; + Window window = caretOwner ? caretOwner.rootWindow : null; + if(window && window.windowData) + { + } + } + + void ClearClipboard() + { + if(clipBoardData) + { + delete clipBoardData; + } + } + + bool AllocateClipboard(ClipBoard clipBoard, uint size) + { + bool result = false; + if((clipBoard.text = new0 byte[size])) + result = true; + return result; + } + + bool SaveClipboard(ClipBoard clipBoard) + { + bool result = false; + if(clipBoard.text) + { + if(clipBoardData) + delete clipBoardData; + + clipBoardData = clipBoard.text; + clipBoard.text = null; + result = true; + } + return result; + } + + bool LoadClipboard(ClipBoard clipBoard) + { + bool result = false; + + // The data is inside this client... + if(clipBoardData) + { + clipBoard.text = new char[strlen(clipBoardData)+1]; + strcpy(clipBoard.text, clipBoardData); + result = true; + } + // The data is with another client... + else + { + } + return result; + } + + void UnloadClipboard(ClipBoard clipBoard) + { + delete clipBoard.text; + } + + // --- State based input --- + + bool AcquireInput(Window window, bool state) + { + acquiredInputMode = state; + if(acquiredInputMode) + { + relMouseX = 0; + relMouseY = 0; + } + return true; + } + + bool GetMouseState(MouseButtons * buttons, int * x, int * y) + { + bool result = false; + if(acquiredInputMode == true) + { + if(x) *x = (int)(relMouseX * 1000); + if(y) *y = (int)(relMouseY * 1000); + relMouseX = 0; + relMouseY = 0; + *buttons = buttonsState; + result = true; + } + else + { + if(x) *x = 0; + if(y) *y = 0; + if(buttons) *buttons = 0; + } + return result; + } + + bool GetJoystickState(int device, Joystick joystick) + { + bool result = false; + return result; + } + + bool GetKeyState(Key key) + { + bool keyState = false; + return keyState; + } + + void SetTimerResolution(uint hertz) + { + // timerDelay = hertz ? (1000000 / hertz) : MAXINT; + } + + bool SetIcon(Window window, BitmapResource resource) + { + if(resource) + { + /*Bitmap bitmap { }; + if(bitmap.Load(resource.fileName, null, null)) + { + } + delete bitmap;*/ + } + return true; + } +} + +static Key keyCodeTable[] = +{ + 0, //AKEYCODE_UNKNOWN = 0, + 0, //AKEYCODE_SOFT_LEFT = 1, + 0, //AKEYCODE_SOFT_RIGHT = 2, + 0, //AKEYCODE_HOME = 3, + 0, //AKEYCODE_BACK = 4, + 0, //AKEYCODE_CALL = 5, + 0, //AKEYCODE_ENDCALL = 6, + k0, //AKEYCODE_0 = 7, + k1, //AKEYCODE_1 = 8, + k2, //AKEYCODE_2 = 9, + k3, //AKEYCODE_3 = 10, + k4, //AKEYCODE_4 = 11, + k5, //AKEYCODE_5 = 12, + k6, //AKEYCODE_6 = 13, + k7, //AKEYCODE_7 = 14, + k8, //AKEYCODE_8 = 15, + k9, //AKEYCODE_9 = 16, + keyPadStar, //AKEYCODE_STAR = 17, + Key { k3, shift = true }, //AKEYCODE_POUND = 18, + up, //AKEYCODE_DPAD_UP = 19, + down, //AKEYCODE_DPAD_DOWN = 20, + left, //AKEYCODE_DPAD_LEFT = 21, + right, //AKEYCODE_DPAD_RIGHT = 22, + keyPad5, //AKEYCODE_DPAD_CENTER = 23, + 0, //AKEYCODE_VOLUME_UP = 24, + 0, //AKEYCODE_VOLUME_DOWN = 25, + 0, //AKEYCODE_POWER = 26, + 0, //AKEYCODE_CAMERA = 27, + 0, //AKEYCODE_CLEAR = 28, + a, //AKEYCODE_A = 29, + b, //AKEYCODE_B = 30, + c, //AKEYCODE_C = 31, + d, //AKEYCODE_D = 32, + e, //AKEYCODE_E = 33, + f, //AKEYCODE_F = 34, + g, //AKEYCODE_G = 35, + h, //AKEYCODE_H = 36, + i, //AKEYCODE_I = 37, + j, //AKEYCODE_J = 38, + k, //AKEYCODE_K = 39, + l, //AKEYCODE_L = 40, + m, //AKEYCODE_M = 41, + n, //AKEYCODE_N = 42, + o, //AKEYCODE_O = 43, + p, //AKEYCODE_P = 44, + q, //AKEYCODE_Q = 45, + r, //AKEYCODE_R = 46, + s, //AKEYCODE_S = 47, + t, //AKEYCODE_T = 48, + u, //AKEYCODE_U = 49, + v, //AKEYCODE_V = 50, + w, //AKEYCODE_W = 51, + x, //AKEYCODE_X = 52, + y, //AKEYCODE_Y = 53, + z, //AKEYCODE_Z = 54, + comma, //AKEYCODE_COMMA = 55, + period, //AKEYCODE_PERIOD = 56, + leftAlt, //AKEYCODE_ALT_LEFT = 57, + rightAlt, //AKEYCODE_ALT_RIGHT = 58, + leftShift, //AKEYCODE_SHIFT_LEFT = 59, + rightShift, //AKEYCODE_SHIFT_RIGHT = 60, + tab, //AKEYCODE_TAB = 61, + space, //AKEYCODE_SPACE = 62, + 0, //AKEYCODE_SYM = 63, + 0, //AKEYCODE_EXPLORER = 64, + 0, //AKEYCODE_ENVELOPE = 65, + enter, //AKEYCODE_ENTER = 66, + backSpace, //AKEYCODE_DEL = 67, + backQuote, //AKEYCODE_GRAVE = 68, + minus, //AKEYCODE_MINUS = 69, + plus, //AKEYCODE_EQUALS = 70, + leftBracket, //AKEYCODE_LEFT_BRACKET = 71, + rightBracket, //AKEYCODE_RIGHT_BRACKET = 72, + backSlash, //AKEYCODE_BACKSLASH = 73, + semicolon, //AKEYCODE_SEMICOLON = 74, + quote, //AKEYCODE_APOSTROPHE = 75, + slash, //AKEYCODE_SLASH = 76, + Key { k2, shift = true }, //AKEYCODE_AT = 77, + 0, //AKEYCODE_NUM = 78, // Interpreted as an Alt + 0, //AKEYCODE_HEADSETHOOK = 79, + 0, //AKEYCODE_FOCUS = 80, // *Camera* focus + keyPadPlus, //AKEYCODE_PLUS = 81, + 0, //AKEYCODE_MENU = 82, + 0, //AKEYCODE_NOTIFICATION = 83, + 0, //AKEYCODE_SEARCH = 84, + 0, //AKEYCODE_MEDIA_PLAY_PAUSE= 85, + 0, //AKEYCODE_MEDIA_STOP = 86, + 0, //AKEYCODE_MEDIA_NEXT = 87, + 0, //AKEYCODE_MEDIA_PREVIOUS = 88, + 0, //AKEYCODE_MEDIA_REWIND = 89, + 0, //AKEYCODE_MEDIA_FAST_FORWARD = 90, + 0, //AKEYCODE_MUTE = 91, + 0, //AKEYCODE_PAGE_UP = 92, + 0, //AKEYCODE_PAGE_DOWN = 93, + 0, //AKEYCODE_PICTSYMBOLS = 94, + 0, //AKEYCODE_SWITCH_CHARSET = 95, + 0, //AKEYCODE_BUTTON_A = 96, + 0, //AKEYCODE_BUTTON_B = 97, + 0, //AKEYCODE_BUTTON_C = 98, + 0, //AKEYCODE_BUTTON_X = 99, + 0, //AKEYCODE_BUTTON_Y = 100, + 0, //AKEYCODE_BUTTON_Z = 101, + 0, //AKEYCODE_BUTTON_L1 = 102, + 0, //AKEYCODE_BUTTON_R1 = 103, + 0, //AKEYCODE_BUTTON_L2 = 104, + 0, //AKEYCODE_BUTTON_R2 = 105, + 0, //AKEYCODE_BUTTON_THUMBL = 106, + 0, //AKEYCODE_BUTTON_THUMBR = 107, + 0, //AKEYCODE_BUTTON_START = 108, + 0, //AKEYCODE_BUTTON_SELECT = 109, + 0, //AKEYCODE_BUTTON_MODE = 110, + escape, //AKEYCODE_BUTTON_ESCAPE = 111, + del, //AKEYCODE_BUTTON_ESCAPE = 112, + leftControl, // = 113 + rightControl, // = 114 + capsLock, // = 115 + scrollLock, // = 116 + 0, // = 117 KEYCODE_META_LEFT + 0, // = 118 KEYCODE_META_RIGHT + 0, // = 119 KEYCODE_FUNCTION + printScreen, // = 120 KEYCODE_SYSRQ + pauseBreak, // = 121 + home, // = 122 + end, // = 123 + insert // = 124 +}; diff --git a/ecere/src/gui/drivers/UWPInterface.ec b/ecere/src/gui/drivers/UWPInterface.ec new file mode 100644 index 0000000000..ba58cd9ab6 --- /dev/null +++ b/ecere/src/gui/drivers/UWPInterface.ec @@ -0,0 +1,526 @@ +import "Display" + +#include "gl123es.h" + +default: +__declspec(dllexport) void testClear() +{ + // On HoloLens, it is important to clear to transparent. + glClearColor(0.0f, 0.2f, 0.f, 0.f); +} +private: + +#define _Noreturn + +namespace gui::drivers; + +// #include + +import "Window" +import "Interface" + +import "egl" + +static bool initialized; + +//static int desktopW, desktopH; +static char * clipBoardData; +static float mouseX = 320, mouseY = 240; +static bool acquiredInputMode; +static MouseButtons buttonsState; +//static Pointf lastButtonPressedPos[2]; +static float relMouseX, relMouseY; + +/* +static Pointf lastTouchPos[2]; +static bool touchPadDown[2]; +static bool wasActiveLastPass[2]; +static float lastTouchPadForce[2]; +static bool wasButtonPressedLastPass_trigger[2]; +static bool wasButtonPressedLastPass_bumper[2]; + +enum MouseEvent { OnTrigger, OnBumper, OnTouchpad }; +static MouseEvent prevMouseButtonEvent[2]; +*/ + +default: +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove; +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown; +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp; +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick; +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown; +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp; +extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick; +private: + +class UWPInterface : Interface +{ + class_property(name) = "UWP"; + + // --- User Interface System --- + bool Initialize() + { + // setlocale(LC_ALL, "en_US.UTF-8"); + initialized = false; + + return true; + } + + void Terminate() + { + + } + + #define DBLCLICK_DELAY 300 // 0.3 second + #define DBLCLICK_DELTA 1 + + bool ProcessInput(bool processAll) + { + static Time lastTime, lastClickTime; + Time time = GetTime(); + bool eventAvailable = false; + /*int w = 640 * 2; + int h = 480 * 2; + */ + //int controllerID = 1; //or 2 + Time diffTime; + + if(!initialized) + { + initialized = true; + + lastTime = time; + lastClickTime = time; + } + + diffTime = time - lastTime; + lastTime = time; + + /* + if(desktopW != w || desktopH != h) + { + guiApp.SetDesktopPosition(0, 0, w, h, true); + desktopW = w; + desktopH = h; + guiApp.desktop.Update(null); + } + */ + return eventAvailable; + } + + void Wait() + { + // guiApp.WaitEvent(); + } + + void Lock(Window window) + { + + } + + void Unlock(Window window) + { + + } + + const char ** GraphicsDrivers(int * numDrivers) + { + static const char *graphicsDrivers[] = { "OpenGL" }; + *numDrivers = sizeof(graphicsDrivers) / sizeof(char *); + return (const char **)graphicsDrivers; + } + + void GetCurrentMode(bool * fullScreen, int * resolution, int * colorDepth, int * refreshRate) + { + + *fullScreen = true; + } + + void EnsureFullScreen(bool *fullScreen) + { + *fullScreen = true; + } + + bool ScreenMode(bool fullScreen, int resolution, int colorDepth, int refreshRate, bool * textMode) + { + bool result = true; + return result; + } + + // --- Window Creation --- + void * CreateRootWindow(Window window) + { + return (void *)1; + } + + void DestroyRootWindow(Window window) + { + + } + + // -- Window manipulation --- + + void SetRootWindowCaption(Window window, const char * name) + { + } + + void PositionRootWindow(Window window, int x, int y, int w, int h, bool move, bool resize) + { + } + + void OrderRootWindow(Window window, bool topMost) + { + + } + + void SetRootWindowColor(Window window) + { + + } + + void OffsetWindow(Window window, int * x, int * y) + { + } + + void UpdateRootWindow(Window window) + { + + if(!window.parent || !window.parent.display) + { + if(window.visible) + { + Box box = window.box; + box.left -= window.clientStart.x; + box.top -= window.clientStart.y; + box.right -= window.clientStart.x; + box.bottom -= window.clientStart.y; + // Logf("Update root window %s\n", window.name); + window.Update(null); + box.left += window.clientStart.x; + box.top += window.clientStart.y; + box.right += window.clientStart.x; + box.bottom += window.clientStart.y; + window.UpdateDirty(box); + } + } + } + + + void SetRootWindowState(Window window, WindowState state, bool visible) + { + } + + void FlashRootWindow(Window window) + { + + } + + void ActivateRootWindow(Window window) + { + + } + + // --- Mouse-based window movement --- + + void StartMoving(Window window, int x, int y, bool fromKeyBoard) + { + + } + + void StopMoving(Window window) + { + + } + + // -- Mouse manipulation --- + + void GetMousePosition(int *x, int *y) + { + *x = (int)mouseX; + *y = (int)mouseY; + } + + void SetMousePosition(int x, int y) + { + mouseX = x; + mouseY = y; + } + + void SetMouseRange(Window window, Box box) + { + } + + void SetMouseCapture(Window window) + { + } + + // -- Mouse cursor --- + + void SetMouseCursor(Window window, int cursor) + { + if(cursor == -1) + { + + } + } + + // --- Caret --- + + void SetCaret(int x, int y, int size) + { + Window caretOwner = guiApp.caretOwner; + Window window = caretOwner ? caretOwner.rootWindow : null; + if(window && window.windowData) + { + } + } + + void ClearClipboard() + { + if(clipBoardData) + { + delete clipBoardData; + } + } + + bool AllocateClipboard(ClipBoard clipBoard, uint size) + { + bool result = false; + if((clipBoard.text = new0 byte[size])) + result = true; + return result; + } + + bool SaveClipboard(ClipBoard clipBoard) + { + bool result = false; + if(clipBoard.text) + { + if(clipBoardData) + delete clipBoardData; + + clipBoardData = clipBoard.text; + clipBoard.text = null; + result = true; + } + return result; + } + + bool LoadClipboard(ClipBoard clipBoard) + { + bool result = false; + + // The data is inside this client... + if(clipBoardData) + { + clipBoard.text = new char[strlen(clipBoardData)+1]; + strcpy(clipBoard.text, clipBoardData); + result = true; + } + // The data is with another client... + else + { + } + return result; + } + + void UnloadClipboard(ClipBoard clipBoard) + { + delete clipBoard.text; + } + + // --- State based input --- + + bool AcquireInput(Window window, bool state) + { + acquiredInputMode = state; + if(acquiredInputMode) + { + relMouseX = 0; + relMouseY = 0; + } + return true; + } + + bool GetMouseState(MouseButtons * buttons, int * x, int * y) + { + bool result = false; + if(acquiredInputMode == true) + { + if(x) *x = (int)(relMouseX * 1000); + if(y) *y = (int)(relMouseY * 1000); + relMouseX = 0; + relMouseY = 0; + *buttons = buttonsState; + result = true; + } + else + { + if(x) *x = 0; + if(y) *y = 0; + if(buttons) *buttons = 0; + } + return result; + } + + bool GetJoystickState(int device, Joystick joystick) + { + bool result = false; + return result; + } + + bool GetKeyState(Key key) + { + bool keyState = false; + return keyState; + } + + void SetTimerResolution(uint hertz) + { + // timerDelay = hertz ? (1000000 / hertz) : MAXINT; + } + + bool SetIcon(Window window, BitmapResource resource) + { + if(resource) + { + /*Bitmap bitmap { }; + if(bitmap.Load(resource.fileName, null, null)) + { + } + delete bitmap;*/ + } + return true; + } +} + +/* +static Key keyCodeTable[] = +{ + 0, //AKEYCODE_UNKNOWN = 0, + 0, //AKEYCODE_SOFT_LEFT = 1, + 0, //AKEYCODE_SOFT_RIGHT = 2, + 0, //AKEYCODE_HOME = 3, + 0, //AKEYCODE_BACK = 4, + 0, //AKEYCODE_CALL = 5, + 0, //AKEYCODE_ENDCALL = 6, + k0, //AKEYCODE_0 = 7, + k1, //AKEYCODE_1 = 8, + k2, //AKEYCODE_2 = 9, + k3, //AKEYCODE_3 = 10, + k4, //AKEYCODE_4 = 11, + k5, //AKEYCODE_5 = 12, + k6, //AKEYCODE_6 = 13, + k7, //AKEYCODE_7 = 14, + k8, //AKEYCODE_8 = 15, + k9, //AKEYCODE_9 = 16, + keyPadStar, //AKEYCODE_STAR = 17, + Key { k3, shift = true }, //AKEYCODE_POUND = 18, + up, //AKEYCODE_DPAD_UP = 19, + down, //AKEYCODE_DPAD_DOWN = 20, + left, //AKEYCODE_DPAD_LEFT = 21, + right, //AKEYCODE_DPAD_RIGHT = 22, + keyPad5, //AKEYCODE_DPAD_CENTER = 23, + 0, //AKEYCODE_VOLUME_UP = 24, + 0, //AKEYCODE_VOLUME_DOWN = 25, + 0, //AKEYCODE_POWER = 26, + 0, //AKEYCODE_CAMERA = 27, + 0, //AKEYCODE_CLEAR = 28, + a, //AKEYCODE_A = 29, + b, //AKEYCODE_B = 30, + c, //AKEYCODE_C = 31, + d, //AKEYCODE_D = 32, + e, //AKEYCODE_E = 33, + f, //AKEYCODE_F = 34, + g, //AKEYCODE_G = 35, + h, //AKEYCODE_H = 36, + i, //AKEYCODE_I = 37, + j, //AKEYCODE_J = 38, + k, //AKEYCODE_K = 39, + l, //AKEYCODE_L = 40, + m, //AKEYCODE_M = 41, + n, //AKEYCODE_N = 42, + o, //AKEYCODE_O = 43, + p, //AKEYCODE_P = 44, + q, //AKEYCODE_Q = 45, + r, //AKEYCODE_R = 46, + s, //AKEYCODE_S = 47, + t, //AKEYCODE_T = 48, + u, //AKEYCODE_U = 49, + v, //AKEYCODE_V = 50, + w, //AKEYCODE_W = 51, + x, //AKEYCODE_X = 52, + y, //AKEYCODE_Y = 53, + z, //AKEYCODE_Z = 54, + comma, //AKEYCODE_COMMA = 55, + period, //AKEYCODE_PERIOD = 56, + leftAlt, //AKEYCODE_ALT_LEFT = 57, + rightAlt, //AKEYCODE_ALT_RIGHT = 58, + leftShift, //AKEYCODE_SHIFT_LEFT = 59, + rightShift, //AKEYCODE_SHIFT_RIGHT = 60, + tab, //AKEYCODE_TAB = 61, + space, //AKEYCODE_SPACE = 62, + 0, //AKEYCODE_SYM = 63, + 0, //AKEYCODE_EXPLORER = 64, + 0, //AKEYCODE_ENVELOPE = 65, + enter, //AKEYCODE_ENTER = 66, + backSpace, //AKEYCODE_DEL = 67, + backQuote, //AKEYCODE_GRAVE = 68, + minus, //AKEYCODE_MINUS = 69, + plus, //AKEYCODE_EQUALS = 70, + leftBracket, //AKEYCODE_LEFT_BRACKET = 71, + rightBracket, //AKEYCODE_RIGHT_BRACKET = 72, + backSlash, //AKEYCODE_BACKSLASH = 73, + semicolon, //AKEYCODE_SEMICOLON = 74, + quote, //AKEYCODE_APOSTROPHE = 75, + slash, //AKEYCODE_SLASH = 76, + Key { k2, shift = true }, //AKEYCODE_AT = 77, + 0, //AKEYCODE_NUM = 78, // Interpreted as an Alt + 0, //AKEYCODE_HEADSETHOOK = 79, + 0, //AKEYCODE_FOCUS = 80, // *Camera* focus + keyPadPlus, //AKEYCODE_PLUS = 81, + 0, //AKEYCODE_MENU = 82, + 0, //AKEYCODE_NOTIFICATION = 83, + 0, //AKEYCODE_SEARCH = 84, + 0, //AKEYCODE_MEDIA_PLAY_PAUSE= 85, + 0, //AKEYCODE_MEDIA_STOP = 86, + 0, //AKEYCODE_MEDIA_NEXT = 87, + 0, //AKEYCODE_MEDIA_PREVIOUS = 88, + 0, //AKEYCODE_MEDIA_REWIND = 89, + 0, //AKEYCODE_MEDIA_FAST_FORWARD = 90, + 0, //AKEYCODE_MUTE = 91, + 0, //AKEYCODE_PAGE_UP = 92, + 0, //AKEYCODE_PAGE_DOWN = 93, + 0, //AKEYCODE_PICTSYMBOLS = 94, + 0, //AKEYCODE_SWITCH_CHARSET = 95, + 0, //AKEYCODE_BUTTON_A = 96, + 0, //AKEYCODE_BUTTON_B = 97, + 0, //AKEYCODE_BUTTON_C = 98, + 0, //AKEYCODE_BUTTON_X = 99, + 0, //AKEYCODE_BUTTON_Y = 100, + 0, //AKEYCODE_BUTTON_Z = 101, + 0, //AKEYCODE_BUTTON_L1 = 102, + 0, //AKEYCODE_BUTTON_R1 = 103, + 0, //AKEYCODE_BUTTON_L2 = 104, + 0, //AKEYCODE_BUTTON_R2 = 105, + 0, //AKEYCODE_BUTTON_THUMBL = 106, + 0, //AKEYCODE_BUTTON_THUMBR = 107, + 0, //AKEYCODE_BUTTON_START = 108, + 0, //AKEYCODE_BUTTON_SELECT = 109, + 0, //AKEYCODE_BUTTON_MODE = 110, + escape, //AKEYCODE_BUTTON_ESCAPE = 111, + del, //AKEYCODE_BUTTON_ESCAPE = 112, + leftControl, // = 113 + rightControl, // = 114 + capsLock, // = 115 + scrollLock, // = 116 + 0, // = 117 KEYCODE_META_LEFT + 0, // = 118 KEYCODE_META_RIGHT + 0, // = 119 KEYCODE_FUNCTION + printScreen, // = 120 KEYCODE_SYSRQ + pauseBreak, // = 121 + home, // = 122 + end, // = 123 + insert // = 124 +}; +*/ diff --git a/ecere/src/gui/drivers/Win32Interface.ec b/ecere/src/gui/drivers/Win32Interface.ec index 51109b99d5..34be176565 100644 --- a/ecere/src/gui/drivers/Win32Interface.ec +++ b/ecere/src/gui/drivers/Win32Interface.ec @@ -4,7 +4,7 @@ import "instance" #define UNICODE -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) #undef WINVER #define WINVER 0x0500 @@ -165,6 +165,7 @@ static Box lastMonitorAreas[32]; static Box monitorAreas[32]; static int monitor; +#if !defined(__UWP__) static bool EnumerateMonitors(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { MONITORINFO info = { 0 }; @@ -174,6 +175,7 @@ static bool EnumerateMonitors(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMoni monitor++; return monitor < 32; } +#endif static bool externalDisplayChange; @@ -280,12 +282,14 @@ class Win32Interface : Interface lastTime = time; // Every sec, check for the auto hide property +#if !defined(__UWP__) if(time - lastAutoHideCheck > 1) { APPBARDATA appBarData = { 0 }; newTaskBarState = (int)SHAppBarMessage(ABM_GETSTATE, &appBarData); lastAutoHideCheck = time; } +#endif monitor = 0; EnumDisplayMonitors(null, null, EnumerateMonitors, 0); @@ -313,6 +317,7 @@ class Win32Interface : Interface y = GetSystemMetrics(SM_YVIRTUALSCREEN) }; +#if !defined(__UWP__) { HMONITOR monitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY); if(monitor) @@ -327,6 +332,7 @@ class Win32Interface : Interface h = info.rcMonitor.bottom - info.rcWork.top; } } +#endif { WINDOWPLACEMENT placement = { 0 }; @@ -393,18 +399,80 @@ class Win32Interface : Interface bool ::ProcessKeyMessage(Window window, DWORD msg, WPARAM wParam, LPARAM lParam, unichar ch) { bool result = true; + static Key code2 = 0; Key code = 0; Key key; // UNICODE FIX bool frenchShift = (ch < 0x10000) ? (((VkKeyScan((uint16)ch) >> 8) & 6) == 6) : false; + unsigned short device = 0; if(msg == WM_CHAR || msg == WM_DEADCHAR) { wParam = 0; lParam = 0; } + if(msg == WM_MOUSEWHEEL) code = (((short) HIWORD(wParam)) < 0) ? wheelDown : wheelUp; + else if(msg == WM_APPCOMMAND) + { + short cmd = GET_APPCOMMAND_LPARAM(lParam); + int state = GET_KEYSTATE_LPARAM(lParam); + device = GET_DEVICE_LPARAM(lParam); + /*switch(device) + { + case FAPPCOMMAND_KEY: + case FAPPCOMMAND_MOUSE: + case FAPPCOMMAND_OEM: + break; + }*/ + switch(state) + { + case MK_CONTROL: code.ctrl = true; break; + case MK_SHIFT: code.shift = true; break; + case MK_LBUTTON: + case MK_MBUTTON: + case MK_RBUTTON: + case MK_XBUTTON1: + case MK_XBUTTON2: + break; + } + switch(cmd) + { + // still unused remote control buttons: + // record, pause, rewind, fastForward, eject, remoteHome, back, menu1, menu2, menu3, menu4, select + case APPCOMMAND_BASS_BOOST: code = bassBoost; break; + case APPCOMMAND_BASS_DOWN: code = bassDown; break; + case APPCOMMAND_BASS_UP: code = bassUp; break; + case APPCOMMAND_BROWSER_BACKWARD: code = browserBackward; break; + case APPCOMMAND_BROWSER_FAVORITES: code = browserFavorites; break; + case APPCOMMAND_BROWSER_FORWARD: code = browserForward; break; + case APPCOMMAND_BROWSER_HOME: code = browserHome; break; + case APPCOMMAND_BROWSER_REFRESH: code = browserRefresh; break; + case APPCOMMAND_BROWSER_SEARCH: code = browserSearch; break; + case APPCOMMAND_BROWSER_STOP: code = browserStop; break; + //case APPCOMMAND_DELETE: code = delete; break; + //case APPCOMMAND_DWM_FLIP3D: code = flip3D; break; + case APPCOMMAND_LAUNCH_APP1: code = launchApp1; break; + case APPCOMMAND_LAUNCH_APP2: code = launchApp2; break; + case APPCOMMAND_LAUNCH_MAIL: code = launchMail; break; + case APPCOMMAND_LAUNCH_MEDIA_SELECT: code = launchMediaSelect; break; + case APPCOMMAND_MEDIA_NEXTTRACK: code = /*mediaN*/nextTrack; break; + case APPCOMMAND_MEDIA_PLAY_PAUSE: code = /*mediaPlayPause*/play; break; + case APPCOMMAND_MEDIA_PREVIOUSTRACK: code = /*mediaP*/previousTrack; break; + case APPCOMMAND_MEDIA_STOP: code = /*mediaS*/stop; break; + case APPCOMMAND_MICROPHONE_VOLUME_DOWN: code = micVolumeDown; break; + case APPCOMMAND_MICROPHONE_VOLUME_MUTE: code = micMute; break; + case APPCOMMAND_MICROPHONE_VOLUME_UP: code = micVolumeUp; break; + case APPCOMMAND_TREBLE_DOWN: code = trebleDown; break; + case APPCOMMAND_TREBLE_UP: code = trebleUp; break; + case APPCOMMAND_VOLUME_DOWN: code = volumeDown; break; + case APPCOMMAND_VOLUME_MUTE: code = /*volumeM*/mute; break; + case APPCOMMAND_VOLUME_UP: code = volumeUp; break; + } + if(device == FAPPCOMMAND_KEY) + code2 = code; + } else { key = (byte)((lParam & 0xFF0000)>>16); @@ -431,7 +499,11 @@ class Win32Interface : Interface result = window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, code, 0); } - else + else if(msg == WM_APPCOMMAND && device != FAPPCOMMAND_KEY) + { + result = window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, code, 0); + } + else if(msg != WM_APPCOMMAND) { if(key != leftShift && key != rightShift && ::GetKeyState(VK_SHIFT) & 0x80000) code.shift = true; @@ -447,7 +519,8 @@ class Win32Interface : Interface if(msg == WM_KEYUP || msg == WM_SYSKEYUP) { - result = window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, code, ch); + result = window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, code2 ? code2 : code, ch); + code2 = 0; } else { @@ -462,10 +535,10 @@ class Win32Interface : Interface } */ - result = window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, code, ch); + result = window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, code2 ? code2 : code, ch); } else if(key<128) - result = window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, code,ch); + result = window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, code2 ? code2 : code,ch); } } @@ -503,6 +576,7 @@ class Win32Interface : Interface HWND modalWindow = modalRoot ? modalRoot.windowHandle : null; +#if !defined(__UWP__) if(window.creationActivation == flash || window.hasMinimize || window.borderStyle != sizableThin) { FLASHWINFO flashInfo = { 0 }; @@ -512,6 +586,7 @@ class Win32Interface : Interface flashInfo.dwFlags = FLASHW_STOP; FlashWindowEx((void *)&flashInfo); } +#endif if(modalWindow && modalWindow != windowHandle) modalRoot.ExternalActivate(true, true, window, null); @@ -574,12 +649,16 @@ class Win32Interface : Interface guiApp.SetAppFocus(false); ShowWindow(windowHandle, SW_MINIMIZE); ChangeDisplaySettings(null,0); +#if !defined(__UWP__) SetSystemPaletteUse(hdc, SYSPAL_STATIC); +#endif } else { ChangeDisplaySettings(&devMode, CDS_FULLSCREEN); +#if !defined(__UWP__) SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC); +#endif if(window.display) window.display.RestorePalette(); ShowWindow(windowHandle, SW_SHOWNORMAL); @@ -611,6 +690,7 @@ class Win32Interface : Interface break; case WM_PAINT: { +#if !defined(__UWP__) PAINTSTRUCT ps; if(!window.alphaBlend || window.display.pixelFormat != pixelFormat888) { @@ -654,6 +734,7 @@ class Win32Interface : Interface BeginPaint(windowHandle, &ps); EndPaint(windowHandle, &ps); } +#endif break; } case WM_DISPLAYCHANGE: @@ -740,6 +821,7 @@ class Win32Interface : Interface { COMPOSITIONFORM form; Window caretOwner = guiApp.caretOwner; +#if !defined(__UWP__) if(caretOwner) { HIMC ctx = ImmGetContext(windowHandle); @@ -750,17 +832,25 @@ class Win32Interface : Interface ImmSetCompositionWindow(ctx, &form); ImmReleaseContext(windowHandle, ctx); } +#endif } if(msg != WM_MOUSEWHEEL) delete window; break; + case WM_APPCOMMAND: + { + ProcessKeyMessage(window, msg, wParam, lParam, ch); + break; + } + case WM_IME_STARTCOMPOSITION: { COMPOSITIONFORM form; Window caretOwner = guiApp.caretOwner; if(caretOwner) { +#if !defined(__UWP__) HIMC ctx = ImmGetContext(windowHandle); form.dwStyle = CFS_POINT; form.ptCurrentPos.x = caretOwner.caretPos.x - caretOwner.scroll.x + caretOwner.absPosition.x - window.absPosition.x + 4; @@ -795,6 +885,7 @@ class Win32Interface : Interface ImmSetCompositionFont(ctx, &font); ImmReleaseContext(windowHandle, ctx); } +#endif return 1; } break; @@ -1226,7 +1317,11 @@ class Win32Interface : Interface }; HDC hdc = GetDC(0); lastRes = MAKELPARAM(GetSystemMetrics(SM_CYSCREEN), GetSystemMetrics(SM_CXSCREEN)); +#if defined(__UWP__) + lastBits = 32; +#else lastBits = (WPARAM)GetDeviceCaps(hdc, BITSPIXEL); +#endif ReleaseDC(0, hdc); AttachConsole(-1); @@ -1367,15 +1462,27 @@ class Win32Interface : Interface devMode.dmFields |=DM_BITSPERPEL; devMode.dmFields |=DM_PELSWIDTH|DM_PELSHEIGHT; devMode.dmFields |= DM_DISPLAYFREQUENCY; +#if defined(__UWP__) + devMode.dmBitsPerPel = colorDepth ? GetDepthBits(colorDepth) : 32; +#else devMode.dmBitsPerPel = colorDepth ? GetDepthBits(colorDepth) : GetDeviceCaps(hdc, BITSPIXEL); +#endif + devMode.dmPelsWidth = resolution ? GetResolutionWidth(resolution) : GetSystemMetrics(SM_CXSCREEN); devMode.dmPelsHeight = resolution ? GetResolutionHeight(resolution) : GetSystemMetrics(SM_CYSCREEN); + +#if defined(__UWP__) + devMode.dmDisplayFrequency = refreshRate ? refreshRate : 60; +#else devMode.dmDisplayFrequency = refreshRate ? refreshRate : GetDeviceCaps(hdc, VREFRESH); +#endif if(ChangeDisplaySettings(&devMode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) result = false; else { +#if !defined(__UWP__) SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC); +#endif guiApp.SetDesktopPosition(0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), false); } } @@ -1385,7 +1492,9 @@ class Win32Interface : Interface if(!firstTime) ChangeDisplaySettings(null, 0); firstTime = false; +#if !defined(__UWP__) SetSystemPaletteUse(hdc, SYSPAL_STATIC); +#endif desktopX = desktopY = desktopW = desktopH = 0; RepositionDesktop(false); diff --git a/ecere/src/gui/drivers/XInterface.ec b/ecere/src/gui/drivers/XInterface.ec index f15e28c32f..f4d4c8dace 100644 --- a/ecere/src/gui/drivers/XInterface.ec +++ b/ecere/src/gui/drivers/XInterface.ec @@ -45,6 +45,7 @@ default: #define KeyCode X11KeyCode #define Picture X11Picture #define Bool X11Bool +#define Glyph X11Glyph #define _XTYPEDEF_BOOL typedef int X11Bool; @@ -79,6 +80,7 @@ typedef int X11Bool; #undef Display #undef Time #undef KeyCode +#undef Glyph #undef uint #undef new @@ -125,6 +127,7 @@ static Point lastMouse; static MouseButtons buttonsState; static bool keyStates[KeyCode]; +static bool ctrlClick = false; static enum NETWMStateAction { remove = 0, add = 1, toggle = 2 }; @@ -283,6 +286,9 @@ public: Box decor; bool gotFrameExtents; bool currentlyVisible; +#if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) && !defined(__ODROID__) + GLXFBConfig config; +#endif }; bool XGetBorderWidths(Window window, Box box) @@ -877,7 +883,11 @@ static bool ProcessKeyMessage(Window window, uint keyCode, int release, XKeyEven int numBytes; if(key < KeyCode::enumSize) + { keyStates[key] = false; + if(key == leftControl || key == rightControl) + ctrlClick = false; + } if(windowData && windowData.ic) ch = buflength ? UTF8GetChar(buf, &numBytes) : 0; if(ch == 127) ch = 0; // printf("Release! %d %d %d\n", keysym, code, ch); @@ -1357,6 +1367,8 @@ static void X11UpdateState(Window window, bool * unmaximized) { if(window.state != maximized) { + // NOTE: State is being forced to maximized here, without calling state property, ignoring OnStateChange return value + window.OnStateChange(maximized, 0); *&window.state = maximized; if(!window.nativeDecorations) window.CreateSystemChildren(); @@ -1366,6 +1378,8 @@ static void X11UpdateState(Window window, bool * unmaximized) { if(window.state != minimized) { + // NOTE: State is being forced to minimized here, without calling state property, ignoring OnStateChange return value + window.OnStateChange(minimized, 0); *&window.state = minimized; if(!window.nativeDecorations) window.CreateSystemChildren(); @@ -1375,6 +1389,8 @@ static void X11UpdateState(Window window, bool * unmaximized) { if(unmaximized && window.state == maximized) *unmaximized = true; + // NOTE: State is being forced to normal here, without calling state property, ignoring OnStateChange return value + window.OnStateChange(normal, 0); *&window.state = normal; if(!window.nativeDecorations) window.CreateSystemChildren(); @@ -1641,6 +1657,8 @@ class XInterface : Interface XFreeCursor(xGlobalDisplay, systemCursors[hand]); XFreeCursor(xGlobalDisplay, systemCursors[arrow]); + delete clipBoardData; + //XPutBackEvent(xGlobalDisplay, &e); // xThread.Wait(); // delete xThread; @@ -1823,7 +1841,7 @@ class XInterface : Interface } if(event->state & ShiftMask) keyFlags.shift = true; - if(event->state & ControlMask) keyFlags.ctrl = true; + if(event->state & ControlMask) keyFlags.ctrl = true, ctrlClick = true; if(event->state & Mod1Mask) keyFlags.alt = true; if(event->state & Button1Mask) keyFlags.left = true; if(event->state & Button2Mask) keyFlags.middle = true; @@ -1941,6 +1959,13 @@ class XInterface : Interface box.top = event->y; box.right = box.left + event->width - 1; box.bottom = box.top + event->height - 1; + while(XCheckIfEvent(xGlobalDisplay, (XEvent *)thisEvent, EventChecker, (void *)Expose)) + { + box.left = Min(box.left, event->x); + box.top = Min(box.top, event->y); + box.right = Max(box.right, event->x + event->width - 1); + box.bottom = Max(box.bottom, event->y + event->height - 1); + } window.UpdateDirty(box); break; } @@ -2022,7 +2047,12 @@ class XInterface : Interface if(modalRoot) modalRoot.ExternalActivate(true, true, window, null); // lastActive); else + { + // FIXME: Temporary work around for alt state ? + keyStates[leftAlt] = false; + keyStates[rightAlt] = false; window.ExternalActivate(true, true, window, null); // lastActive); + } } windowData = modalRoot ? modalRoot.windowData : window.windowData; if(windowData && windowData.ic) @@ -2450,6 +2480,9 @@ class XInterface : Interface Visual * visual; XIC ic = null; unsigned long mask = EVENT_MASK; +#if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) && !defined(__ODROID__) + GLXFBConfig fbconfig = null; +#endif // Old WM (e.g. TWM), use built-in decorations if(!atomsSupported[_net_wm_state]) @@ -2477,7 +2510,7 @@ class XInterface : Interface }; int numAttribs = 14; - GLXFBConfig *fbconfigs = null, fbconfig; + GLXFBConfig *fbconfigs = null; int numfbconfigs; int i; //printf("Samples = %d, alpha = %d\n", samples, alpha); @@ -2521,6 +2554,7 @@ class XInterface : Interface else { //printf("Found what we're looking for\n"); + fbconfig = fbconfigs[i]; found = true; break; } @@ -2848,7 +2882,11 @@ class XInterface : Interface } // XFlush(xGlobalDisplay); - window.windowData = XWindowData { visualInfo, ic }; + window.windowData = XWindowData { visualInfo, ic +#if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) && !defined(__ODROID__) + , config = fbconfig +#endif + }; XSaveContext(xGlobalDisplay, windowHandle, windowContext, (XPointer)window); @@ -3037,6 +3075,7 @@ class XInterface : Interface window.nativeDecorations = false; if(!window.parent || !window.parent.display) { + bool justMadeVisible = false; XWindowData windowData = window.windowData; //Logf("Set root window state %d %s\n", state, window.name); if(visible) @@ -3048,6 +3087,7 @@ class XInterface : Interface XA_CARDINAL,32,PropModeReplace, (byte *)&t, 1); XMapWindow(xGlobalDisplay, (X11Window)window.windowHandle); windowData.currentlyVisible = true; + justMadeVisible = true; WaitForViewableWindow(window); if(window.creationActivation == activate && guiApp.desktop.active && state != minimized) ActivateRootWindow(window); @@ -3124,7 +3164,7 @@ class XInterface : Interface if(atomsSupported[_net_wm_state]) { // Maximize / Restore the window - if(curState != state) + if(justMadeVisible || curState != state) SetNETWMState((X11Window)window.windowHandle, true, state == maximized ? add : remove, atoms[_net_wm_state_maximized_vert], atoms[_net_wm_state_maximized_horz]); @@ -3371,12 +3411,11 @@ class XInterface : Interface if(!fullScreenMode) { for(rootWindow = rootWindow.children.first; - rootWindow && !rootWindow.windowHandle; + rootWindow && (!rootWindow.windowHandle || !rootWindow.visible || rootWindow.interim); rootWindow = rootWindow.next); } - if(clipBoardData) - delete clipBoardData; - else if(rootWindow) + delete clipBoardData; + if(rootWindow) XSetSelectionOwner(xGlobalDisplay, atoms[clipboard], (X11Window) rootWindow.windowHandle, CurrentTime); clipBoardData = clipBoard.text; @@ -3406,7 +3445,7 @@ class XInterface : Interface if(!fullScreenMode) { for(rootWindow = rootWindow.children.first; - rootWindow && !rootWindow.windowHandle; + rootWindow && (!rootWindow.windowHandle || !rootWindow.visible || rootWindow.interim); rootWindow = rootWindow.next); } if(rootWindow) @@ -3486,7 +3525,8 @@ class XInterface : Interface if(x) *x = lastMouse.x - acquireStart.x; if(y) *y = lastMouse.y - acquireStart.y; *buttons = buttonsState; - SetMousePosition(acquireStart.x, acquireStart.y); + if((x && *x) || (y && *y)) + SetMousePosition(acquireStart.x, acquireStart.y); lastMouse = acquireStart; result = true; } @@ -3544,7 +3584,7 @@ class XInterface : Interface else if(key == shift) return keyStates[leftShift] || keyStates[rightShift]; else if(key == control) - return keyStates[leftControl] || keyStates[rightControl]; + return keyStates[leftControl] || keyStates[rightControl] || ctrlClick; else return keyStates[key.code]; } diff --git a/ecere/src/gui/skins/WindowsSkin.ec b/ecere/src/gui/skins/WindowsSkin.ec index 225d6e67ce..e448bef23a 100644 --- a/ecere/src/gui/skins/WindowsSkin.ec +++ b/ecere/src/gui/skins/WindowsSkin.ec @@ -1,4 +1,4 @@ -#if defined(WIN32) +#if defined(__WIN32__) #define WIN32_LEAN_AND_MEAN #define String _String #define Method _Method @@ -11,7 +11,7 @@ import "Window" -#if !defined(WIN32) +#if !defined(__WIN32__) bool gui::drivers::XGetBorderWidths(Window window, Box box); #endif @@ -187,7 +187,7 @@ public class WindowsSkin_Window : Window if(nativeDecorations && rootWindow == this && windowHandle && !is3D) { -#if defined(WIN32) +#if defined(__WIN32__) RECT rcClient = { 0 }, rcWindow = { 0 }; if(GetClientRect(windowHandle, &rcClient) && GetWindowRect(windowHandle, &rcWindow)) { @@ -276,7 +276,7 @@ public class WindowsSkin_Window : Window if(nativeDecorations && rootWindow == this && windowHandle && !is3D) { -#if defined(WIN32) +#if defined(__WIN32__) RECT rcWindow = { 0, 0, 0, 0 }; POINT client00 = { 0, 0 }; ClientToScreen(windowHandle, &client00); diff --git a/ecere/src/gui/typeEdit.ec b/ecere/src/gui/typeEdit.ec index 7451639a07..accce5fa37 100644 --- a/ecere/src/gui/typeEdit.ec +++ b/ecere/src/gui/typeEdit.ec @@ -12,7 +12,7 @@ class Enumeration : struct static __attribute__((unused)) void UnusedFunction() { - int a; + int a = 0; a.OnGetString(0,0,0); a.OnFree(); a.OnCopy(null); @@ -84,11 +84,11 @@ static void OnDisplay(Class _class, void * data, Surface surface, int x, int y, static char tempString[16384]; const char * string; int len; - bool needClass = false; + ObjectNotationType onType = none; int w, h; tempString[0] = '\0'; - string = ((const char * (*)(void *, void *, void *, void *, void *))(void *)_class._vTbl[__ecereVMethodID_class_OnGetString])(_class, data, tempString, fieldData, &needClass); + string = ((const char * (*)(void *, void *, void *, void *, void *))(void *)_class._vTbl[__ecereVMethodID_class_OnGetString])(_class, data, tempString, fieldData, &onType); len = string ? strlen(string) : 0; //surface.TextOpacity(false); @@ -200,8 +200,8 @@ static Window OnEdit(Class _class, void * data, Window window, Window master, if(data) { - bool needClass = false; - const char * result = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)_class._vTbl[__ecereVMethodID_class_OnGetString])(_class, data, tempString, fieldData, &needClass); + ObjectNotationType onType = none; + const char * result = ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)_class._vTbl[__ecereVMethodID_class_OnGetString])(_class, data, tempString, fieldData, &onType); if(result) string = result; } diff --git a/ecere/src/net/HTTPFile.ec b/ecere/src/net/HTTPFile.ec index 2506e3a625..28b6cd4dbf 100644 --- a/ecere/src/net/HTTPFile.ec +++ b/ecere/src/net/HTTPFile.ec @@ -1,320 +1,55 @@ -#ifndef ECERE_NONET - +#undef __BLOCKS__ #include import "List" + +#if !defined(ECERE_NONET) && !defined(ECERE_NOCURL)//&& !defined(__ANDROID__) // Curl / OpenSSL not set up right now for Android builds + import "network" #ifndef ECERE_NOSSL import "SSLSocket" #endif -class ConnectionsHolder -{ - List connections { }; - - ~ConnectionsHolder() - { - HTTPConnection c; - while((c = connections[0])) - delete c; // The HTTPConnection destructor will take out from the list - } -} - -static ConnectionsHolder holder { }; +#define Size Size_ +#define String String_ +#define Interface Interface_ +#define Socket Socket_ +#define Alignment Alignment_ +#include +#undef Size +#undef String +#undef Interface +#undef Socket +#undef Alignment + +#undef CompareString + +#include +#ifndef WIN32 +# include +#endif +#include +#include namespace net; -/*static */define HTTPFILE_BUFFERSIZE = 65536; - -static Mutex connectionsMutex { }; - -static class ServerNode : BTNode +static intsize writeMemoryCallback(void *contents, intsize size, intsize nmemb, HTTPFile file) { - char address[24]; - bool resolved; - ~ServerNode() - { - delete (char *)key; - } -} - -static class ServerNameCache -{ - BinaryTree servers - { - CompareKey = (void *)BinaryTree::CompareString; - }; - Mutex mutex { }; - - ~ServerNameCache() - { - ServerNode server; - while((server = (ServerNode)servers.root)) - { - servers.Remove(server); - delete server; - } - } - bool Resolve(const char * host, char * address) - { - ServerNode server; - mutex.Wait(); - server = (ServerNode)servers.FindString(host); - if(!server) - { - server = ServerNode { key = (uintptr)CopyString(host) }; - servers.Add(server); - server.resolved = GetAddressFromName(host, server.address); - } - mutex.Release(); - if(server.resolved) - strcpy(address, server.address); - return server.resolved; - } -}; - -static ServerNameCache serverNameCache { }; - -static const char * GetString(const char * string, const char * what, int count) -{ - int c; - for(c = 0; what[c]; c++) - { - if((count && c >= count) || (string[c] != what[c] && tolower(string[c]) != tolower(what[c]))) - return null; - } - return string + c; -} - -#ifdef ECERE_NOSSL -private class HTTPConnection : Socket -#else -private class HTTPConnection : SSLSocket -#endif -{ - class_no_expansion; - char * server; - int port; - HTTPFile file; - bool secure; - - processAlone = true; - - ~HTTPConnection() - { - // printf("Before TakeOut we have %d connections:\n", holder.connections.count); for(c : holder.connections) ::PrintLn(c.server); ::PrintLn(""); - holder.connections.TakeOut(this); - /* - PrintLn(server, " Connection Closed (", holder.connections.count, ") opened"); - printf("Now we have %d connections:\n", holder.connections.count); - for(c : holder.connections) - { - ::PrintLn(c.server); - } - ::PrintLn(""); - */ - delete server; - } - - void OnDisconnect(int code) - { - connectionsMutex.Wait(); - if(file) - delete file.connection; // This decrements the file's reference - - connectionsMutex.Release(); - - // PrintLn(server, " Disconnected Us"); - - delete this; // The 'connections' reference - } - - uint Open_OnReceive(const byte * buffer, uint count) - { - HTTPFile file = this.file; - int pos = 0; - int c; - while(!file.done) - { - bool gotEndLine = false; - for(c = 0; c<(int)count-1; c++) - { - if(buffer[c] == '\r' && buffer[c+1] == '\n') - { - gotEndLine = true; - break; - } - } - if(!gotEndLine) - // Incomplete packet - return pos; - if(c 128) - { - byte nibble; - msg[len++] = '%'; - nibble = (ch & 0xF0) >> 4; - msg[len++] = (byte)((nibble > 9) ? (nibble - 10 + 'a') : (nibble + '0')); - nibble = ch & 0x0F; - msg[len++] = (byte)((nibble > 9) ? (nibble - 10 + 'a') : (nibble + '0')); - } - else - msg[len++] = ch; - } - msg[len] = '\0'; - } - - strcat(msg, " HTTP/1.1\r\nHost: "); - //strcat(msg, " HTTP/1.0\r\nHost: "); - strcat(msg, server); - strcat(msg, "\r\n"); - strcat(msg, "Accept-Charset: UTF-8\r\n"); - //strcat(msg, "Accept-Charset: ISO-8859-1\r\n"); - strcat(msg, "Connection: Keep-Alive\r\n"); - if(referer) - { - strcat(msg, "Referer: "); - strcat(msg, referer); - strcat(msg, "\r\n"); - } - strcat(msg, "\r\n"); - len = strlen(msg); - - //::PrintLn("Releasing connectionsMutex before GET for ", name, " ", (uint64)this, " in thread ", GetCurrentThreadID()); - connectionsMutex.Release(); - - // ::PrintLn("Sending GET for ", name, " ", (uint64)this, " in thread ", GetCurrentThreadID(), "\n"); - connection.Send(msg, len); - - while(this.connection && this.connection.connected && !done) - { - this.connection.Process(); - } - //::PrintLn("Got DONE for ", name, " ", (uint64)this, " in thread ", GetCurrentThreadID(), "\n"); - - if(this.connection) - { - if(name != location) - { - delete location; - location = CopyString(name); - } - if(status == 200 || (!status && totalSizeSet)) - { - if(askBody) - this.connection.OnReceive = HTTPConnection::Read_OnReceive; - - result = true; - connectionsMutex.Wait(); - } - else - { - if(askBody) - { - if(chunked) - { - //bool wait = false; - this.connection.OnReceive = HTTPConnection::Read_OnReceive; - while(!eof) - { - if(!this.connection) - eof = true; - else - { - // First time check if we already have bytes, second time wait for an event - this.connection.Process(); - //wait = true; - } - } - } - else if(totalSizeSet) - { - // Is it possible to have Content-Length set but not chunked? - done = false; - this.connection.OnReceive = HTTPConnection::Read_OnReceive; - while(this.connection && this.connection.connected && position + (bufferCount - bufferPos) < totalSize) - { - connection.Process(); - position += bufferCount - bufferPos; - bufferCount = 0; - bufferPos = 0; - } - } - } - - connectionsMutex.Wait(); - if(this.connection) - { - this.connection.OnReceive = null; - this.connection.file = null; - - if(close) - { - this.connection.Disconnect(0); - connection = null; - } - } - - status = 0; - delete this.connection; // This decrements the file's reference - this.relocation = null; - totalSize = 0; - totalSizeSet = false; - done = false; - eof = false; - position = 0; - bufferPos = 0; - bufferCount = 0; - chunked = false; - } - } - else - { - connectionsMutex.Wait(); - } - if(reuse && !status && connection && !connection.connected) - { - delete connection; - reuse = false; - goto tryagain; - } - else - delete connection; - } - connectionsMutex.Release(); + if(!curlInited) + { + curl_global_init(CURL_GLOBAL_ALL); + curlInited = true; + } + curl_handle = curl_easy_init(); + curl_easy_setopt(curl_handle, CURLOPT_ACCEPT_ENCODING, /*"br, */ "gzip, deflate"); + curl_easy_setopt(curl_handle, CURLOPT_URL, name); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writeMemoryCallback); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, this); + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + SetCurlEmbeddedCA(curl_handle); + res = curl_easy_perform(curl_handle); + if(res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed (%s): %s\n", curl_easy_strerror(res), name); + else + { + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpCode); + result = true; } return result; } +private: + ~HTTPFile() { - delete location; - delete contentType; - delete contentDisposition; - { - connectionsMutex.Wait(); - if(connection) - { - if(totalSizeSet && askedBody) - { - done = false; - this.connection.OnReceive = HTTPConnection::Read_OnReceive; - while(this.connection && this.connection.connected && position + (bufferCount - bufferPos) < totalSize) - { - connectionsMutex.Release(); - connection.Process(); - connectionsMutex.Wait(); - position += bufferCount - bufferPos; - bufferCount = 0; - bufferPos = 0; - } - position = 0; - } - - if(connection) - { - connection.file = null; - if(close) - connection.Disconnect(0); - delete connection; - } - } - connectionsMutex.Release(); - - if(chunked) - { - while(connection && connection.connected && connection.file) - { - connectionsMutex.Release(); - connection.Process(); - connectionsMutex.Wait(); - } - } - //::PrintLn("Done with ", (uint64)this); - } + delete memory; + curl_easy_cleanup(curl_handle); + // curl_global_cleanup(); } uintsize Read(byte * buffer, uintsize size, uintsize count) { - uintsize readSize = size * count; - uintsize read = 0; - bool wait = false; - Time lastTime = GetTime(); - - if(!askedBody) - { - askedBody = true; - if(!RetrieveHead(this.location, null, null, true)) - return 0; - } - - if(totalSizeSet && position >= totalSize) - eof = true; - while(!eof && read < readSize && !aborted) - { - uint64 numbytes = bufferCount - bufferPos; - numbytes = Min(numbytes, readSize - read); - if(totalSizeSet) - numbytes = Min(numbytes, totalSize - position); - - if(numbytes) - { - lastTime = GetTime(); - memcpy(buffer + read, this.buffer + bufferPos, numbytes); - bufferPos += numbytes; - position += numbytes; - read += numbytes; - - lastTime = GetTime(); - - if(bufferPos > HTTPFILE_BUFFERSIZE / 2) - { - // Shift bytes back to beginning of buffer - uint64 shift = bufferCount - bufferPos; - if(shift) - memmove(this.buffer, this.buffer + bufferPos, shift); - bufferCount -= bufferPos; - bufferPos = 0; - } - } - else - { - if(!connection || !connection.connected) - eof = true; - else - { - // First time check if we already have bytes, second time wait for an event - connection.Process(); - if(wait && bufferCount - bufferPos == 0 && GetTime() - lastTime > 5) - eof = true; - wait = true; - } - } - if(totalSizeSet && position >= totalSize) - eof = true; - } + int read = Max(0, Min(this.size - pos, count * size)); + memcpy(buffer, memory + pos, read); + pos += read; return read / size; } @@ -777,7 +125,7 @@ private: bool Getc(char * ch) { uintsize read = Read(ch, 1, 1); - return !eof && read != 0; + return read != 0; } bool Putc(char ch) @@ -792,19 +140,21 @@ private: bool Seek(int64 pos, FileSeekMode mode) { - if(mode == start && bufferPos == 0 && pos <= bufferCount && pos >= 0) + if(mode == start) // && bufferPos == 0 && pos <= bufferCount && pos >= 0) { - bufferPos = pos; + this.pos = Min(size, pos); return true; } - else if(mode == current && bufferPos == 0 && ((int)position + pos) <= bufferCount && ((int)position + pos) >= 0) + else if(mode == current) // && bufferPos == 0 && ((int)position + pos) <= bufferCount && ((int)position + pos) >= 0) { - bufferPos = position + pos; + this.pos += pos; + if(this.pos > size) this.pos = size; return true; } - else if(mode == end && totalSizeSet && bufferPos == 0 && bufferCount == totalSize && ((int)totalSize - pos) <= bufferCount && ((int)totalSize - pos) >= 0) + else if(mode == end) // && totalSizeSet && bufferPos == 0 && bufferCount == totalSize && ((int)totalSize - pos) <= bufferCount && ((int)totalSize - pos) >= 0) { - bufferPos = totalSize - pos; + this.pos = size - pos; + if(this.pos < 0) this.pos = 0; return true; } return false; @@ -812,48 +162,31 @@ private: uint64 Tell() { - return position; + return pos; } bool Eof() { - return eof; + return pos >= size; } uint64 GetSize() { - return totalSize; + return size; } void Abort() { - aborted = true; + //aborted = true; } private: - bool askedBody; - - HTTPConnection connection; - uint64 position; - bool done; - bool eof; - int status; - uint64 totalSize; - bool chunked; - bool close; - uint64 chunkSize; - char * relocation; - String location; - - // Buffering... - byte buffer[HTTPFILE_BUFFERSIZE]; - uint64 bufferPos; - uint64 bufferCount; - bool aborted; - bool totalSizeSet; - String contentType; String contentDisposition; + + char *memory; + intsize pos, size; + CURL *curl_handle; } public HTTPFile FileOpenURL(const char * name) @@ -868,4 +201,110 @@ public HTTPFile FileOpenURL(const char * name) } } +#define _Noreturn + +#ifndef ECERE_NOSSL + +#if defined(__WIN32__) + +#define byte _byte +#define int64 _int64 +#define uint _uint +#define set _set +#include +#if defined(__WIN32__) && OPENSSL_VERSION_NUMBER < 0x1010006fL +#include +#endif +#undef byte +#undef int64 +#undef uint +#undef set + +static CURLcode sslctx_function(CURL *curl, void *sslctx, void *certdata) +{ + BIO * bio = BIO_new_mem_buf((char *)certdata, -1); + if(!bio) + PrintLn("Error: BIO_new_mem_buf()"); + else + { + X509_STORE * store = SSL_CTX_get_cert_store((SSL_CTX *)sslctx); + STACK_OF(X509_INFO) * certStack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); + if(!certStack) + PrintLn("Error: PEM_X509_INFO_read_bio()"); + else + { + int i; + + for(i = 0; i < sk_X509_INFO_num(certStack); i++) + { + X509_INFO *itmp = sk_X509_INFO_value(certStack, i); + if(itmp->x509) + if(!X509_STORE_add_cert(store, itmp->x509)) + PrintLn("Error: X509_STORE_add_cert()"); + if(itmp->crl) + X509_STORE_add_crl(store, itmp->crl); + } + sk_X509_INFO_pop_free(certStack, X509_INFO_free); + } + } + BIO_free(bio); + return CURLE_OK; +} + +static char * sslCACert; + +#endif + +public bool SetCurlEmbeddedCA(void * curlHandle) +{ + bool result = false; + // Things work out of the box on Linux... +#if defined(__WIN32__) + if(!sslCACert) + { + const String fn = "<:ecere>mozilla-cacert.pem"; + if(FileExists(fn)) + { + File f = FileOpen(fn, read); + if(f) + { + int len = (int)f.GetSize(); + sslCACert = new char[len+1]; + + f.Read(sslCACert, 1, len); + sslCACert[len] = 0; + delete f; + } + } + } + if(sslCACert) + { + curl_easy_setopt(curlHandle, CURLOPT_CAINFO, null); + curl_easy_setopt(curlHandle, CURLOPT_CAPATH, null); + curl_easy_setopt(curlHandle, CURLOPT_SSLCERTTYPE, "PEM"); + curl_easy_setopt(curlHandle, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curlHandle, CURLOPT_SSL_CTX_FUNCTION, sslctx_function); + curl_easy_setopt(curlHandle, CURLOPT_SSL_CTX_DATA, sslCACert); + + result = true; + } +#endif + return result; +} +#endif + +#else + +namespace net; + +public HTTPFile FileOpenURL(const char * name) +{ + return null; +} + +public bool SetCurlEmbeddedCA(void * curlHandle) +{ + return false; +} + #endif diff --git a/ecere/src/net/NetworkClientFile.ec b/ecere/src/net/NetworkClientFile.ec index 0ca1f8c8b0..77d9a56bfd 100644 --- a/ecere/src/net/NetworkClientFile.ec +++ b/ecere/src/net/NetworkClientFile.ec @@ -1,7 +1,45 @@ +#define _Noreturn + namespace net; +// NOTE: These definitions are currently necessary for defining FileServerConnection class +// inheriting from Socket. Because of lack of "static" members SOCKADDR_IN a; gets +// re-declared here. #ifndef ECERE_NONET +#if defined(__WIN32__) + +#define WIN32_LEAN_AND_MEAN +#define String _String +#include +#undef String +#define SOCKLEN_TYPE int + +#elif defined(__unix__) || defined(__APPLE__) + +default: +#define SOCKLEN_TYPE socklen_t +#define set _set +#define uint _uint +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#undef set +#undef uint +private: + +typedef struct sockaddr_in SOCKADDR_IN; + +#endif + + import "network" #define PUTXDWORD(b, d) \ diff --git a/ecere/src/net/SSLSocket.ec b/ecere/src/net/SSLSocket.ec index 3562ef365e..1697e957be 100644 --- a/ecere/src/net/SSLSocket.ec +++ b/ecere/src/net/SSLSocket.ec @@ -1,3 +1,5 @@ +#define _Noreturn + #undef __BLOCKS__ #ifndef ECERE_NOSSL #define byte _byte @@ -5,7 +7,7 @@ #define uint _uint #define set _set #include -#if defined(__WIN32__) +#if defined(__WIN32__) && OPENSSL_VERSION_NUMBER < 0x1010006fL #include #endif #undef byte diff --git a/ecere/src/net/Service.ec b/ecere/src/net/Service.ec index 2728439866..ef276d0548 100644 --- a/ecere/src/net/Service.ec +++ b/ecere/src/net/Service.ec @@ -13,7 +13,7 @@ namespace net; #define SOCKLEN_TYPE int #define WIN32_LEAN_AND_MEAN #define String _String -#include +#include #undef String #elif defined(__unix__) || defined(__APPLE__) @@ -70,6 +70,7 @@ public: property int port { set { port = value; } get { return port; } }; property Socket firstClient { get { return sockets.first; } }; property bool processAlone { get { return processAlone; } set { processAlone = value; } }; + property bool started { get { return s != -1; } } virtual void OnAccept(); @@ -127,9 +128,15 @@ public: network.mutex.Release(); return true; } + else + PrintLn("Service: Start() could not listen on socket!"); } + else + PrintLn("Service: Start() could not bind socket!"); closesocket(s); } + else + PrintLn("Service: Start() could not allocate socket!"); #endif return false; } @@ -170,13 +177,18 @@ public: } bool Process() + { + return ProcessTimeOut(0.2); // 200 ms is a long time! + } + + bool ProcessTimeOut(Seconds timeOut) { bool gotEvent = false; if(s != -1) { fd_set rs, ws, es; int selectResult; - struct timeval tvTO = {0, 200000}; + struct timeval tvTO = {0, (int)(timeOut * 1000000) }; FD_ZERO(&rs); FD_ZERO(&ws); diff --git a/ecere/src/net/Socket.ec b/ecere/src/net/Socket.ec index b462f7a0df..9034644f3e 100644 --- a/ecere/src/net/Socket.ec +++ b/ecere/src/net/Socket.ec @@ -8,7 +8,7 @@ namespace net; #define WIN32_LEAN_AND_MEAN #define String _String -#include +#include #undef String #define SOCKLEN_TYPE int @@ -144,6 +144,8 @@ static class SocketConnectThread : Thread }; + #define MAX_RECEIVE (2 * 65536) + public class Socket { public: @@ -164,7 +166,9 @@ public: int sendsize=65536; int recvsize=65536; + network.mutex.Wait(); value.sockets.Add(this); + network.mutex.Release(); incref this; setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&sendsize, (int)sizeof(sendsize)); @@ -642,7 +646,7 @@ private: return result; } - #define MAX_RECEIVE 65536 + bool hadLeftOver; bool ProcessSocket(fd_set * rs, fd_set * ws, fd_set * es) { @@ -659,38 +663,44 @@ private: int count = 0; result = true; - if((int)recvBufferSize - recvBytes < MAX_RECEIVE) + if(!leftOver || hadLeftOver) { - recvBuffer = renew recvBuffer byte[recvBufferSize + MAX_RECEIVE]; - recvBufferSize += MAX_RECEIVE; - } - - if(FD_ISSET(s, rs) && disconnectCode == (DisconnectCode)-1) - { - if(type == tcp /*|| _connected*/) - count = ReceiveData(recvBuffer + recvBytes, recvBufferSize - recvBytes, 0); - else + hadLeftOver = false; + if((int)recvBufferSize - recvBytes < MAX_RECEIVE) { - SOCKLEN_TYPE len = sizeof(a); - count = (int)recvfrom(s, (char *)recvBuffer + recvBytes, - recvBufferSize - recvBytes, 0, (SOCKADDR *)&a, &len); - strcpy(inetAddress, inet_ntoa(this.a.sin_addr)); - inetPort = ntohs((uint16)a.sin_port); + recvBuffer = renew recvBuffer byte[recvBufferSize + MAX_RECEIVE]; + recvBufferSize += MAX_RECEIVE; } - switch(count) + + if(FD_ISSET(s, rs) && disconnectCode == (DisconnectCode)-1) { - case 0: - disconnectCode = remoteClosed; - break; - case -1: + if(type == tcp /*|| _connected*/) + count = ReceiveData(recvBuffer + recvBytes, recvBufferSize - recvBytes, 0); + else { - /*int yo = errno; - printf("Errno is %d", errno);*/ - disconnectCode = remoteLost; - break; + SOCKLEN_TYPE len = sizeof(a); + count = (int)recvfrom(s, (char *)recvBuffer + recvBytes, + recvBufferSize - recvBytes, 0, (SOCKADDR *)&a, &len); + strcpy(inetAddress, inet_ntoa(this.a.sin_addr)); + inetPort = ntohs((uint16)a.sin_port); + } + switch(count) + { + case 0: + disconnectCode = remoteClosed; + break; + case -1: + { + /*int yo = errno; + printf("Errno is %d", errno);*/ + disconnectCode = remoteLost; + break; + } } } } + else + hadLeftOver = true; if(count > 0 || (leftOver && !count)) { @@ -771,8 +781,27 @@ private: fd_set rs, ws, es; int selectResult; Mutex mutex; + bool deleteMutex = false; + bool leftOver; - if(disconnectCode > 0 && !leftOver) return false; + mutex = this.mutex; + mutex.Wait(); + incref this; + + leftOver = this.leftOver; + if(disconnectCode > 0 && !leftOver) + { + if(_refCount == 1) + { + deleteMutex = true; + this.mutex = null; + } + delete this; + mutex.Release(); + if(deleteMutex) + delete mutex; + return false; + } FD_ZERO(&rs); FD_ZERO(&ws); FD_ZERO(&es); @@ -780,9 +809,6 @@ private: //FD_SET(s, &ws); FD_SET(s, &es); - mutex = this.mutex; - mutex.Wait(); - incref this; mutex.Release(); selectResult = select((int)(s+1), &rs, &ws, &es, leftOver ? &tv : (timeOut ? &tvTO : null)); @@ -791,8 +817,15 @@ private: gotEvent |= ProcessSocket(&rs, &ws, &es); } mutex.Wait(); + if(_refCount == 1) + { + deleteMutex = true; + this.mutex = null; + } delete this; mutex.Release(); + if(deleteMutex) + delete mutex; return gotEvent; } diff --git a/ecere/src/net/dcom.ec b/ecere/src/net/dcom.ec index 4bd3d9b069..a82c743b41 100644 --- a/ecere/src/net/dcom.ec +++ b/ecere/src/net/dcom.ec @@ -6,7 +6,7 @@ namespace net; #define WIN32_LEAN_AND_MEAN #define String _String -#include +#include #undef String #elif defined(__unix__) || defined(__APPLE__) @@ -74,6 +74,7 @@ static class CallMethodPacket : DCOMPacket int objectID; int methodID; int callID; + bool hasReturnValue; unsigned int argsSize; byte args[1]; }; @@ -200,7 +201,7 @@ public: incref this; - while(true) + while(true && hasReturnValue) { if(!serverSocket || !serverSocket.connected || !serverSocket.thread) break; @@ -223,6 +224,8 @@ public: returnBuffer.WriteData(ack.buffer.buffer, ack.buffer.count); delete ack; } + else if(!hasReturnValue) + result = true; lockCount = guiApp.UnlockEx(); mutex.Release(); @@ -248,7 +251,7 @@ public: unsigned int id; SerialBuffer argsBuffer { }; SerialBuffer returnBuffer { }; - List acks { }; + private List acks { }; Mutex mutex { }; int nextCallID; @@ -288,6 +291,7 @@ class DCOMServerThread : Thread unsigned int Main() { + incref socket; incref socket; while(connected) { @@ -297,6 +301,8 @@ class DCOMServerThread : Thread guiApp.Unlock(); semaphore.Release(); } + // TOCHECK: Why isn't the first incref enough? + socket._refCount--; delete socket; return 0; } @@ -359,7 +365,7 @@ class DCOMClientThread : Thread if(callMethod.objectID < numObjects /*&& callMethod.methodID < numMethods*/) { DCOMServerObject object = objects[callMethod.objectID]; - bool hasReturnValue = true; + bool hasReturnValue = callMethod.hasReturnValue; //true; MethodReturnedPacket packet; unsigned int size; SerialBuffer buffer { }; @@ -370,6 +376,7 @@ class DCOMClientThread : Thread if(!hasReturnValue) { + /* size = (uint)(uintptr)&((MethodReturnedPacket)0).args; packet = (MethodReturnedPacket)new0 byte[size]; packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_MethodReturned); @@ -378,6 +385,7 @@ class DCOMClientThread : Thread packet.methodID = callMethod.methodID; packet.argsSize = 0; SendPacket(packet); + */ } incref object; @@ -602,7 +610,7 @@ public: unsigned int objectID; bool answered; SerialBuffer __ecereBuffer { }; - List acks { }; + private List acks { }; int nextCallID; nextCallID = GetRandom(1, 999999); @@ -746,7 +754,7 @@ public: answered = true; //2; } - dllexport bool CallMethod(unsigned int methodID) + dllexport bool CallMethod(unsigned int methodID, bool hasReturnValue) { bool result = false; if(this && connected) @@ -757,6 +765,7 @@ public: CallMethodPacket packet = (CallMethodPacket)new0 byte[size]; packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_CallMethod); packet.size = size; + packet.hasReturnValue = hasReturnValue; packet.objectID = htoled(objectID); packet.methodID = htoled(methodID); packet.callID = callID; @@ -765,7 +774,7 @@ public: SendPacket(packet); delete packet; - while(true) + while(hasReturnValue && true) { int lockCount; if(!thread || !connected) @@ -786,10 +795,11 @@ public: guiApp.LockEx(lockCount); } - if(ack) + //if(ack) { __ecereBuffer.Free(); - __ecereBuffer.WriteData(ack.buffer.buffer, ack.buffer.count); + if(ack) + __ecereBuffer.WriteData(ack.buffer.buffer, ack.buffer.count); delete ack; result = true; } diff --git a/ecere/src/net/network.ec b/ecere/src/net/network.ec index 36b37f4361..1daaa10e0e 100644 --- a/ecere/src/net/network.ec +++ b/ecere/src/net/network.ec @@ -8,7 +8,7 @@ namespace net; #define WIN32_LEAN_AND_MEAN #define String _String -#include +#include #undef String static WSADATA wsaData; diff --git a/ecere/src/sys/Archive.ec b/ecere/src/sys/Archive.ec index 8bf396691f..869f01beaa 100644 --- a/ecere/src/sys/Archive.ec +++ b/ecere/src/sys/Archive.ec @@ -22,6 +22,7 @@ public: virtual ArchiveDir OpenDirectory(const char * name, FileStats stats, ArchiveAddMode addMode); virtual bool Clear(void); + virtual File FileOpenCompressed(const char * fileName, bool * isCompressed, uint64 * ucSize); virtual File FileOpen(const char * fileName); virtual FileAttribs FileExists(const char * fileName); virtual File FileOpenAtPosition(uint position); diff --git a/ecere/src/sys/Date.ec b/ecere/src/sys/Date.ec index 0049f8a898..a797ab5fff 100644 --- a/ecere/src/sys/Date.ec +++ b/ecere/src/sys/Date.ec @@ -50,15 +50,15 @@ public enum Month { january, february, march, april, may, june, july, august, september, october, november, december; - const char * OnGetString(char * string, void * fieldData, bool * needClass) + const char * OnGetString(char * string, void * fieldData, ObjectNotationType * onType) { Month m = this; if(m >= january && m <= december) { - if(!needClass || !*needClass) + if(!onType || !*onType) return longMonthsNames[m]; else - return class::OnGetString(string, fieldData, needClass); + return class::OnGetString(string, fieldData, onType); } return null; } @@ -106,20 +106,27 @@ public struct Date return 0; } - const char * OnGetString(char * stringOutput, void * fieldData, bool * needClass) + const char * OnGetString(char * stringOutput, void * fieldData, ObjectNotationType * onType) { if(stringOutput) { + const String quotes = onType && (*onType == json || *onType == econ) ? "\"" : null; if(day && year) - sprintf(stringOutput, "%s, %s %2d, %d", - longDaysNames[dayOfTheWeek], longMonthsNames[month], day, year); + sprintf(stringOutput, "%s%s, %s %2d, %d%s", + quotes ? quotes : "", longDaysNames[dayOfTheWeek], longMonthsNames[month], day, year, quotes ? quotes : ""); + else if(quotes) + { + stringOutput[0] = quotes[0]; + stringOutput[1] = quotes[0]; + stringOutput[2] = 0; + } else stringOutput[0] = 0; } return stringOutput; } - const char * OnGetStringEn(char * stringOutput, void * fieldData, bool * needClass) + const char * OnGetStringEn(char * stringOutput, void * fieldData, ObjectNotationType * onType) { if(stringOutput) { @@ -289,22 +296,31 @@ public struct Date } else if(count >= 2 && !gotAlphaMonth) { - // No Year Specified - year = time.year; - if(numerics[0] >= 1 && numerics[0] <= 12 && - numerics[1] >= 1 && numerics[1] <= 31) - { - month = (Month)(numerics[0] - 1); - day = numerics[1]; - } - else if(numerics[0] >= 1 && numerics[0] <= 31 && - numerics[1] >= 1 && numerics[1] <= 12) + if(numerics[0] > 100 && numerics[1] <= 12) { - day = numerics[0]; + year = numerics[0]; month = (Month)(numerics[1] - 1); + day = 0; } else - return false; + { + // No Year Specified + year = time.year; + if(numerics[0] >= 1 && numerics[0] <= 12 && + numerics[1] >= 1 && numerics[1] <= 31) + { + month = (Month)(numerics[0] - 1); + day = numerics[1]; + } + else if(numerics[0] >= 1 && numerics[0] <= 31 && + numerics[1] >= 1 && numerics[1] <= 12) + { + day = numerics[0]; + month = (Month)(numerics[1] - 1); + } + else + return false; + } } else if(count >= 1 && gotAlphaMonth) { @@ -353,20 +369,23 @@ public struct Date Window OnEdit(DataBox dataBox, Window obsolete, int x, int y, int w, int h, void * fieldData) { const char * string = ""; - DateDropBox comboBox - { - dataBox, - editText = true; - anchor = { 0, 0, 0, 0 }; - borderStyle = 0; - hotKey = f2; - }; + DateDropBox comboBox = (DateDropBox)dataBox.editor; + + if(!comboBox || comboBox._class != class(DateDropBox)) + comboBox = + { + dataBox, + editText = true; + anchor = { 0, 0, 0, 0 }; + borderStyle = 0; + hotKey = f2; + }; if(day || year || month) { char tempString[MAX_F_STRING] = ""; - bool needClass = false; - const char * result = OnGetString(tempString, null, &needClass); + ObjectNotationType onType = none; + const char * result = OnGetString(tempString, null, &onType); if(result) string = result; comboBox.calendar.dateValue = this; @@ -514,9 +533,9 @@ class DateDropBox : DropBox } { char tempString[1024] = ""; - bool needClass = false; - // char * string = date.OnGetString(tempString, null, &needClass); - const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &date, tempString, null, &needClass); + ObjectNotationType onType = none; + // char * string = date.OnGetString(tempString, null, &onType); + const char * string = ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &date, tempString, null, &onType); dropBox.contents = string; } return true; diff --git a/ecere/src/sys/DualPipe.c b/ecere/src/sys/DualPipe.c index 235423833a..6a3742e062 100644 --- a/ecere/src/sys/DualPipe.c +++ b/ecere/src/sys/DualPipe.c @@ -408,7 +408,7 @@ _DualPipe * _DualPipeOpen(PipeOpenMode mode, const char * commandLine, const cha close(hOutput[PIPE_READ]); } } -#else +#elif !defined(__UWP__) { HANDLE hOutput[2] = { 0 },hOutputRead = 0; HANDLE hInput[2] = { 0 }, hInputWrite = 0; diff --git a/ecere/src/sys/EARArchive.ec b/ecere/src/sys/EARArchive.ec index 6d32db41d7..8dff3d1730 100644 --- a/ecere/src/sys/EARArchive.ec +++ b/ecere/src/sys/EARArchive.ec @@ -46,7 +46,8 @@ static File EAROpenArchive(const char * archive, EARHeader header) { char moduleName[MAX_LOCATION]; const char * name = archive + 1; -#if defined(__ANDROID__) + moduleName[0] = 0; +#if defined(__ANDROID__) && !defined(__LUMIN__) if(!name[0]) name = ((SubModule)__thisModule.application.modules.first).next.module.name; #endif @@ -116,7 +117,7 @@ static FileAttribs EARGetEntry(File f, EAREntry entry, const char * name, char * if(path) PathCat(path, fileName); if(nameRest[0]) - return EARGetEntry(f, entry, nameRest, path); + return (entry.type == ENTRY_FOLDER) ? EARGetEntry(f, entry, nameRest, path) : 0; else return (entry.type == ENTRY_FILE) ? FileAttribs { isFile = true } : FileAttribs { isDirectory = true }; } @@ -516,6 +517,33 @@ class EARArchive : Archive // bf.handle = f; } + File FileOpenCompressed(const char * name, bool * isCompressed, uint64 * ucSize) + { + File result = null; + EARFile file {}; + if(file) + { + EAREntry entry { }; + + f.Seek(archiveStart + sizeof(EARHeader), start); + if(EARGetEntry(f, entry, name, null).isFile) + { + file.start = f.Tell(); + file.position = 0; + file.size = entry.cSize ? entry.cSize : entry.size; + if(ucSize) *ucSize = entry.size; + file.f = f; + incref file.f; + file.f.Seek(file.start, start); + result = file; + if(isCompressed) *isCompressed = entry.cSize != 0; + } + if(!result) + delete file; + } + return result; + } + File FileOpen(const char * name) { File result = null; diff --git a/ecere/src/sys/File.c b/ecere/src/sys/File.c index 10556469eb..24506584eb 100644 --- a/ecere/src/sys/File.c +++ b/ecere/src/sys/File.c @@ -12,7 +12,136 @@ #include #include #include + +#define __USE_LARGEFILE64 #include + +#if defined(__linux__) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__i386__) + +// From https://stackoverflow.com/questions/58472958/how-to-force-linkage-to-older-libc-fcntl-instead-of-fcntl64 + +asm(".symver fcntl64,fcntl@GLIBC_2.2.5"); +asm(".symver __xstat,__xstat@GLIBC_2.2.5"); +asm(".symver __fxstat,__fxstat@GLIBC_2.2.5"); + +int __xstat (int __ver, const char *__filename, struct stat *__stat_buf); +int __fxstat (int __ver, int fd, struct stat *__stat_buf); +#define stat(a, b) stat_glibcwrapper(a, b) +#define fstat(a, b) fstat_glibcwrapper(a, b) +__attribute__((visibility("default"))) int stat_glibcwrapper(const char *fn, struct stat * buf) +{ // 1 for 64 bit, 3 for 32 bit + return __xstat(1, fn, buf); +} + +__attribute__((visibility("default"))) int fstat_glibcwrapper(int fd, struct stat * buf) +{ // 1 for 64 bit, 3 for 32 bit + return __fxstat(1, fd, buf); +} + +__attribute__((visibility("default"))) int __wrap_fcntl64(int fd, int cmd, ...) +{ + int result; + va_list va; + va_start(va, cmd); + + switch (cmd) { + // + // File descriptor flags + // + case F_GETFD: goto takes_void; + case F_SETFD: goto takes_int; + + // File status flags + // + case F_GETFL: goto takes_void; + case F_SETFL: goto takes_int; + + // File byte range locking, not held across fork() or clone() + // + case F_SETLK: goto takes_flock_ptr_INCOMPATIBLE; + case F_SETLKW: goto takes_flock_ptr_INCOMPATIBLE; + case F_GETLK: goto takes_flock_ptr_INCOMPATIBLE; +#if 0 + // File byte range locking, held across fork()/clone() -- Not POSIX + // + case F_OFD_SETLK: goto takes_flock_ptr_INCOMPATIBLE; + case F_OFD_SETLKW: goto takes_flock_ptr_INCOMPATIBLE; + case F_OFD_GETLK: goto takes_flock_ptr_INCOMPATIBLE; + + // Managing I/O availability signals + // + case F_GETOWN: goto takes_void; + case F_SETOWN: goto takes_int; + case F_GETOWN_EX: goto takes_f_owner_ex_ptr; + case F_SETOWN_EX: goto takes_f_owner_ex_ptr; + case F_GETSIG: goto takes_void; + case F_SETSIG: goto takes_int; + + // Notified when process tries to open or truncate file (Linux 2.4+) + // + case F_SETLEASE: goto takes_int; + case F_GETLEASE: goto takes_void; + + // File and directory change notification + // + case F_NOTIFY: goto takes_int; + + // Changing pipe capacity (Linux 2.6.35+) + // + case F_SETPIPE_SZ: goto takes_int; + case F_GETPIPE_SZ: goto takes_void; + + // File sealing (Linux 3.17+) + // + case F_ADD_SEALS: goto takes_int; + case F_GET_SEALS: goto takes_void; + + // File read/write hints (Linux 4.13+) + // + case F_GET_RW_HINT: goto takes_uint64_t_ptr; + case F_SET_RW_HINT: goto takes_uint64_t_ptr; + case F_GET_FILE_RW_HINT: goto takes_uint64_t_ptr; + case F_SET_FILE_RW_HINT: goto takes_uint64_t_ptr; +#endif + + default: + fprintf(stderr, "fcntl64 workaround got unknown F_XXX constant"); + } + + takes_void: + va_end(va); + return fcntl64(fd, cmd); + + takes_int: + result = fcntl64(fd, cmd, va_arg(va, int)); + va_end(va); + return result; + + takes_flock_ptr_INCOMPATIBLE: + // + // !!! This is the breaking case: the size of the flock + // structure changed to accommodate larger files. If you + // need this, you'll have to define a compatibility struct + // with the older glibc and make your own entry point using it, + // then call fcntl64() with it directly (bear in mind that has + // been remapped to the old fcntl()) + // + fprintf(stderr, "fcntl64 hack can't use glibc flock directly"); + exit(1); +#if 0 + takes_f_owner_ex_ptr: + result = fcntl64(fd, cmd, va_arg(va, struct f_owner_ex*)); + va_end(va); + return result; + + takes_uint64_t_ptr: + result = fcntl64(fd, cmd, va_arg(va, uint64_t*)); + va_end(va); + return result; +#endif +} +#endif + #endif #if defined(__unix__) || defined(__APPLE__) @@ -111,6 +240,7 @@ bool __ecereNameSpace__ecere__sys__SplitArchivePath(const char * fileName, char #if defined(__WIN32__) && !defined(ECERE_BOOTSTRAP) void __ecereMethod___ecereNameSpace__ecere__sys__EARFileSystem_FixCase(const char * archive, char * name); +#if !defined(__UWP__) static BOOL CALLBACK EnumThreadWindowsProc(HWND hwnd, LPARAM lParam) { DWORD pid; @@ -121,6 +251,7 @@ static BOOL CALLBACK EnumThreadWindowsProc(HWND hwnd, LPARAM lParam) } return TRUE; } + bool WinReviveNetworkResource(uint16 * _wfileName) { bool result = false; @@ -157,6 +288,7 @@ bool WinReviveNetworkResource(uint16 * _wfileName) result = true; return result; } +#endif TimeStamp Win32FileTimeToTimeStamp(FILETIME * fileTime); void TimeStampToWin32FileTime(TimeStamp t, FILETIME * fileTime); @@ -235,7 +367,7 @@ FileAttribs FILE_FileExists(const char * fileName) } else attribute = GetFileAttributes(_wfileName); -#if !defined(ECERE_BOOTSTRAP) +#if !defined(ECERE_BOOTSTRAP) && !defined(__UWP__) if(!result && attribute == 0xFFFFFFFF) { if(WinReviveNetworkResource(_wfileName)) @@ -335,7 +467,7 @@ bool FILE_FileGetStats(const char * fileName, FileStats * stats) stats->size = s.st_size; stats->attribs = (s.st_mode & S_IFDIR) ? ((FileAttribs) (isDirectory)): ((FileAttribs) 0); -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__UWP__) { HANDLE hFile = CreateFile(_wfileName, 0, FILE_SHARE_READ, null, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, null); @@ -400,7 +532,7 @@ void FILE_FileFixCase(char * file) char parent[MAX_LOCATION] = ""; // Skip network protocols - if(strstr(file, "http://") == file) return; + if(strstr(file, "http://") == file || strstr(file, "https://") == file || strstr(file, "wfs://") == file || strstr(file, "wfs3://") == file || strstr(file, "wfs3s://") == file) return; // Copy drive letter to new path if(file[0] && file[1] == ':') @@ -484,7 +616,7 @@ void FILE_FileFixCase(char * file) strcat(parent, directory); } } -#ifndef ECERE_BOOTSTRAP +#if !defined(ECERE_BOOTSTRAP) && !defined(__UWP__) else { // Network server @@ -562,27 +694,29 @@ void FILE_FileOpen(const char * fileName, FileOpenMode mode, FILE ** input, FILE { #if defined(__WIN32__) && !defined(ECERE_BOOTSTRAP) uint16 * _wfileName = __ecereNameSpace__ecere__sys__UTF8toUTF16(fileName, null); + /* + struct WinFile { HANDLE handle; } file; file.handle = CreateFile(_wfileName, ((mode == FOM_read || mode == FOM_readWrite || mode == FOM_writeRead || mode == FOM_appendRead) ? GENERIC_READ : 0) | ((mode == FOM_write || mode == FOM_append || mode == FOM_readWrite || mode == FOM_writeRead || mode == FOM_appendRead) ? GENERIC_WRITE: 0), FILE_SHARE_READ|FILE_SHARE_WRITE, null, - (mode == write || mode == writeRead) ? TRUNCATE_EXISTING : ((mode == read || mode == readWrite) ? OPEN_EXISTING : OPEN_ALWAYS), 0, null); + (mode == FOM_write || mode == FOM_writeRead) ? TRUNCATE_EXISTING : ((mode == FOM_read || mode == FOM_readWrite) ? OPEN_EXISTING : OPEN_ALWAYS), 0, null); if(file.handle) { int flags; - int handle; + int handle = -1; switch(mode) { - case FOM_read: handle = _open_osfhandle((int)file.handle, _O_RDONLY); break; - case FOM_write: handle = _open_osfhandle((int)file.handle, _O_WRONLY | _O_CREAT | _O_TRUNC); break; - case FOM_append: handle = _open_osfhandle((int)file.handle, _O_WRONLY | _O_CREAT | _O_APPEND); break; - case FOM_readWrite: handle = _open_osfhandle((int)file.handle, _O_RDWR); break; - case FOM_writeRead: handle = _open_osfhandle((int)file.handle, _O_RDWR | _O_CREAT | _O_TRUNC); break; - case FOM_appendRead: handle = _open_osfhandle((int)file.handle, _O_RDWR | _O_APPEND | _O_CREAT); break; + case FOM_read: handle = _open_osfhandle((intptr_t)file.handle, _O_RDONLY); break; + case FOM_write: handle = _open_osfhandle((intptr_t)file.handle, _O_WRONLY | _O_CREAT | _O_TRUNC); break; + case FOM_append: handle = _open_osfhandle((intptr_t)file.handle, _O_WRONLY | _O_CREAT | _O_APPEND); break; + case FOM_readWrite: handle = _open_osfhandle((intptr_t)file.handle, _O_RDWR); break; + case FOM_writeRead: handle = _open_osfhandle((intptr_t)file.handle, _O_RDWR | _O_CREAT | _O_TRUNC); break; + case FOM_appendRead: handle = _open_osfhandle((intptr_t)file.handle, _O_RDWR | _O_APPEND | _O_CREAT); break; } - if(handle) + if(handle != -1) { switch(mode) { @@ -605,6 +739,7 @@ void FILE_FileOpen(const char * fileName, FileOpenMode mode, FILE ** input, FILE case FOM_writeRead: *input = *output = _wfopen(_wfileName, L"w+b"); break; case FOM_appendRead: *input = *output = _wfopen(_wfileName, L"a+b"); break; } + #if !defined(__UWP__) if(!mode && WinReviveNetworkResource(_wfileName)) { switch(mode) @@ -617,6 +752,7 @@ void FILE_FileOpen(const char * fileName, FileOpenMode mode, FILE ** input, FILE case FOM_appendRead: *input = *output = _wfopen(_wfileName, L"a+b"); break; } } +#endif __ecereNameSpace__ecere__com__eSystem_Delete(_wfileName); #else switch(mode) diff --git a/ecere/src/sys/File.ec b/ecere/src/sys/File.ec index 4010b42c0b..0fa01468df 100644 --- a/ecere/src/sys/File.ec +++ b/ecere/src/sys/File.ec @@ -41,6 +41,15 @@ default: #include #endif +#if defined(__linux__) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__i386__) + +asm(".symver __xstat,__xstat@GLIBC_2.2.5"); + +int stat_glibcwrapper(const char *fn, struct stat * buf); +#define stat(a, b) stat_glibcwrapper(a, b) + +#endif + #if defined(__WIN32__) #define WIN32_LEAN_AND_MEAN #define String String_ @@ -133,7 +142,7 @@ public class FileSize : uint return result; } - const char * OnGetString(char * string, void * fieldData, bool * needClass) + const char * OnGetString(char * string, void * fieldData, ObjectNotationType * onType) { PrintSize(string, this, 2); return string; @@ -168,7 +177,7 @@ public class FileSize64 : uint64 return result; } - const char * OnGetString(char * string, void * fieldData, bool * needClass) + const char * OnGetString(char * string, void * fieldData, ObjectNotationType * onType) { PrintBigSize(string, this, 2); return string; @@ -280,7 +289,7 @@ public class File : IOChannel return false; } - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { if(this) { @@ -406,8 +415,8 @@ public class File : IOChannel void OnUnserialize(IOChannel channel) { - uLongf size; - uint cSize; + uLongf size = 0; + uint cSize = 0; this = null; @@ -596,24 +605,26 @@ public: return result; } - public void PrintLn(typed_object object, ...) + public void PrintLn(const typed_object object, ...) { va_list args; char buffer[4096]; + int len; va_start(args, object); - PrintStdArgsToBuffer(buffer, sizeof(buffer), object, args); - Puts(buffer); + len = PrintStdArgsToBuffer(buffer, sizeof(buffer), object, args); + WriteData(buffer, len); Putc('\n'); va_end(args); } - public void Print(typed_object object, ...) + public void Print(const typed_object object, ...) { va_list args; char buffer[4096]; + int len; va_start(args, object); - PrintStdArgsToBuffer(buffer, sizeof(buffer), object, args); - Puts(buffer); + len = PrintStdArgsToBuffer(buffer, sizeof(buffer), object, args); + WriteData(buffer, len); va_end(args); } @@ -766,6 +777,30 @@ public: return c; } + bool CopyToFile(File f) + { + bool result = false; + if(f) + { + byte buffer[65536]; + + result = true; + Seek(0, start); + while(!Eof()) + { + uintsize count = Read(buffer, 1, sizeof(buffer)); + if(count && !f.Write(buffer, 1, count)) + { + result = false; + break; + } + if(!count) break; + } + } + Seek(0, start); + return result; + } + bool CopyTo(const char * outputFileName) { bool result = false; @@ -784,6 +819,7 @@ public: result = false; break; } + if(!count) break; } delete f; } @@ -953,7 +989,7 @@ public FileAttribs FileExists(const char * fileName) { return EARFileSystem::Exists(archiveName, archiveFile); } - else if(strstr(fileName, "http://") == fileName) + else if(strstr(fileName, "http://") == fileName || strstr(fileName, "https://") == fileName || strstr(fileName, "wfs://") == fileName || strstr(fileName, "wfs3://") == fileName || strstr(fileName, "wfs3s://") == fileName) { return FileAttribs { isFile = true }; } @@ -976,10 +1012,14 @@ public File FileOpen(const char * fileName, FileOpenMode mode) { result = EARFileSystem::Open(archiveName, archiveFile, mode); } -#if !defined(ECERE_VANILLA) && !defined(ECERE_NONET) +#if !defined(ECERE_VANILLA) && !defined(ECERE_NONET) && !defined(ECERE_NOCURL) /*&& !defined(__ANDROID__)*/ // Curl version not supported yet else if(strstr(fileName, "http://") == fileName || strstr(fileName, "https://")) { - result = FileOpenURL(fileName); + HTTPFile r = FileOpenURL(fileName); + if(r && r.httpCode != 200) + delete r; + else + result = r; } #endif else @@ -1124,7 +1164,7 @@ public bool FileSetTime(const char * fileName, TimeStamp created, TimeStamp acce if(!modified) modified = currentTime; if(fileName) { -#ifdef __WIN32__ +#if defined(__WIN32__) && !defined(__UWP__) uint16 * _wfileName = UTF8toUTF16(fileName, null); HANDLE hFile = CreateFile(_wfileName, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, null, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, null); @@ -1152,7 +1192,7 @@ public bool FileSetTime(const char * fileName, TimeStamp created, TimeStamp acce CloseHandle(hFile); } -#else +#elif !defined(__UWP__) struct utimbuf t = { (int)accessed, (int)modified }; if(!utime(fileName, &t)) result = true; @@ -1170,6 +1210,7 @@ private class Dir : struct #if defined(__WIN32__) HANDLE fHandle; +#if !defined(__UWP__) int resource; NETRESOURCE * resources; int numResources; @@ -1177,6 +1218,8 @@ private class Dir : struct int workGroup; NETRESOURCE * workGroups; int numWorkGroups; +#endif + #else DIR * d; #endif @@ -1254,7 +1297,9 @@ static FileDesc FileFind(const char * path, const char * extensions) break; } d.fHandle = (HANDLE)(uintptr) drives; +#if !defined(__UWP__) d.resource = 0; +#endif } else if(path[0] != '\\' || path[1] != '\\' || strstr(path+2, "\\")) { @@ -1266,8 +1311,10 @@ static FileDesc FileFind(const char * path, const char * extensions) wcscat(dir, L"*.*"); d.fHandle = FindFirstFile(dir, &winFile); + #if !defined(__UWP__) if(d.fHandle == INVALID_HANDLE_VALUE && WinReviveNetworkResource(dir)) d.fHandle = FindFirstFile(dir, &winFile); +#endif if(d.fHandle != INVALID_HANDLE_VALUE) { UTF16toUTF8Buffer(winFile.cFileName, file.name, MAX_FILENAME); @@ -1295,6 +1342,7 @@ static FileDesc FileFind(const char * path, const char * extensions) result = file; } } + #if !defined(__UWP__) else { HANDLE handle = 0; @@ -1417,6 +1465,7 @@ static FileDesc FileFind(const char * path, const char * extensions) if(c >= count && buffer) delete buffer; } } +#endif #else struct dirent *de; struct stat s; @@ -1550,6 +1599,7 @@ private class FileDesc : struct case DRIVE_REMOTE: stats.attribs.isRemote = true; break; case DRIVE_CDROM: stats.attribs.isCDROM = true; break; } + #if !defined(__UWP__) if(driveType == DRIVE_NO_ROOT_DIR) { uint16 remoteName[1024]; @@ -1564,6 +1614,7 @@ private class FileDesc : struct _wpath[2] = '\\'; _wpath[3] = 0; } +#endif if(driveType != DRIVE_REMOVABLE && driveType != DRIVE_REMOTE && GetVolumeInformation(_wpath, _wvolume, MAX_FILENAME - 1, null, null, null, null, 0)) @@ -1614,6 +1665,7 @@ private class FileDesc : struct else break; } +#if !defined(__UWP__) else { if(d.name[2]) @@ -1697,6 +1749,7 @@ private class FileDesc : struct } } } +#endif #else struct dirent *de; struct stat s; diff --git a/ecere/src/sys/JSON.ec b/ecere/src/sys/JSON.ec index 610070a8ec..de6cd1dcc3 100644 --- a/ecere/src/sys/JSON.ec +++ b/ecere/src/sys/JSON.ec @@ -3,11 +3,12 @@ namespace sys; import "instance" import "System" import "Array" +import "AVLTree" default: __attribute__((unused)) static void UnusedFunction() { - int a; + int a = 0; a.OnGetDataFromString(null); a.OnGetString(null, 0, 0); a.OnCopy(0); @@ -17,9 +18,209 @@ extern int __ecereVMethodID_class_OnCopy; extern int __ecereVMethodID_class_OnGetDataFromString; extern int __ecereVMethodID_class_OnGetString; extern int __ecereVMethodID_class_OnFree; + + +public define jsonIndentWidth = 3; +const String indentModule = " "; + private: +// TOFIX: How should this be handled? +/*static*/ class FreeingAVLTree : AVLTree +{ + ~FreeingAVLTree() + { + Free(); + } +} + +public class OptionsMap : Map +{ + ~OptionsMap() + { + Free(); + } +} + +public class JSONTypeOptions : uint +{ + /* + * bit class to hold flags tht apply to a specific class when + * (de)serialized (from)to JSON + */ + + // Options to force use of OnGetDataFromString for a specific JSON type + +public: + // Numeric values: (hexa)decimal int, double (allows exponent notation). + bool numbersUseOGDFS:1; + // JSON booleans: unquoted true or false. + bool boolUseOGDFS:1; + // JSON null value: unquoted null. + bool nullUseOGDFS:1; + // Values enclosed in '"double quotes"' + bool stringUseOGDFS:1; + // Values enclosed in '[brackets]' + bool arrayUseOGDFS:1; + // Values enclosed in '{braces}' + bool objectUseOGDFS:1; + // Set this to remove surrounding quotes before calling OGDFS: kept by default; + bool stripQuotesForOGDFS:1; + // If the current JSON type is not among those that should use OGDFS, fail immediately. + bool strictOGDFS:1; + // Options to control the writing of an object to JSON/eCON. + // bool boolUseOGDFS:1; + // bool nullUseOGDFS:1; +} + + +OptionsMap defaultJsonOptions{[ + { + "FieldValue", { + // Without the type part, the value is read and set, but + // the type remains 0 and teh bject is unusable. + // sonething like { "d" : 1.2, "type" : {"type" : 2}} would be + // interpreted corectly, but do we want to expose so + // much implementataion in JSON files? FieldValue is + // not meant to read arrays, maps or anything else anways. + numbersUseOGDFS = true, + boolUseOGDFS = true, + nullUseOGDFS = true, + stringUseOGDFS = true, + strictOGDFS = true + } + }, + { + "FlexyField" , { + numbersUseOGDFS = true, + boolUseOGDFS = true, + nullUseOGDFS = true, + stringUseOGDFS = true, + arrayUseOGDFS = true, + objectUseOGDFS = true + } + }, + { // from here on the classes are from gnosis3 + "ProcessInputValue", { + // Derives from FieldValue, adds [] and {} suport. + numbersUseOGDFS = true, + boolUseOGDFS = true, + nullUseOGDFS = true, + stringUseOGDFS = true, + arrayUseOGDFS = true, + objectUseOGDFS = true + } + }, + { + "GeoJSONValue", { + // Derives from FieldValue, adds [] suport. + numbersUseOGDFS = true, + boolUseOGDFS = true, + nullUseOGDFS = true, + stringUseOGDFS = true, + arrayUseOGDFS = true + } + }, + { + "MBGLFilterValue", { + // Derives from FieldValue : adds [] support. + numbersUseOGDFS = true, + boolUseOGDFS = true, + nullUseOGDFS = true, + stringUseOGDFS = true, + arrayUseOGDFS = true + } + }, + { + "OGCAPIMultiBoundingBox", { + // Seems to only work with [] and null. + strictOGDFS = true, + nullUseOGDFS = true, + arrayUseOGDFS = true + } + }, + { + "OGCAPIMultiInterval", { + // Seems to only work with [] and null. + strictOGDFS = true, + nullUseOGDFS = true, + arrayUseOGDFS = true + } + }, + { + "OGCAPISpatialExtent", { + nullUseOGDFS = true, + arrayUseOGDFS = true + } + }, + { + "OGCAPITemporalExtent", { + nullUseOGDFS = true, + arrayUseOGDFS = true + } + } + ]}; + + +FreeingAVLTree compactTypes +{ [ + "GeoPoint", + "GeoExtent", + "GeometryData", + "PolygonGeometryData", + "LineGeometryData", + "PointGeometryData", + "RecordDataField", + "MapNode", + "LineString", + "PolygonContour", + "StartEndPair", + "VectorPiece", + "ShortPoint", + "uint16", + "uint32", + "PolygonVertexFlags", + "byte", + "UMSRowsSpecs", + "GMLTimeExtent", + "EX_TemporalExtent", + "RS_Identifier", + "CI_Date", + "CI_Telephone", + "MD_Resolution", + "UMSFormat", + "OGCAPITileMatrixSetLimit", + "OGCAPIVariableWidth", + "Vector3D", + "Vector3Df", + "Quaternion", + "Euler" +] }; + +FreeingAVLTree compactArrays +{ [ + "Polygon", + "GeoPoint", + "PolygonContour", + "StartEndPair", + "VectorPiece", + "ShortPoint", + "uint16", + "uint32", + "PolygonVertexFlags", + "byte", + "UMSRowsSpecs", + "CI_ResponsibleParty", + "MD_CharacterSetCode", + "MD_Keywords", + "EX_Extent", + "FieldValue", + "double" +] }; + public enum JSONResult { syntaxError, success, typeMismatch, noItem }; +public enum JSONFirstLetterCapitalization { keepCase, upperCase, lowerCase }; + public enum SetBool : uint { @@ -32,22 +233,104 @@ public enum SetBool : uint }*/ }; +static Mutex mutexTemplateInstanceFix { }; public class ECONParser : JSONParser { eCON = true; } -// #define DEBUG_PARSING +static struct JSONParserState +{ + int pos; + char ch; + int col, line; +}; public class JSONParser { public: File f; + // A parser instance can register additional options sets for selected + // classes by setting the OptionsMAp customJsonOptions member to non-null + // and filling it with {class names, JSONTypeOptions) key-value pairs. + // Note that customJsonOptions is checked before the global OptionsMAp + // defaultJsonOptions, so it is possible to overrule th options for a class + // that is already registered there. + OptionsMap customJsonOptions; private: char ch; bool eCON; + int charPos, col, line; + int maxPos; + bool debug; + bool warnings; + + warnings = true; + + public property bool debug { set { debug = value; } get { return debug; } } + public property bool warnings { set { warnings = value; } get { return warnings; } } + + ~JSONParser() + { + if(customJsonOptions) + { + customJsonOptions.Free(); + delete customJsonOptions; + } + } + + static inline JSONTypeOptions getTypeOptions(Class type) + { + JSONTypeOptions retrieved {}; + if(customJsonOptions) + retrieved = customJsonOptions[type.name]; + if(!retrieved) + retrieved = defaultJsonOptions[type.name]; + return retrieved; + } + + bool ReadChar(char * ch) + { + bool result = f.Getc(ch); + charPos++; + if(charPos > maxPos) + { + if(*ch == '\r'); + else if(*ch == '\n') + { + line++; + col = 1; + } + else + col++; +#ifdef _DEBUG + if(debug) + Print(*ch); +#endif + maxPos = charPos; + } + return result; + } + + void BackUpState(JSONParserState state) + { + state.ch = ch; + state.pos = charPos; + state.col = col; + state.line = line; + } + + void ResetState(JSONParserState state) + { + ch = state.ch; + charPos = state.pos; + col = state.col; + line = state.line; + f.Seek(charPos, start); + } + void SkipEmpty() { if(eCON) @@ -58,10 +341,7 @@ private: while(!f.Eof() && (!ch || lineComment || comment || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '/')) { pch = ch; - f.Getc(&ch); - #ifdef DEBUG_PARSING - Print(ch); - #endif + ReadChar(&ch); if(!lineComment && !comment && pch == '/') { if(ch == '/') @@ -79,10 +359,7 @@ private: { while(!f.Eof() && (!ch || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '/')) { - f.Getc(&ch); - #ifdef DEBUG_PARSING - Print(ch); - #endif + ReadChar(&ch); } } } @@ -91,241 +368,322 @@ private: { while(!f.Eof() && (!ch || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == ';')) { - f.Getc(&ch); - #ifdef DEBUG_PARSING - Print(ch); - #endif + ReadChar(&ch); } } JSONResult GetValue(Class type, DataValue value) { - return _GetValue(type, value, null); + return _GetValue(type, value, null, null); } - static inline JSONResult _GetValue(Class type, DataValue value, Container forMap) + static inline JSONResult _GetValue(Class type, DataValue value, Container forMap, Class * rType) { JSONResult result = syntaxError; - ch = 0; + bool (* onGetDataFromString)(void *, void *, const char *) = type ? (void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString] : null; + JSONParserState backState; + JSONTypeOptions typeOptions {}; + + BackUpState(backState); SkipEmpty(); - if(ch == '\"') + // TODO: Offer more flexible mechanism to customize JSON representation... + if( type && onGetDataFromString != type.base._vTbl[__ecereVMethodID_class_OnGetDataFromString]) { - String string; - result = GetString(&string); - if(result) + // Avoid unnecessary map lookup + typeOptions = getTypeOptions(type); + } + + if(ch == '\"') + { // Quoted Strings and special cases for selected classes + if (typeOptions.stringUseOGDFS || typeOptions.strictOGDFS) + result = useOGDFS(type, value, typeOptions, !typeOptions.stringUseOGDFS); + else { - Property prop; - if(type && (!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))) - { - value.p = string; - } - else if(type && (type.type == enumClass || type.type == unitClass)) + String normalStr; + if(!type && rType) type = class(String); + result = GetString(&normalStr); // Checked: this removes the quotes + if(!type) delete normalStr, normalStr = null, result = typeMismatch; + + if(result) { - if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, &value.i, string)) - result = success; - else - result = typeMismatch; - delete string; + result = ConvertStringToValue(type, normalStr, value); } - else if(type && (prop = eClass_FindProperty(type, "String", type.module))) + delete normalStr; + } + } + else if(ch == '[') + { + // Normal array of objects (but could be a map). + + if (typeOptions.arrayUseOGDFS || typeOptions.strictOGDFS) + result = useOGDFS(type, value, typeOptions, !typeOptions.arrayUseOGDFS); + else + { + Container array = null; + if(type && eClass_IsDerived(type, class(Map))) { - // TOFIX: Add more conversion property support... Expecting void * compatible here - value.p = ((void *(*)())(void *)prop.Set)(string); - result = success; - delete string; + result = GetECONMap(type, (Map *)&array); } - else if(type && eClass_IsDerived(type, class(ColorAlpha))) + else if(!type || eClass_IsDerived(type, class(Container))) { - result = GetColorAlpha(string, value); - delete string; + result = GetArray(type ? type : class(Array), &array); + if(!type && array) + { + array.Free(); + delete array; + } } - else if(type && (type.type == structClass)) + else + result = typeMismatch; + + if(array) type = array._class; + + if(result == success && type && eClass_IsDerived(type, class(Container))) { - if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, value.p, string)) - result = success; - else - result = typeMismatch; - delete string; + value.p = array; } - else + else if(array && type && (type.type == normalClass || type.type == noHeadClass)) { - delete string; - result = typeMismatch; + if(type.type == normalClass && eClass_IsDerived(type, class(Container))) + array.Free(); + delete array; } } } - else if(ch == '[') + else if(ch == '-' || isdigit(ch)) { - Container array; - if(type && eClass_IsDerived(type, class(Map))) - { - result = GetMap(type, (Map *)&array); - } - else - result = GetArray(type, &array); + //Numeric Value - if(result == success && type && eClass_IsDerived(type, class(Container))) - { - value.p = array; - } + if (typeOptions.numbersUseOGDFS || typeOptions.strictOGDFS) + result = useOGDFS(type, value, typeOptions, !typeOptions.numbersUseOGDFS); else { - if(array) - array.Free(); - delete array; + if(!type) type = class(double); + result = GetNumber(type, value); } } - else if(ch == '-' || isdigit(ch)) - { - result = GetNumber(type, value); - } else if(ch == '{') { - if(type && (type.type == structClass || type.type == normalClass || type.type == noHeadClass)) + // Normal Object: does different things if derived from Map or according to the type.type + + if (typeOptions.objectUseOGDFS || typeOptions.strictOGDFS) + result = useOGDFS(type, value, typeOptions, !typeOptions.objectUseOGDFS); + else { - void * object = value.p; - result = _GetObject(type, &object, forMap); - if(result) + if(type && eClass_IsDerived(type, class(Map))) { - if(type && type.type == structClass); + Container array; + result = GetJSONMap(type, (Map *)&array); + if(result == success && type && eClass_IsDerived(type, class(Container))) + { + value.p = array; + } else - value.p = object; + { + if(array) + array.Free(); + delete array; + } + } + else if(type && (type.type == structClass || type.type == normalClass || type.type == noHeadClass)) + { + void * object = value.p; + result = _GetObject(type, &object, forMap); + if(result) + { + if(type && type.type == structClass); + else + value.p = object; + } + } + else if(type && type.type == bitClass) + { + uint64 object = 0; + result = _GetObject(type, (void **)&object, null); + if(result) + value.ui64 = object; + } + else + { + void * object = value.p; + result = _GetObject(type, &object, null); + if(result) + { + result = typeMismatch; + if(type) + ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, object); + } } } - else if(type && type.type == bitClass) - { - uint64 object = 0; - result = GetObject(type, (void **)&object); - if(result) - value.ui64 = object; + } + else if(isalpha(ch) || ch == '_') + { + // Unquoted string values like true false null and some special cases understood by eCON + // REVIEW: Why do we have this new eCON / JSON difference? + // true, false and null are also valid in JSON + String unqStr; + if (eCON) + { // Get the string the right way for eCON + if(GetIdentifier(&unqStr, null)) + result = success; } else { - void * object = value.p; - result = GetObject(type, &object); - if(result) + // Get the string the right way for JSON + int c = 0; + unqStr = new char[256]; + while(c < 255 && (isalpha(ch) || isdigit(ch) || ch == '_')) { - result = typeMismatch; - if(type) - ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, object); + unqStr[c++] = ch; + if(!ReadChar(&ch)) break; } + unqStr[c] = 0; + result = success; } - } - else if(isalpha(ch) || ch == '_') - { - if(eCON) + + if(result && type) { - String string; - if(GetIdentifier(&string, null)) + // TODO: Make this configurable as well for new types? + if( eCON & (type.type == enumClass || type.type == unitClass || eClass_IsDerived(type, class(ColorAlpha)) || eClass_IsDerived(type, class(Color)))) { - result = success; - if(eCON && type && (type.type == enumClass || type.type == unitClass || eClass_IsDerived(type, class(ColorAlpha)) || eClass_IsDerived(type, class(Color)))) + bool isColorAlpha = type.type != enumClass && type.type != unitClass && + eClass_IsDerived(type, class(ColorAlpha)); // how to handle classes that derive from a special one in general? + if(isColorAlpha) + type = class(Color); + // should this be set by calling __ecereVMethodID_class_OnGetDataFromString ? + if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, &value.i, unqStr)) { - bool isColorAlpha = type.type != enumClass && type.type != unitClass && eClass_IsDerived(type, class(ColorAlpha)); if(isColorAlpha) - type = class(Color); - // should this be set by calling __ecereVMethodID_class_OnGetDataFromString ? - if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, &value.i, string)) - { - if(isColorAlpha) - value.ui |= 0xFF000000; - result = success; - } - else - result = typeMismatch; + value.ui |= 0xFF000000; + result = success; } - else if(type && !strcmp(type.name, "bool")) + else + result = typeMismatch; + } + // Start of normal JSON cases + else if(!strcmp(type.name, "bool")) + { + if(!strcmpi(unqStr, "false")) value.i = 0; + else if(!strcmpi(unqStr, "true")) value.i = 1; + else + result = typeMismatch; + } + else if(!strcmp(type.name, "SetBool")) + { + if(!strcmpi(unqStr, "false")) value.i = SetBool::false; + else if(!strcmpi(unqStr, "true")) value.i = SetBool::true; + else + result = typeMismatch; + } + else if(!strcmpi(unqStr, "false") || !strcmpi(unqStr, "true")) + { + // Some classes 'can'/'need to' handle booleans on their own. + // and in this position they are neither bool or SetBool, that + // do not need OGDFS and are already treated. + + if (typeOptions.boolUseOGDFS || typeOptions.strictOGDFS) { - if(!strcmpi(string, "false")) value.i = 0; - else if(!strcmpi(string, "true")) value.i = 1; + if (typeOptions.boolUseOGDFS) + result = ConvertStringToValue(type, unqStr, value); else result = typeMismatch; } - else if(type && !strcmp(type.name, "SetBool")) + } + else if(!strcmpi(unqStr, "null")) + { + // Some classes 'can'/'need to' handle null values on their own. + + if (typeOptions.nullUseOGDFS || typeOptions.strictOGDFS) { - if(!strcmpi(string, "false")) value.i = SetBool::false; - else if(!strcmpi(string, "true")) value.i = SetBool::true; + if (typeOptions.nullUseOGDFS) + result = ConvertStringToValue(type, unqStr, value); else result = typeMismatch; } - else if(type && !strcmpi(string, "null")) - { - if(type.type != structClass) - value.p = 0; - } - else if(type && isSubclass(type, string)) + else if(type.type != structClass) + value.p = 0; + } + // End of normal JSON cases + else if(eCON) + { + Class cType = superFindClass(unqStr, type.module); + if(cType && eClass_IsDerived(cType, type)) { void * object = value.p; - Class subtype = superFindClass(string, type.module); SkipEmpty(); - result = GetObject(subtype, &object); + result = _GetObject(cType, &object, null); // TO REVIEW: Is this a problem with bitClass here, in 32 bit? if(result) { - if(subtype && subtype.type == structClass); - else if(subtype && (subtype.type == normalClass || subtype.type == noHeadClass || subtype.type == bitClass)) + if(cType && cType.type == structClass); + else if(cType && (cType.type == normalClass || cType.type == noHeadClass || cType.type == bitClass)) { value.p = object; } else { result = typeMismatch; - if(subtype) - ((void (*)(void *, void *))(void *)subtype._vTbl[__ecereVMethodID_class_OnFree])(subtype, object); + if(cType) + ((void (*)(void *, void *))(void *)cType._vTbl[__ecereVMethodID_class_OnFree])(cType, object); } } } - else - result = typeMismatch; - } - delete string; - } - else - { - char buffer[256]; - int c = 0; - while(c < sizeof(buffer)-1 && (isalpha(ch) || isdigit(ch) || ch == '_')) - { - buffer[c++] = ch; - if(!f.Getc(&ch)) break; - #ifdef DEBUG_PARSING - Print(ch); - #endif - } - buffer[c] = 0; - result = success; - - if(type) - { - if(!strcmp(type.name, "bool")) - { - if(!strcmpi(buffer, "false")) value.i = 0; - else if(!strcmpi(buffer, "true")) value.i = 1; - else - result = typeMismatch; - } - else if(!strcmp(type.name, "SetBool")) + else if(ch != '=') { - if(!strcmpi(buffer, "false")) value.i = SetBool::false; - else if(!strcmpi(buffer, "true")) value.i = SetBool::true; + Property convProp = null; + if (cType) // avoid being left with the first conversion property if cType is null + for(convProp = type.conversions.first; convProp; convProp = convProp.next) + { + if(!strcmp(convProp.name, cType.fullName)) + break; + } + + // At this point, convProp != null guarantees cType != null + + if(convProp && cType.type == unitClass) + { + // TODO: Improve on this... + DataValue v; + SkipEmpty(); + if(ch == '{') + ch = 0; + SkipEmpty(); + result = GetNumber(cType, v); + SkipEmpty(); + if(ch == '}') + ch = 0; + if(result) + { + if(type && (type.type == normalClass || type.type == noHeadClass)) + { + if(!strcmp(cType.dataTypeString, "double")) + { + value.p = ((void *(*)(double))(void *)convProp.Set)(v.d); + } + } + else + result = typeMismatch; + } + else + result = typeMismatch; + } else result = typeMismatch; } - else if(!strcmpi(buffer, "null")) - { - if(type.type != structClass) - value.p = 0; - } else result = typeMismatch; } else result = typeMismatch; } + delete unqStr; } else if(ch == '}' || ch == ']') result = noItem; - if(result == typeMismatch) - PrintLn("Warning: Value type mismatch"); + if(result == typeMismatch && warnings) + if(type) + PrintLn("Warning: Value type mismatch (", line, ":", col, ")"); + if(rType) + *rType = type; return result; } @@ -336,56 +694,75 @@ private: *array = null; if(ch == '[') { + bool isAVLTree = eClass_IsDerived(type, class(AVLTree)); + + mutexTemplateInstanceFix.Wait(); *array = eInstance_New(type); + mutexTemplateInstanceFix.Release(); result = success; while(result) { DataValue value { }; Class arrayType = null; + Class rType = null; JSONResult itemResult; if(eClass_IsDerived(type, class(Container))) - { arrayType = type.templateArgs[0].dataTypeClass; - } if(arrayType && arrayType.type == structClass) value.p = new0 byte[arrayType.structSize]; - itemResult = GetValue(arrayType, value); + ch = 0; + itemResult = _GetValue(arrayType, value, null, &rType); + if(!arrayType && rType) + { + char className[1024]; + sprintf(className, "%s<%s>", type.name, rType.name); + delete *array; + mutexTemplateInstanceFix.Wait(); + type = eSystem_FindClass(type.module.application, className); + *array = eInstance_New(type); + mutexTemplateInstanceFix.Release(); + arrayType = rType; + } +#ifdef _DEBUG + else if(!arrayType && warnings) + PrintLn("JSON Parser Warning: Null item type parsing ", type.name); +#endif if(itemResult == success) { // TODO: Verify the matching between template type and uint64 uint64 t; - if(arrayType.type == structClass) + if(arrayType && arrayType.type == structClass) { t = (uint64)(uintptr)value.p; } - else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double")) + else if(arrayType && (arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))) { t = value.ui64; //*(uint64 *)&value.d; } - else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float")) + else if(arrayType && (arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))) { t = value.ui; //f*(uint *)&value.f; } - else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") || - !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64")) + else if(arrayType && (arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") || + !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))) { t = value.ui64; } - else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") || - !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint")) + else if(arrayType && (arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") || + !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))) { t = value.i; } - else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") || + else if(arrayType && (arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") || !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") || - !strcmp(arrayType.dataTypeString, "int16")) + !strcmp(arrayType.dataTypeString, "int16"))) { t = value.s; } - else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") || - !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte")) + else if(arrayType && (arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") || + !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))) { t = value.c; } @@ -393,7 +770,18 @@ private: { t = (uint64)(uintptr)value.p; } - ((void *(*)(void *, uint64))(void *)array->Add)(*array, t); + if(*array && (arrayType || value.p)) + ((void *(*)(void *, uint64))(void *)array->Add)(*array, t); + + if(isAVLTree && value.p) + { + if(arrayType && (!strcmp(arrayType.name, "String") || !strcmp(arrayType.dataTypeString, "char *"))) + delete value.p; + else if(arrayType.type == normalClass) + eInstance_Delete(value.p); + else if(arrayType.type == systemClass) + delete value.p; + } if(arrayType && arrayType.type == structClass) delete value.p; @@ -402,8 +790,8 @@ private: { if(itemResult == typeMismatch) { - if(arrayType) - PrintLn("Warning: Incompatible value for array value, expected ", (String)arrayType.name); + if(arrayType && warnings) + PrintLn("Warning: Incompatible value for array value, expected ", (String)arrayType.name, " (", line, ":", col, ")"); } else if(itemResult == noItem) result = success; @@ -431,7 +819,7 @@ private: return result; } - JSONResult GetMap(Class type, Map * map) + JSONResult GetECONMap(Class type, Map * map) { JSONResult result = syntaxError; SkipEmpty(); @@ -440,11 +828,14 @@ private: { Class mapNodeType = type.templateArgs[0].dataTypeClass; Class keyType = mapNodeType.templateArgs[0].dataTypeClass; + Class valueType = mapNodeType.templateArgs[2].dataTypeClass; Property keyProp = null; if(keyType && !strcmp(keyType.dataTypeString, "char *")) keyProp = eClass_FindProperty(mapNodeType, "key", mapNodeType.module); + mutexTemplateInstanceFix.Wait(); *map = eInstance_New(type); + mutexTemplateInstanceFix.Release(); result = success; while(result) @@ -453,7 +844,8 @@ private: JSONResult itemResult; - itemResult = _GetValue(mapNodeType, value, *map); + ch = 0; + itemResult = _GetValue(mapNodeType, value, *map, null); if(itemResult == success) { String s = keyProp ? ((void * (*)(void *))(void *)keyProp.Get)(value.p) : null; @@ -465,14 +857,16 @@ private: { if(itemResult == typeMismatch) { - if(mapNodeType) - PrintLn("Warning: Incompatible value for array value, expected ", (String)mapNodeType.name); + if(mapNodeType && warnings) + PrintLn("Warning: Incompatible value for array value, expected ", (String)mapNodeType.name, " (", line, ":", col, ")"); } else if(itemResult == noItem) result = success; else result = itemResult; } + if(valueType && valueType.type == structClass) + delete value.p; if(result != syntaxError) { @@ -494,6 +888,105 @@ private: return result; } + JSONResult GetJSONMap(Class type, Map * map) + { + JSONResult result = syntaxError; + SkipEmpty(); + *map = null; + if(ch == '{') + { + Class mapNodeType = type.templateArgs[0].dataTypeClass; + Class valueType = mapNodeType.templateArgs[2].dataTypeClass; + mutexTemplateInstanceFix.Wait(); + *map = eInstance_New(type); + mutexTemplateInstanceFix.Release(); + result = success; + + while(result) + { + DataValue value { }; + String string; + bool wasQuoted = false; + + ch = 0; + if(eCON) + { + SkipExtraSemicolon(); + if(ch == '}') + break; + } + SkipEmpty(); + + if(eCON ? GetIdentifier(&string, &wasQuoted) : GetString(&string)) + { + ch = 0; + SkipEmpty(); + + if(ch == ':' || (eCON && ch == '=')) + { + JSONResult itemResult; + if(ch == ':' || ch == '=') + ch = 0; + if(valueType.type == structClass) + value.p = new0 byte[valueType.structSize]; + itemResult = GetValue(valueType, value); + if(itemResult == success) + { + IteratorPointer node = ((IteratorPointer (*)(Map, uint64, bool, void *))(void *)map->GetAtPosition)(*map, (uint64)(uintptr)string, true, null); + + switch(valueType.type) + { + case normalClass: + case noHeadClass: + case structClass: + ((bool (*)(Map, IteratorPointer, uint64))(void *)map->SetData)(*map, node, (uint64)(uintptr)value.p); + break; + case systemClass: + ((bool (*)(Map, IteratorPointer, uint64))(void *)map->SetData)(*map, node, value.ui64); + break; + default: + if(warnings) + PrintLn("Warning: Unhandled class type for JSON map ", (String)valueType.name, " (", line, ":", col, ")"); + break; + } + } + else + { + if(itemResult == typeMismatch) + { + if(mapNodeType && warnings) + PrintLn("Warning: Incompatible value for JSON map value, expected ", (String)valueType.name, " (", line, ":", col, ")"); + } + else if(itemResult == noItem) + result = success; + else + result = itemResult; + } + if(valueType.type == structClass) + delete value.p; + } + } + delete string; + if(result != syntaxError) + { + if(ch != '}' && ch != ',') + { + ch = 0; + SkipEmpty(); + } + if(ch == '}') + { + break; + } + else if(ch != ',') + result = syntaxError; + } + } + } + ch = 0; + return result; + } + JSONResult GetIdentifier(String * string, bool * wasQuoted) { JSONResult result = syntaxError; @@ -508,18 +1001,12 @@ private: else buffer.Add(ch); result = success; - while(f.Getc(&ch)) + while(ReadChar(&ch)) { - #ifdef DEBUG_PARSING - Print(ch); - #endif - if(!comment && ch == '/') + if(!comment && !quoted && ch == '/') { - if(f.Getc(&ch)) + if(ReadChar(&ch)) { - #ifdef DEBUG_PARSING - Print(ch); - #endif if(ch == '/') break; @@ -539,12 +1026,8 @@ private: } else if(comment && ch == '*') { - if(f.Getc(&ch)) + if(ReadChar(&ch)) { - #ifdef DEBUG_PARSING - Print(ch); - #endif - if(ch == '/') { comment = false; @@ -576,11 +1059,164 @@ private: *string = CopyString(buffer.array); } delete buffer; - if(ch != ',' && ch != '}' && ch != ';' && ch != '/' && ch != '=' && ch != ':') + if(ch != ']' && ch != ',' && ch != '}' && ch != ';' && ch != '/' && ch != '=' && ch != ':') ch = 0; return result; } + JSONResult ConvertStringToValue(Class type, const String string, DataValue value) + { + // Assign the proper value obtained from the special String to the + // DataValue object scording to type. + // The string should be disposed of appropriately in the caller. + + Property prop; + JSONResult result; + bool (* onGetDataFromString)(void *, void *, const char *) = type ? (void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString] : null; + + if(!type) // Fail if no type is given and guarantee type != null afterwards. + result = typeMismatch; + else if((!strcmp(type.name, "String") || + !strcmp(type.dataTypeString, "char *"))) + { + value.p = CopyString(string); + result = success; + } + else if((type.type == enumClass || type.type == unitClass) && onGetDataFromString && + onGetDataFromString(type, &value.i, string)) + result = success; + else if((prop = eClass_FindProperty(type, "String", type.module))) + { + // TOFIX: Add more conversion property support... Expecting void * compatible here + value.p = ((void *(*)())(void *)prop.Set)(string); + result = success; + } + else if((eClass_IsDerived(type, class(ColorAlpha)) || + eClass_IsDerived(type, class(Color)))) + result = GetColorAlpha(string, value); + else if((type.type == structClass) && onGetDataFromString && + onGetDataFromString(type, value.p, string)) + result = success; + else if((type.type == normalClass) && onGetDataFromString && + onGetDataFromString(type, &value.p, string)) + result = success; + else + { + result = typeMismatch; + // The catch-all takes care of objects created + // in the normalClass check if any: + // the case of type == null is treated in the first if + if(type.type == normalClass) + ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, value.p); + } + return result; + } + + static inline JSONResult useOGDFS(Class type, DataValue value, JSONTypeOptions typeOptions, bool discard) + { + String special = null; + JSONResult result = GetSpecialValueString(&special); + if(result) + { + if (discard) + result = typeMismatch; + else + { + if (special[0] == '\"' && typeOptions.stripQuotesForOGDFS) + { + int len = strlen(special) -2; + memmove(special, special+1, len); + special[len] = '\0'; + } + result = ConvertStringToValue(type, special, value); + } + } + delete special; + return result; + } + + JSONResult GetSpecialValueString(String * string) + { + // Get verbatim all text enclosed in double quotes ("text") square + // brackets ([text]) or curly braces ({text}), including the delimiters. + // In the case of brackets and braces it also enforces proper nesting of + // the two types of delimiters. + + bool done = false, escaped = false, quoted = ch == '\"'; + int size = 32, len = 0; + char * s = new char[size]; + + int nestLevel = 0; + uint64 openSquar = 0; // track up to 63 + uint64 openCurly = 0; // nesting levels + char oC = '{', cC = '}', oS = '[', cS = ']'; + + while(ch && !done) + { + if(len + 1 >= size) + { + size += size >> 1; + s = renew s char[size]; + } + if(ch != '\n' && ch != 32) + { + if(!quoted) + { + if(nestLevel <= 0 && (ch == ',' || ch == cC || ch == cS)) // Completed correctly + break; + else if(ch == oC) + { + nestLevel++; + openCurly = (openCurly << 1) | 1; + openSquar = openSquar << 1; + } + else if(ch == oS) + { + nestLevel++; + openCurly = openCurly << 1; + openSquar = (openSquar << 1) | 1; + } + else if(ch == cC) + { + nestLevel--; // let nestLevel free to become negative + if (openSquar & 1 ) + break; // openSquar will certainly be > 0 + openCurly >>= 1; + openSquar >>= 1; + } + else if(ch == cS) + { + nestLevel--; // let nestLevel free to become negative + if (openCurly & 1) + break; // openCurly will certainly be > 0 + openCurly >>= 1; + openSquar >>= 1; + } + } + else if(quoted) + { + if(escaped) + escaped = false; + else if(ch == '\\') + escaped = true; + else if(ch == '\"' && len > 0) + done = true; + } + } + + s[len++] = ch; + if(!ReadChar(&ch)) + break; // Get next ch and stop on failure to do so + } + s[len] = 0; + while(len > 0 && isspace(s[len-1])) + s[--len] = 0; + s = renew s char[len + 1]; + *string = s; // | open braces match closed one | no extra closing + + return (len > 0 && openSquar == 0 && openCurly == 0 && nestLevel > -1) ? success : syntaxError; + } + JSONResult GetString(String * string) { JSONResult result = syntaxError; @@ -591,12 +1227,8 @@ private: SkipEmpty(); if(ch == '\"' || eCON) { - while(f.Getc(&ch)) + while(ReadChar(&ch)) { - #ifdef DEBUG_PARSING - Print(ch); - #endif - if(ch == '\\' && !escaped) escaped = true; else @@ -612,29 +1244,32 @@ private: { // SKIP FOR NOW... char unicode[4]; - f.Getc(&unicode[0]); - f.Getc(&unicode[1]); - f.Getc(&unicode[2]); - f.Getc(&unicode[3]); + ReadChar(&unicode[0]); + ReadChar(&unicode[1]); + ReadChar(&unicode[2]); + ReadChar(&unicode[3]); } escaped = false; } else if(eCON && ch == '\"') { - int seekback = 0; - char pch; bool lineComment = false; bool comment = false; + JSONParserState seekBackState; + bool seekBack = false; + while(!f.Eof()) { - pch = ch; - f.Getc(&ch); - - #ifdef DEBUG_PARSING - Print(ch); - #endif - seekback--; - if(!lineComment && !comment && pch == '/') + JSONParserState backState; + BackUpState(backState); + if(!seekBack) + { + seekBack = true; + seekBackState = backState; + } + ReadChar(&ch); + + if(!lineComment && !comment && backState.ch == '/') { if(ch == '/') lineComment = true; @@ -643,24 +1278,25 @@ private: } else if(lineComment && ch == '\n') lineComment = false; - else if(comment && pch == '*' && ch == '/') + else if(comment && backState.ch == '*' && ch == '/') comment = false; else if(ch == '=' || ch == ':' || ch == ';' || ch == ',' || ch == ']' || ch == '}') { ch = 0; - seekback = -1; + seekBackState = backState; + seekBack = true; break; } else if(ch == '\"') { - seekback = 0; + seekBack = false; ch = 0; break; } } - if(seekback != 0) + if(seekBack) { - f.Seek(seekback, current); + ResetState(seekBackState); break; } } @@ -688,7 +1324,22 @@ private: public JSONResult GetObject(Class objectType, void ** object) { - return _GetObject(objectType, object, null); + charPos = 0, line = 1, col = 1, maxPos = 0; + if(!eCON && objectType && objectType.type == normalClass && eClass_IsDerived(objectType, class(Map))) + { + return GetJSONMap(objectType, (Map *)object); + } + else if(!eCON && objectType && objectType.type == normalClass && eClass_IsDerived(objectType, class(Array))) + { + return GetArray(objectType, (Array *)object); + } + else if(objectType && objectType.type == structClass) + { + memset(object, 0, objectType.structSize); + return _GetObject(objectType, &object, null); + } + else + return _GetObject(objectType, object, null); } static inline JSONResult _GetObject(Class objectType, void ** object, Container forMap) @@ -702,6 +1353,13 @@ private: *object = null; } SkipEmpty(); + if(!forMap && eCON && ch != '{') + { + DataValue value { }; + JSONResult result = GetValue(objectType, value); + *object = value.p; + return result; + } if(ch == '{') { Class mapKeyClass = null, mapDataClass = null; @@ -730,13 +1388,20 @@ private: result = success; if(objectType && (objectType.type == noHeadClass || objectType.type == normalClass)) - *object = eInstance_New(objectType); + { + mutexTemplateInstanceFix.Wait(); + if(!strcmp(objectType.dataTypeString, "char *")) + *object = null; // Type mismatch... was expecting String + else + *object = eInstance_New(objectType); + mutexTemplateInstanceFix.Release(); + } while(result) { String string; bool wasQuoted = false; - int64 seek; + JSONParserState backState; ch = 0; if(eCON) { @@ -745,7 +1410,7 @@ private: break; } SkipEmpty(); - seek = f.Tell(); + BackUpState(backState); if(eCON ? GetIdentifier(&string, &wasQuoted) : GetString(&string)) { DataMember member = null; @@ -755,9 +1420,12 @@ private: bool isTemplateArg = false; uint offset = 0; + removeDashes(string); + ch = 0; SkipEmpty(); + // Find Member in Object Class if(eCON && (ch != '=' && ch != ':')) { eClass_FindNextMember(objectType, &curClass, &curMember, subMemberStack, &subMemberStackPos); @@ -768,9 +1436,14 @@ private: // TODO: Document/Improve this! eClass_FindDataMemberAndOffset(objectType, curMember.name, &offset, objectType.module, null, null); - if(curMember._class.type == normalClass || curMember._class.type == noHeadClass) - offset += curMember._class.base.structSize; + if(curMember._class.type == normalClass || curMember._class.type == noHeadClass) + { + int add = curMember._class.base.structSize; + if(curMember._class.structAlignment && curMember._class.base.structSize % curMember._class.structAlignment) + add += curMember._class.structAlignment - curMember._class.base.structSize % curMember._class.structAlignment; + offset += add; + } if(mapKeyClass && !strcmp(prop ? prop.name : member.name, "key")) { type = mapKeyClass; @@ -791,10 +1464,13 @@ private: } else { - if(ch == '=' || ch == ':') - PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name); - else - PrintLn("Warning: default member assignment: no more members"); + if((ch == '=' || ch == ':') && warnings) + { + if(string[0] != '@') + PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name, " (", line, ":", col, ")"); + } + else if(warnings) + PrintLn("Warning: default member assignment: no more members (", line, ":", col, ")"); } } else if(objectType) @@ -816,29 +1492,61 @@ private: } else { - member = eClass_FindDataMemberAndOffset(objectType, string, &offset, objectType.module, subMemberStack, &subMemberStackPos); - if(member) - { - type = superFindClass(member.dataTypeString, objectType.module); - if(member._class.type == normalClass || member._class.type == noHeadClass) - offset += member._class.base.structSize; - curMember = member; - curClass = member._class; - } - else if(!member) + int c; + for(c = 0; c < ((!eCON || wasQuoted) ? 2 : 1); c++) { - prop = eClass_FindProperty(objectType, string, objectType.module); - if(prop) + if(c == 1) + string[0] = (char)toupper(string[0]); + member = eClass_FindDataMemberAndOffset(objectType, string, &offset, objectType.module, subMemberStack, &subMemberStackPos); + if(member) { - type = superFindClass(prop.dataTypeString, objectType.module); - curMember = (DataMember)prop; - curClass = prop._class; + type = superFindClass(member.dataTypeString, objectType.module); + if(member._class.type == normalClass || member._class.type == noHeadClass) + { + int add = member._class.base.structSize; + if(member._class.structAlignment && member._class.base.structSize % member._class.structAlignment) + add += member._class.structAlignment - member._class.base.structSize % member._class.structAlignment; + offset += add; + } + curMember = member; + curClass = member._class; + break; + } + else if(!member) + { + prop = eClass_FindProperty(objectType, string, objectType.module); + if(prop) + { + type = superFindClass(prop.dataTypeString, objectType.module); + curMember = (DataMember)prop; + curClass = prop._class; + break; + } + else if(c == 1) + { + string[0] = (char)tolower(string[0]); + if(warnings) + { + if(string[0] != '@') + PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name, " (", line, ":", col, ")"); + } + } } - else - PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name); } } } + +#ifdef _DEBUG + if(objectType && !member && !prop) + { + if(warnings) + { + if(string[0] != '@') + PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name, " (", line, ":", col, ")"); + } + } +#endif + if(type && type.templateClass && type.templateClass == class(Container)) { char * br = strchr(type.fullName, '<'); @@ -851,7 +1559,6 @@ private: } } - // Find Member in Object Class { DataValue value { }; @@ -866,21 +1573,36 @@ private: { value.p = new0 byte[type.structSize]; } - } - if(eCON && ch != '=' && ch != ':') + } // Need to pass over unknowns to avoid syntax errors + if((ch == ':' || (eCON && ch == '=')) || (eCON /*&& type && (prop || member)*/)) { - f.Seek(seek-1, start); - ch = 0; - } - if((ch == ':' || (eCON && ch == '=')) || (eCON && type && (prop || member))) - { - JSONResult itemResult = GetValue(type, value); + JSONResult itemResult; + if(ch == ':' || ch == '=') + ch = 0; + else + ResetState(backState); + + if(!eCON && type && type.type == normalClass) + { + // Fancy stuff: allow instantiation through 'type' property + if(member) + { + Instance * instance = (Instance *)((byte *)*object + offset); + if(*instance) + type = instance->_class; + } + } + + itemResult = GetValue(type, value); if(itemResult != syntaxError) { if(prop || member) { if(!type) - PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString); + { + if(warnings) + PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString, " (", line, ":", col, ")"); + } else if(itemResult == success) { BitMember bitMember = objectType.type == bitClass ? (BitMember) member : null; @@ -899,6 +1621,13 @@ private: container.Free(); delete container; } + else if(*ptr) + { + delete *ptr; +#ifdef _DEBUG + PrintLn("JSON: Warning: deleting existing instance!"); +#endif + } *ptr = value.p; } else if(type == class(double) || !strcmp(type.dataTypeString, "double")) @@ -1026,7 +1755,7 @@ private: else if(!strcmp(type.dataTypeString, "char *")) { ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p); - if(!isKey) + if(!isKey && !forMap) // TOCHECK: Map String values were being deleted here... delete value.p; } else if(type.type == enumClass || type.type == bitClass || type.type == unitClass || type.type == systemClass) @@ -1092,10 +1821,10 @@ private: } } } - else + else if(warnings) { PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name, - ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString); + ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString, " (", line, ":", col, ")"); } } } @@ -1155,12 +1884,8 @@ private: { if(!comment && ch == '/') { - if(f.Getc(&ch)) + if(ReadChar(&ch)) { - #ifdef DEBUG_PARSING - Print(ch); - #endif - if(ch == '*') comment = true; } @@ -1172,12 +1897,8 @@ private: } else if(comment && ch == '*') { - if(f.Getc(&ch)) + if(ReadChar(&ch)) { - #ifdef DEBUG_PARSING - Print(ch); - #endif - if(ch == '/') comment = false; } @@ -1193,11 +1914,7 @@ private: hexMode = true; buffer[c++] = ch; } - if(!f.Getc(&ch)) break; - #ifdef DEBUG_PARSING - Print(ch); - #endif - + if(!ReadChar(&ch)) break; } } else @@ -1205,11 +1922,7 @@ private: while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch))) { buffer[c++] = ch; - if(!f.Getc(&ch)) break; - #ifdef DEBUG_PARSING - Print(ch); - #endif - + if(!ReadChar(&ch)) break; } } buffer[c] = 0; @@ -1217,7 +1930,6 @@ private: if(result == syntaxError) return result; if(!type) return success; - result = syntaxError; // TOFIX: How to swiftly handle classes with base data type? if(type == class(double) || !strcmp(type.dataTypeString, "double")) @@ -1234,12 +1946,12 @@ private: //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64")) else if(!strcmp(type.dataTypeString, "int64")) { - value.i64 = strtol(buffer, null, eCON ? 0 : 10); // TOFIX: 64 bit support + value.i64 = strtoll(buffer, null, eCON ? 0 : 10); result = success; } else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64")) { - value.ui64 = strtoul(buffer, null, eCON ? 0 : 10); // TOFIX: 64 bit support + value.ui64 = strtoull(buffer, null, eCON ? 0 : 10); result = success; } else if(type == class(uint) || !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint")) @@ -1247,11 +1959,13 @@ private: value.ui = (uint)strtoul(buffer, null, eCON ? 0 : 10); // TOFIX: 64 bit support result = success; } - else + else if(type.type != structClass && type.type != noHeadClass && type.type != normalClass) { value.i = (int)strtol(buffer, null, eCON ? 0 : 10); result = success; } + else + result = typeMismatch; if(result == success && type.type == unitClass) { @@ -1264,9 +1978,12 @@ private: refProp = true; else { - Class c = eSystem_FindClass(type.module, prop.name); + Class c; + mutexTemplateInstanceFix.Wait(); + c = eSystem_FindClass(type.module, prop.name); if(!c) c = eSystem_FindClass(type.module.application, prop.name); + mutexTemplateInstanceFix.Release(); if(c) { Property p; @@ -1306,7 +2023,7 @@ private: return result; } - JSONResult GetColorAlpha(String string, DataValue value) + JSONResult GetColorAlpha(const String string, DataValue value) { ColorAlpha color = 0; DefinedColor c = 0; @@ -1314,9 +2031,15 @@ private: { if(string[0] == '0' && string[1] == 'x') color = (uint)strtoul(string, null, 0); + else if(string[0] == '#') + color = (uint)strtoul(string+1, null, 16); + else if((strlen(string) == 6 || strlen(string) == 8) && ishexdigit(string[0])) + { + color = (Color)strtoul(string, null, 16); + } else { - char *d; + const char *d; byte a = 255; if((d = strchr(string, ','))) { @@ -1339,54 +2062,94 @@ private: } } -static bool WriteMap(File f, Class type, Map map, int indent, bool eCON) +static bool WriteMap(File f, Class type, Map map, int indent, bool eCON, Map stringMap, JSONFirstLetterCapitalization capitalize) { if(map) { int i; bool isFirst = true; - MapIterator it { map = map }; + Class arrayType = (type = map._class, type.templateArgs[0].dataTypeClass); + const String tArg = strchr(arrayType.name, '<'); + bool spacing = eCON || (tArg && (strchr(tArg + 1, '<') || strstr(tArg + 1, "GeometryData") || strstr(tArg + 1, "UMSFieldValue") || + strstr(tArg + 1, "FlexyField"))); + MapIterator it { map = (void*)map }; Class mapNodeClass = map._class.templateArgs[0].dataTypeClass; - f.Puts("[\n"); - indent++; + bool jsonDicMap = false; + if(mapNodeClass && mapNodeClass.templateClass && eClass_IsDerived(mapNodeClass.templateClass, class(MapNode))) + { + Class mapKeyClass = mapNodeClass.templateArgs[0].dataTypeClass; + Class mapDataClass = mapNodeClass.templateArgs[2].dataTypeClass; + // TOCHECK: When would we *not* want this JSON dictionary behavior for a Map? + // TODO: Reverse the default behavior and identify any case where we do not want this. + jsonDicMap = (!eCON && ( + !strcmp(mapKeyClass.name, "UMSFormatType") || + strstr(mapDataClass.name, "UMSFieldValue") || + (!strcmp(mapKeyClass.name, "String") && !strcmp(mapDataClass.name, "String")) || + strstr(mapDataClass.name, "Array") || + strstr(mapDataClass.name, "MapboxGLSourceData") || + strstr(mapDataClass.name, "ProcessingInput") || + strstr(mapDataClass.name, "FlexyField") || + strstr(mapDataClass.name, "JSONSchema") + ) + ); + } + + if(jsonDicMap) + f.Puts(spacing ? "{\n" : "{ "); + else + f.Puts(spacing ? "[\n" : "[ "); + if(spacing) indent++; while(it.Next()) { MapNode n = (MapNode)it.pointer; + Class ot = n && mapNodeClass.type == normalClass ? ((Instance)n)._class : mapNodeClass; if(!isFirst) - f.Puts(",\n"); + f.Puts(spacing ? ",\n" : ", "); else isFirst = false; - for(i = 0; i stringMap, JSONFirstLetterCapitalization capitalize) { if(array) { int i; bool isFirst = true; Iterator it { array }; - Class arrayType = type.templateArgs[0].dataTypeClass; - f.Puts("[\n"); - indent++; - - while(it.Next()) + Class arrayType = type.templateArgs[0].dataTypeClass ? type.templateArgs[0].dataTypeClass : + eClass_IsDerived(type, class(Container)) && eClass_IsDerived(array._class, type) ? array._class.templateArgs[0].dataTypeClass : null; + const String tName = arrayType ? (arrayType.templateClass ? arrayType.templateClass.name : arrayType.name) : ""; + bool spacing = compactArrays.Find(tName) == null; + f.Puts(spacing ? "[\n" : "[ "); + if(spacing) indent++; + + while(arrayType && it.Next()) { DataValue value { }; uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer); if(!isFirst) - f.Puts(",\n"); + f.Puts(spacing ? ",\n" : ", "); else isFirst = false; @@ -1431,12 +2194,17 @@ static bool WriteArray(File f, Class type, Container array, int indent, bool eCO { value.p = (void *)(uintptr)t; } - for(i = 0; i stringMap, bool useHex, bool jsonBitClass, bool forceQuotes) { char buffer[1024]; - bool needClass = eCON; + ObjectNotationType onType = eCON ? econ : json; bool quote; buffer[0] = 0; if(type == class(double) || !strcmp(type.dataTypeString, "double")) - ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass); + ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &onType); else if(type == class(float) || !strcmp(type.dataTypeString, "float")) - ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &needClass); + ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &onType); else if(!strcmp(type.dataTypeString, "int64")) - ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &needClass); + ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &onType); else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64)) { if(useHex && eCON) sprintf(buffer, __runtimePlatform == win32 ? "0x%016I64X" : "0x%016llX", value.ui64); else - ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &needClass); + ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &onType); } else if(!strcmp(type.dataTypeString, "int")) - ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &needClass); + ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &onType); else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int)) { if(useHex && eCON) sprintf(buffer, "0x%08X", value.ui); else - ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &needClass); + ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &onType); } else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16")) - ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &needClass); + ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &onType); else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int)) - ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &needClass); + ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &onType); else if(!strcmp(type.dataTypeString, "char")) - ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass); + ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &onType); else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte)) - ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass); + ((const char *(*)(void *, void *, char *, void *, ObjectNotationType *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &onType); - quote = (type.type == unitClass && ((buffer[0] != '.' && buffer[0] != '-' && !isdigit(buffer[0])) || strchr(buffer, ' '))) || - (type.type == enumClass && !eCON); + quote = forceQuotes || (!jsonBitClass && ((type.type == unitClass && buffer[0] != '\"' && ((buffer[0] != '.' && buffer[0] != '-' && !isdigit(buffer[0])) || strchr(buffer, ' '))) || + (type.type == enumClass && !eCON))); if(quote) f.Puts("\""); // TODO: Review / Clarify / Document how needClass should work - else if(needClass && type.type == bitClass) f.Puts("{ "); + else if((onType == econ || (onType == json && jsonBitClass)) && type.type == bitClass) f.Puts("{ "); f.Puts(buffer); if(quote) f.Puts("\""); - else if(needClass && type.type == bitClass) f.Puts(" }"); + else if((onType == econ || (onType == json && jsonBitClass)) && type.type == bitClass) f.Puts(" }"); return true; } -public bool WriteColorAlpha(File f, Class type, DataValue value, int indent, bool eCON) +static bool WriteColorAlpha(File f, Class type, DataValue value, int indent, bool eCON) { char tmpColorString[1024], output[1024]; ColorAlpha color = value.ui; DefinedColor c = color.color; - bool needBrackets = false, needQuotes = false, needClass = true; - const String s = c.class::OnGetString(tmpColorString, null, eCON ? &needClass : null); + bool needBrackets = false, needQuotes = false; + ObjectNotationType onType = eCON ? econ : json; + const String s = c.class::OnGetString(tmpColorString, null, &onType); if(s) { if(color.a == 255) @@ -1521,177 +2290,232 @@ public bool WriteColorAlpha(File f, Class type, DataValue value, int indent, boo return true; } -static bool WriteValue(File f, Class type, DataValue value, int indent, bool eCON) +public bool WriteONString(File f, const String s, bool eCON, int indent) { - if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *")) + if(!s) + f.Puts("null"); + else { - if(!value.p) - f.Puts("null"); - else + f.Puts("\""); { - f.Puts("\""); + int c = 0; + int b = 0; + char buffer[1024]; + const char * string = s; + char ch; + while(true) { - int c = 0; - int b = 0; - char buffer[1024]; - char * string = value.p; - char ch; - while(true) + ch = string[c++]; + if(ch == '\"') { - ch = string[c++]; - if(ch == '\"') - { - buffer[b] = 0; - f.Puts(buffer); - f.Puts("\\\""); - b = 0; - } - else if(ch == '\\') - { - buffer[b] = 0; - f.Puts(buffer); - f.Puts("\\\\"); - b = 0; - } - else if(eCON && ch == '\t') - { - buffer[b] = 0; - f.Puts(buffer); - f.Puts("\\t"); - b = 0; - } - else if(eCON && ch == '\n') + buffer[b] = 0; + f.Puts(buffer); + f.Puts("\\\""); + b = 0; + } + else if(ch == '\\') + { + buffer[b] = 0; + f.Puts(buffer); + f.Puts("\\\\"); + b = 0; + } + else if(ch == '\t') + { + buffer[b] = 0; + f.Puts(buffer); + f.Puts("\\t"); + b = 0; + } + else if(ch == '\r') + { + buffer[b] = 0; + f.Puts(buffer); + f.Puts("\\r"); + b = 0; + } + else if(ch == '\f') + { + buffer[b] = 0; + f.Puts(buffer); + f.Puts("\\f"); + b = 0; + } + else if(ch == '\n') + { + int i; + buffer[b] = 0; + f.Puts(buffer); + if(eCON) { - int i; - buffer[b] = 0; - f.Puts(buffer); f.Puts("\\n\"\n"); - for(i = 0; i= 4 && ch == '>' && string[c-2] == 'r' && string[c-3] == 'b' && string[c-4] == '<') - { - // Add an automatic newline for
as this is how we imported documentor data... - int i; - buffer[b] = 0; - f.Puts(buffer); - f.Puts(">\"\n"); - for(i = 0; i= 4 && ch == '>' && string[c-2] == 'r' && string[c-3] == 'b' && string[c-4] == '<') + { + // Add an automatic newline for
as this is how we imported documentor data... + int i; + buffer[b] = 0; + f.Puts(buffer); + f.Puts(">\"\n"); + for(i = 0; i stringMap, bool forceQuotes, JSONFirstLetterCapitalization capitalize) +{ + if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *")) + WriteONString(f, value.p, eCON, indent); else if(!strcmp(type.name, "bool")) f.Puts(value.i ? "true" : "false"); else if(!strcmp(type.name, "SetBool")) f.Puts(value.i == SetBool::true ? "true" : value.i == SetBool::false ? "false" : "unset"); else if(type.type == enumClass) - WriteNumber(f, type, value, indent, eCON, false); + WriteNumber(f, type, value, indent, eCON, stringMap, false, false, forceQuotes); else if(eClass_IsDerived(type, class(Map))) - WriteMap(f, type, value.p, indent, eCON); + WriteMap(f, type, value.p, indent, eCON, stringMap, capitalize); else if(eClass_IsDerived(type, class(Container))) - WriteArray(f, type, value.p, indent, eCON); + WriteArray(f, type, value.p, indent, eCON, stringMap, capitalize); else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass) - WriteONObject(f, type, value.p, indent, eCON, false, null); + { + Class ot = value.p && type.type == normalClass ? ((Instance)value.p)._class : type; + bool omitNames = false; + if(eCON) + { + omitNames = type.type == structClass && type.members.count < 5 && + !strstr(type.name, "GeometryData") && !strstr(type.name, "Transform") && !strstr(type.name, "Euler") && + (type.members.count == type.membersAndProperties.count || + !strcmp(type.name, "GeoExtent") || !strcmp(type.name, "GeoPoint") || !strcmp(type.name, "UMSRowsSpecs") || + !strcmp(type.name, "Vector3D") || !strcmp(type.name, "Vector3Df")); + } + WriteONObject(f, ot, value.p, indent, eCON, stringMap, omitNames, capitalize, null); + } else if(eClass_IsDerived(type, class(ColorAlpha))) WriteColorAlpha(f, type, value, indent, eCON); else if(type.type == bitClass) { - if(eCON) - WriteNumber(f, type, value, indent, true, false); + if(eCON || !strcmp(type.name, "MapDataType")) + WriteNumber(f, type, value, indent, eCON, stringMap, false, true, forceQuotes); else - WriteNumber(f, superFindClass(type.dataTypeString, type.module), value, indent, false, true); + WriteNumber(f, superFindClass(type.dataTypeString, type.module), value, indent, false, stringMap, true, false, forceQuotes); } else if(type.type == systemClass || type.type == unitClass) - WriteNumber(f, type, value, indent, eCON, false); + WriteNumber(f, type, value, indent, eCON, stringMap, false, false, forceQuotes); return true; } -public bool WriteJSONObject(File f, Class objectType, void * object, int indent) -{ - bool result = false; - if(object) - { - result = WriteONObject(f, objectType, object, indent, false, false, null); - f.Puts("\n"); - } - return result; -} - -public bool WriteECONObject(File f, Class objectType, void * object, int indent) -{ - bool result = false; - if(object) - { - result = WriteONObject(f, objectType, object, indent, true, false, null); - f.Puts("\n"); - } - return result; -} - -static bool WriteONObject(File f, Class objectType, void * object, int indent, bool eCON, bool omitDefaultIdentifier, Container forMap) +static bool WriteONObject(File f, Class objectType, void * object, int indent, bool eCON, Map stringMap, bool omitDefaultIdentifier, JSONFirstLetterCapitalization capitalize, Container forMap) { + const String tName = objectType.templateClass ? objectType.templateClass.name : objectType.name; + bool spacing = compactTypes.Find(tName) == null; if(object) { const char * string = null; + bool quote = true; + ObjectNotationType onType = eCON ? econ : json; if(objectType._vTbl[__ecereVMethodID_class_OnGetString] != objectType.base._vTbl[__ecereVMethodID_class_OnGetString]) { - char buffer[1024]; + char buffer[16384]; // TODO: Improve OnGetString() to support returning dynamic memory that must be freed buffer[0] = 0; - string = ((const char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, null); + string = ((const char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, &onType); + quote = false; } if(string) { // TOCHECK: ProjectNode.ec why do we add quotes in OnGetString there? - if(string[0] == '\"') - f.Puts(string); - else { - f.Puts("\""); + if(quote) f.Puts("\""); f.Puts(string); - f.Puts("\""); + if(quote) f.Puts("\""); } } + else if (eClass_IsDerived(objectType,class(Array))) + { + // A file containing only [ 1, 2, 3 ] is supported by the parser so the writer should too. + // Why would we want a (JS|eC)ON file with the array internals rather than its contents? + WriteArray(f, objectType, (Container)object, indent, eCON, stringMap, capitalize); + } + else if (eClass_IsDerived(objectType,class(Map))) + { + // If it is a Map, cut to the right call + WriteMap(f, objectType, (Map)object, indent, eCON, stringMap, capitalize); + } else { - Class _class = (eCON && objectType.type == normalClass) ? ((Instance)object)._class : objectType; + Class _class = (objectType.type == normalClass) ? ((Instance)object)._class : objectType; Property prop; int c; bool isFirst = true; Class mapKeyClass = null, mapDataClass = null; Class baseClass; List bases { }; + bool cantOmit = false; + Class jsonClass = null; + bool jsonDicMap = false; if(objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode))) { mapKeyClass = objectType.templateArgs[0].dataTypeClass; mapDataClass = objectType.templateArgs[2].dataTypeClass; + // TOCHECK: When would we *not* want this JSON dictionary behavior for a Map? + jsonDicMap = (!eCON && ( + !strcmp(mapKeyClass.name, "UMSFormatType") || + strstr(mapDataClass.name, "UMSFieldValue") || + (!strcmp(mapKeyClass.name, "String") && !strcmp(mapDataClass.name, "String")) || + strstr(mapDataClass.name, "Array") || + strstr(mapDataClass.name, "MapboxGLSourceData") || + strstr(mapDataClass.name, "FlexyField") || + strstr(mapDataClass.name, "ProcessingInput") || + strstr(mapDataClass.name, "JSONSchema") + ) + ); } - if(eCON && _class != objectType && eClass_IsDerived(_class, objectType)) + if(_class && _class.bindingsClass) _class = _class.base; + if(_class != objectType && eClass_IsDerived(_class, objectType)) { - f.Puts(_class.name); - f.Puts(" "); + if(eCON) + { + f.Puts(_class.name); + f.Puts(" "); + } + else + jsonClass = _class; } - f.Puts("{\n"); - indent++; + if(!jsonDicMap) + f.Puts(spacing ? "{\n" : omitDefaultIdentifier ? "{" : "{ "); + if(spacing) indent++; + + if(jsonClass) + { + if(spacing) for(c = 0; c stringMap) +{ + /* + * Print the JSON representation of an object to a file. + * NOTE: The 'indent' parameter is kept for backwards compatibiity reasons, but + * it shouild always be passed as 0 (zero). + * */ + bool result = false; + if(object) + { + result = WriteONObject(f, objectType, object, indent, false, stringMap, false, upperCase, null); + f.Puts("\n"); + } + return result; +} + +public String PrintECONObject(Class objectType, void * object, int indent) +{ + /* + * Print the ECON representation of an object to a string. + * NOTE: This function allocates memory for the output String + * NOTE: The 'indent' parameter is kept for backwards compatibiity reasons, but + * it shouild always be passed as 0 (zero). + * */ + String result = null; + if(object) + { + TempFile f { }; + if(WriteONObject(f, objectType, object, indent, true, null, false, upperCase, null)) + { + f.Putc(0); + result = (String)f.StealBuffer(); + } + delete f; + } + return result; +} + +public String PrintObjectNotationString(Class objectType, void * object, ObjectNotationType onType, int indent, bool indentFirst, JSONFirstLetterCapitalization capitalize) +{ + /* + * Print the ECON or JSON representation of an object to a string. + * An mount of (indent * jsonIndentWidth) whitespaces are inserted after each '\n', + * if 'indentFirst' is true, the same amount is prepended to the first character of the object representation. + * NOTE: This function allocates memory for the output String, that must be managed in the caller. + * */ + String result = null; + if(object) + { + TempFile f { }; + if(WriteONObject(f, objectType, object, 0, onType == econ, null, false, 0, null)) + { + f.Putc('\0'); + if(indent>0) + result = StringIndent((String)f.buffer, indent * jsonIndentWidth, indentFirst); + else + result = (String)f.StealBuffer(); + } + delete f; + } + return result; +} + +public String StringIndent(const String base, int nSpaces, bool indentFirst) { - Class _class = superFindClass(name, type.module); - if(eClass_IsDerived(_class, type)) - return true; - return false; +/* + * Allocate a new String and fill it with a copy of the 'base', with 'nSpaces' of + * indentation added after each '\n' character. + * + * If 'indentfirst' is true, the indentation is also added before the first char of 'base'. + * If 'base' points to null, act as if it were the empty string "". + * + * Return a pointer to the newly allocated String. + * */ + String target = null; + if(base) + { + int basePos = 0; + int targetPos = 0; + int targetLen = (strlen(base) + nSpaces) * 2; + + char * indent = new char[nSpaces + 1]; + memset(indent, ' ', nSpaces), indent[nSpaces] = '\0'; + target = new char[targetLen + 1]; + + if(indentFirst) + { + strcpy(target, indent); + targetPos += nSpaces; + } + for(basePos = 0; base[basePos]; ++basePos, ++targetPos) + { + if(targetLen - targetPos <= nSpaces) + { + targetLen *= 2; + target = renew target char[targetLen + 1]; + } + target[targetPos] = base[basePos]; + if(base[basePos] == '\n') + { + strcpy(target + targetPos + 1, indent); + targetPos += nSpaces; + } + } + target[targetPos] = '\0'; + delete indent; + } + else + { + target = StringIndent("", nSpaces, indentFirst); + } + return target; } + diff --git a/ecere/src/sys/Semaphore.ec b/ecere/src/sys/Semaphore.ec index 6d582cc034..e57addd5fd 100644 --- a/ecere/src/sys/Semaphore.ec +++ b/ecere/src/sys/Semaphore.ec @@ -94,6 +94,8 @@ public: #else result = sem_trywait(&semaphore) != EAGAIN; #endif +#else + result = false; #endif return result; } @@ -170,7 +172,7 @@ public: #if !defined(__EMSCRIPTEN__) #if defined(__WIN32__) if(handle) CloseHandle(handle); - handle = CreateSemaphore(null, value, maxCount, null); + handle = CreateSemaphore(null, 0, value, null); #endif #endif maxCount = value; diff --git a/ecere/src/sys/System.c b/ecere/src/sys/System.c index d3c9ae3d48..717e038a89 100644 --- a/ecere/src/sys/System.c +++ b/ecere/src/sys/System.c @@ -238,7 +238,9 @@ char * System_GetWorkingDir(char * buf, int size) bool System_ChangeWorkingDir(const char * buf) { -#if defined(__WIN32__) +#if defined(__UWP__) + return false; +#elif defined(__WIN32__) bool result; uint16 * _wbuf = __ecereNameSpace__ecere__sys__UTF8toUTF16(buf, null); result = !chdir(buf); @@ -364,7 +366,7 @@ bool System_ShellOpen(const char * fileName, va_list args) #if !defined(__WIN32__) { - char command[MAX_LOCATION] = ""; + char command[sizeof(filePath) + 61] = ""; char desktop[MAX_F_STRING]; __ecereNameSpace__ecere__sys__GetEnvironment("ECERE_DESKTOP", desktop, sizeof(desktop)); if(__ecereNameSpace__ecere__sys__SearchString(desktop, 0, "ecere", false, false)) @@ -373,9 +375,9 @@ bool System_ShellOpen(const char * fileName, va_list args) { __ecereNameSpace__ecere__sys__GetEnvironment("DESKTOP_SESSION", desktop, sizeof(desktop)); if(__ecereNameSpace__ecere__sys__SearchString(desktop, 0, "gnome", false, false)) - sprintf(command, "gnome-open \"%s\" &", filePath); + sprintf(command, "$(which gnome-open || which gvfs-open || which xdg-open) \"%s\" &", filePath); else if(__ecereNameSpace__ecere__sys__SearchString(desktop, 0, "kde", false, false)) - sprintf(command, "kde-open \"%s\" &", filePath); + sprintf(command, "$(which kde-open || which xdg-open) \"%s\" &", filePath); else sprintf(command, "xdg-open \"%s\" &", filePath); } @@ -383,7 +385,7 @@ bool System_ShellOpen(const char * fileName, va_list args) if(command[0] && system(command) != -1) result = true; } -#elif defined(ECERE_VANILLA) +#elif defined(ECERE_VANILLA) || defined(__UWP__) { uint16 * _wfilePath = __ecereNameSpace__ecere__sys__UTF8toUTF16(filePath, null); if(_wsystem(_wfilePath) != -1) diff --git a/ecere/src/sys/System.ec b/ecere/src/sys/System.ec index 9dd5afa715..dc788f08da 100644 --- a/ecere/src/sys/System.ec +++ b/ecere/src/sys/System.ec @@ -26,6 +26,7 @@ default: #include #include +#include #endif @@ -234,7 +235,7 @@ public bool ExecuteEnv(const char * env, const char * command, ...) va_end(args); return result; } - +// WARNING: This takes a printf format string! Document this properly. public bool ShellOpen(const char * fileName, ...) { bool result; @@ -517,3 +518,17 @@ private struct System }; System globalSystem; + +public void debugBreakpoint() +{ +#if !defined(ECERE_BOOTSTRAP) && defined(_DEBUG) + +#if defined(__WIN32__) + DebugBreak(); +#else + raise(SIGTRAP); + // asm("int $3"); +#endif + +#endif +} diff --git a/ecere/src/sys/TempFile.ec b/ecere/src/sys/TempFile.ec index 79cdce197e..f193023e85 100644 --- a/ecere/src/sys/TempFile.ec +++ b/ecere/src/sys/TempFile.ec @@ -24,7 +24,7 @@ public class TempFile : File uintsize read = Min(readSize, this.size - position); if(position >= this.size) eof = true; - if(buffer) memcpy(buffer, this.buffer + position, read); + if(buffer && read) memcpy(buffer, this.buffer + position, read); position += read; @@ -44,10 +44,10 @@ public class TempFile : File this.allocated *= 2; if(this.allocated < this.size) this.allocated = this.size * 2; - this.buffer = renew this.buffer byte[this.allocated]; + this.buffer = renew0 this.buffer byte[this.allocated]; } } - memcpy(this.buffer + position, buffer, writeSize); + if(writeSize) memcpy(this.buffer + position, buffer, writeSize); position += written; @@ -158,7 +158,11 @@ public class TempFile : File if(increase) { this.size += increase; - this.buffer = renew this.buffer byte[this.size]; + if(this.size > this.allocated) + { + this.allocated = this.size; + this.buffer = renew0 this.buffer byte[this.size]; + } } return result; } @@ -193,5 +197,8 @@ public: set { openMode = value; } get { return openMode; } } - property byte * buffer { get { return buffer; } }; + property byte * buffer { get { return buffer; } set { delete buffer; buffer = value; } }; + property uintsize size { get { return size; } set { size = value; } } + property uintsize allocated { get { return allocated; } set { allocated = value; } } + byte * StealBuffer() { byte * result = buffer; buffer = null; return result; } }; diff --git a/ecere/src/sys/Time.ec b/ecere/src/sys/Time.ec index 5ff62ddc06..31fa9d2125 100644 --- a/ecere/src/sys/Time.ec +++ b/ecere/src/sys/Time.ec @@ -1,3 +1,9 @@ +default: +extern char *tzname[2]; +extern long timezone; +extern int daylight; + +private: namespace sys; #define _Noreturn @@ -21,6 +27,9 @@ namespace sys; #define WIN32_LEAN_AND_MEAN #define String String_ #include + +#include + #undef String #include #elif defined(__unix__) || defined(__APPLE__) @@ -212,7 +221,7 @@ import "System" public class Time : double { - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { Time time = this; int value; @@ -235,7 +244,7 @@ public class Time : double } value = (int)(time / 60); - sprintf(temp, "%d:", value); + sprintf(temp, "%02d:", value); strcat(tempString, temp); time -= value * 60; @@ -251,14 +260,39 @@ public class Time : double }*/ return tempString; } + + bool OnGetDataFromString(const char * string) + { + bool result = false; + char s[100], * tokens[20]; + int count, i, multiplier = 1; + Time t = 0; + + strncpy(s, string ? string : "", sizeof(s)); + s[99] = 0; + count = TokenizeWith(s, 20, tokens, ":", false); + + // handles d:h:m:s, h:m:s, m:s, or s + for(i = count-1; i >= 0; i--) + { + result = true; + t += multiplier * strtol(tokens[i], null, 10); + if(multiplier == 60 * 60 * 24) break; + multiplier *= (multiplier == 60 * 60) ? 24 : 60; + } + this = t; + return result; + } } public class Seconds : Time { public property Time {} }; #if !defined(__WIN32__) -static time_t MakeTimeT(SecSince1970 t) + +static time_t MakeTimeTfromLocal(SecSince1970 t, int isDST) { - struct tm tm; + // mktime() takes time expressed in *local* time + struct tm tm = { 0 }; time_t result; DateTime dt = t; tm.tm_year = dt.year - 1900; @@ -269,13 +303,15 @@ static time_t MakeTimeT(SecSince1970 t) tm.tm_sec = dt.second; tm.tm_yday = dt.dayInTheYear; tm.tm_wday = dt.dayOfTheWeek; + tm.tm_isdst = isDST; result = mktime(&tm); return result; } -static time_t MakeTimeTfromDT(DateTime dt) +static time_t MakeTimeTfromLocalDT(DateTime dt, int isDST) { - struct tm tm; + // mktime() takes time expressed in *local* time + struct tm tm = { 0 }; time_t result; tm.tm_year = dt.year - 1900; tm.tm_mon = dt.month; @@ -285,17 +321,28 @@ static time_t MakeTimeTfromDT(DateTime dt) tm.tm_sec = dt.second; tm.tm_yday = dt.dayInTheYear; tm.tm_wday = dt.dayOfTheWeek; + tm.tm_isdst = isDST; result = mktime(&tm); return result; } +static time_t MakeTimeTfromGlobal(SecSince1970 t, bool isDST) +{ + return (time_t)(t - isDST * 3600); +} + +static time_t MakeTimeTfromGlobalDT(DateTime dt, bool isDST) +{ + return (time_t)((SecSince1970)dt - isDST * 3600); +} + #endif public class SecSince1970 : int64 { - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { - return ((DateTime)this).OnGetString(tempString, fieldData, needClass); + return ((DateTime)this).OnGetString(tempString, fieldData, onType); } bool OnGetDataFromString(const char * string) @@ -359,10 +406,9 @@ public: return global; #else - struct tm tm; + struct tm tm = { 0 }; DateTime global; - time_t t = MakeTimeT(this); - // gmtime_r((time_t *)&this, &tm); + time_t t = MakeTimeTfromLocal(this, -1); gmtime_r(&t, &tm); global.year = tm.tm_year + 1900; global.month = (Month)tm.tm_mon; @@ -408,8 +454,8 @@ public: return local; #else DateTime local; - struct tm tm; - time_t t = MakeTimeT(this); + struct tm tm = { 0 }; + time_t t = MakeTimeTfromGlobal(this, false); //localtime_r((time_t *)&this, &tm); localtime_r(&t, &tm); local.year = tm.tm_year + 1900; @@ -429,9 +475,9 @@ public: public class TimeStamp32 : uint32 { public: - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { - return ((DateTime)(TimeStamp)this).OnGetString(tempString, fieldData, needClass); + return ((DateTime)(TimeStamp)this).OnGetString(tempString, fieldData, onType); } // Is this required? @@ -481,7 +527,7 @@ public struct DateTime dayOfTheWeek = date.dayOfTheWeek; } #else - struct tm tm; + struct tm tm = { 0 }; time_t currentTime = time(null); localtime_r(¤tTime, &tm); @@ -532,9 +578,9 @@ public struct DateTime value.minute = systemTime.wMinute; value.second = systemTime.wSecond; #else - struct tm tm; + struct tm tm = { 0 }; //time_t t = (time_t)(SecSince1970)this; - time_t t = MakeTimeTfromDT(this); + time_t t = MakeTimeTfromLocalDT(this, -1); gmtime_r(&t, &tm); value.year = tm.tm_year + 1900; value.month = (Month)tm.tm_mon; @@ -575,9 +621,9 @@ public struct DateTime value.FixDayOfYear(); #else - struct tm tm; + struct tm tm = { 0 }; // time_t t = (time_t)(SecSince1970)this; - time_t t = MakeTimeTfromDT(this); + time_t t = MakeTimeTfromGlobalDT(this, false); localtime_r(&t, &tm); value.year = tm.tm_year + 1900; value.month = (Month)tm.tm_mon; @@ -671,7 +717,7 @@ public struct DateTime get { value = Date { year, month, day }; } } - const char * OnGetString(char * stringOutput, void * fieldData, bool * needClass) + const char * OnGetString(char * stringOutput, void * fieldData, ObjectNotationType * onType) { static const char ampm[2][3] = { "AM", "PM" }; int hour = this.hour; @@ -681,10 +727,18 @@ public struct DateTime if(!hour) hour = 12; if(!year && !day && !month && !this.hour && !minute && !second) - stringOutput[0] = 0; + { + if(onType && (*onType == json || *onType == econ)) + strcpy(stringOutput, "null"); + else + stringOutput[0] = 0; + } else - sprintf(stringOutput, "%s %s %2d %2d:%02d:%02d %s %04d", - shortDaysNames[dayOfTheWeek], shortMonthsNames[month], day, hour, minute, second, ampm[pm], year); + { + const String quotes = (onType && (*onType == json || *onType == econ)) ? "\"" : ""; + sprintf(stringOutput, "%s%s %s %2d %2d:%02d:%02d %s %04d%s", + quotes, shortDaysNames[dayOfTheWeek], shortMonthsNames[month], day, hour, minute, second, ampm[pm], year, quotes); + } return stringOutput; } @@ -721,10 +775,12 @@ public struct DateTime if(strchr(tokens[c], ':')) { - char * subTokens[20]; - int sCount = TokenizeWith(tokens[c], 20, subTokens, " :", false); int t; bool pm = false, am = false; + char * subTokens[20]; + char * timeAfterDate = strchr(tokens[c], 'T'); + // If there is a 'T' the time string starts after its position + int sCount = TokenizeWith( (timeAfterDate)?timeAfterDate+1:tokens[c], 20, subTokens, " :", false); for(t = 0; t> moduleMaps { }; public dllexport void LoadTranslatedStrings(const String moduleName, const char * name) { -#if !defined(ECERE_NOFILE) && !defined(__EMSCRIPTEN__) +#if !defined(ECERE_NOFILE) && !defined(__EMSCRIPTEN__) && !defined(__LUMIN__) File f; char fileName[MAX_LOCATION]; diff --git a/ecere/src/sys/units.ec b/ecere/src/sys/units.ec index bbcd8d2169..3cf3683f68 100644 --- a/ecere/src/sys/units.ec +++ b/ecere/src/sys/units.ec @@ -4,10 +4,11 @@ import "instance" public struct Point { int x, y; }; public struct Pointf { float x, y; }; +public struct Pointd { double x, y; }; public class MinMaxValue : int { - const char * OnGetString(char * string, void * fieldData, bool * needClass) + const char * OnGetString(char * string, void * fieldData, ObjectNotationType * onType) { if(this == MAXINT) sprintf(string, "Inf"); @@ -114,6 +115,13 @@ public class Radians : Angle public class Degrees : Angle { + const char * OnGetString(char * tempString, void * reserved, ObjectNotationType * onType) + { + // Optimization + // double d = this; // FIXME, also directly calling OnGetString()... + double d = *(Degrees *)&this; + return d.OnGetString(tempString, reserved, onType); + } public property Radians { //get { return (Angle)this * Pi / 180; } diff --git a/eda/Makefile b/eda/Makefile index a8e6e2a604..13bdde8a69 100644 --- a/eda/Makefile +++ b/eda/Makefile @@ -1,18 +1,35 @@ ifneq ($(V),1) .SILENT: endif -.PHONY: all cmd cleantarget clean realclean distclean + +ifndef DISABLE_EDA_SQLITE + EDASQLite := defined +endif + +ifndef DISABLE_EDA_SQLITE + EDAdBASE := defined +endif + +.PHONY: all cleantarget clean realclean wipeclean distclean _CF_DIR = ../ include $(_CF_DIR)crossplatform.mk include $(_CF_DIR)default.cf +.NOTPARALLEL: $(NOT_PARALLEL_TARGETS) + all: @$(call echo,Building Ecere Data Access layer...) +cd libeda && $(_MAKE) +ifdef EDAdBASE + @$(call echo,Building EDAdBASE driver...) + +cd drivers && cd dbase && $(_MAKE) +endif +ifdef EDASQLite @$(call echo,Building EDASQLite driver...) +cd drivers && cd sqlite && $(_MAKE) +endif ifdef EDASQLiteCipher @$(call echo,Building EDASQLiteCipher driver...) +cd drivers && cd sqliteCipher && $(_MAKE) @@ -20,28 +37,49 @@ endif cleantarget: +cd libeda && $(_MAKE) cleantarget +ifdef EDAdBASE + +cd drivers && cd dbase && $(_MAKE) cleantarget +endif +ifdef EDASQLite +cd drivers && cd sqlite && $(_MAKE) cleantarget +endif ifdef EDASQLiteCipher +cd drivers && cd sqliteCipher && $(_MAKE) cleantarget endif clean: +cd libeda && $(_MAKE) clean +ifdef EDAdBASE + +cd drivers && cd dbase && $(_MAKE) clean +endif +ifdef EDASQLite +cd drivers && cd sqlite && $(_MAKE) clean +endif ifdef EDASQLiteCipher +cd drivers && cd sqliteCipher && $(_MAKE) clean endif realclean: +cd libeda && $(_MAKE) realclean +ifdef EDAdBASE + +cd drivers && cd dbase && $(_MAKE) realclean +endif +ifdef EDASQLite +cd drivers && cd sqlite && $(_MAKE) realclean +endif ifdef EDASQLiteCipher +cd drivers && cd sqliteCipher && $(_MAKE) realclean endif +wipeclean: + +cd libeda && $(_MAKE) wipeclean + +cd drivers && cd dbase && $(_MAKE) wipeclean + +cd drivers && cd sqlite && $(_MAKE) wipeclean + +cd drivers && cd sqliteCipher && $(_MAKE) wipeclean + distclean: - $(MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs + $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/eda/drivers/dbase/EDAdBASE.ec b/eda/drivers/dbase/EDAdBASE.ec index d7f37927ef..fe860ccb40 100644 --- a/eda/drivers/dbase/EDAdBASE.ec +++ b/eda/drivers/dbase/EDAdBASE.ec @@ -619,7 +619,7 @@ class dBaseTableRow : dBaseRow } break; case 'L': // Logical (true or false) - *(bool *)data = fldData[0] == 'T'; + *(int64 *)data = fldData[0] == 'T'; result = fldData[0] == 'T' || fldData[0] == 'F'; break; case 'N': // Numbers (as strings) @@ -627,7 +627,7 @@ class dBaseTableRow : dBaseRow if(!field.info.decimalCount) { char * p; - *(int *)data = strtol(fldData, &p, 10); + *(int64 *)data = strtoll(fldData, &p, 10); result = p > fldData; // Why do we have **** for 0? break; } @@ -656,7 +656,7 @@ class dBaseTableRow : dBaseRow case '+': // (Autoincrement) { int * iPtr = (int *)fldData; - *(int *)data = *iPtr; + *(int64 *)data = *iPtr; result = true; break; } diff --git a/eda/drivers/dbase/Makefile b/eda/drivers/dbase/Makefile new file mode 100644 index 0000000000..72a4485ee3 --- /dev/null +++ b/eda/drivers/dbase/Makefile @@ -0,0 +1,208 @@ +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean + +# CORE VARIABLES + +MODULE := EDAdBASE +CONFIG := release +ifndef COMPILER +COMPILER := default +endif + +TARGET_TYPE = sharedlib + +# FLAGS + +ECFLAGS = +ifndef DEBIAN_PACKAGE +CFLAGS = +LDFLAGS = +endif +PRJ_CFLAGS = +CECFLAGS = +OFLAGS = +LIBS = + +ifdef DEBUG +NOSTRIP := y +endif + +CONSOLE = -mwindows + +# INCLUDES + +_CF_DIR = ../../../ + +include $(_CF_DIR)crossplatform.mk +include $(_CF_DIR)default.cf + +# POST-INCLUDES VARIABLES + +OBJ = obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ + +RES = + +ifdef LINUX_TARGET +TARGET = obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDAdBASE$(SO).0.44 +SONAME = -Wl,-soname,$(LP)EDAdBASE$(SO).0 +else +TARGET = obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/$(LP)EDAdBASE$(SO) +SONAME = +endif + +_ECSOURCES = \ + EDAdBASE.ec + +ECSOURCES = $(call shwspace,$(_ECSOURCES)) + +COBJECTS = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(C),$(notdir $(_ECSOURCES))))) + +SYMBOLS = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(S),$(notdir $(_ECSOURCES))))) + +IMPORTS = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(I),$(notdir $(_ECSOURCES))))) + +ECOBJECTS = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(O),$(notdir $(_ECSOURCES))))) + +BOWLS = $(call shwspace,$(addprefix $(OBJ),$(patsubst %.ec,%$(B),$(notdir $(_ECSOURCES))))) + +OBJECTS = $(ECOBJECTS) $(OBJ)$(MODULE).main$(O) + +SOURCES = $(ECSOURCES) + +SYMBOLS = $(call shwspace,$(_SYMBOLS)) + +IMPORTS = $(call shwspace,$(_IMPORTS)) + +ECOBJECTS = $(call shwspace,$(_ECOBJECTS)) + +BOWLS = $(call shwspace,$(_BOWLS)) + +OBJECTS = $(ECOBJECTS) $(OBJ)$(MODULE).main$(O) + +SOURCES = $(ECSOURCES) + +RESOURCES = + +LIBS += $(SHAREDLIB) $(EXECUTABLE) $(LINKOPT) + +ifndef STATIC_LIBRARY_TARGET +LIBS += \ + $(call _L,ecere) +endif + +PRJ_CFLAGS += \ + $(if $(DEBUG), -g, -O2 -ffast-math) $(FPIC) -Wall -DREPOSITORY_VERSION="\"$(REPOSITORY_VER)\"" + +ECFLAGS += -module $(MODULE) +CECFLAGS += -cpp $(_CPP) + +ifndef STATIC_LIBRARY_TARGET +OFLAGS += \ + -L../../../obj/$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/bin \ + -L../../../obj/$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/lib +endif + +# TARGETS + +all: objdir $(TARGET) + +objdir: + $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(if $(ECERE_SDK_SRC),$(if $(wildcard $(call escspace,$(ECERE_SDK_SRC)/crossplatform.mk)),,@$(call echo,Ecere SDK Source Warning: The value of ECERE_SDK_SRC is pointing to an incorrect ($(ECERE_SDK_SRC)) location.)),) + $(if $(ECERE_SDK_SRC),,$(if $(ECP_DEBUG)$(ECC_DEBUG)$(ECS_DEBUG),@$(call echo,ECC Debug Warning: Please define ECERE_SDK_SRC before using ECP_DEBUG, ECC_DEBUG or ECS_DEBUG),)) + +$(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS) + @$(call rm,$(OBJ)symbols.lst) + @$(call touch,$(OBJ)symbols.lst) + $(call addtolistfile,$(SYMBOLS),$(OBJ)symbols.lst) + $(call addtolistfile,$(IMPORTS),$(OBJ)symbols.lst) + $(ECS) $(ARCH_FLAGS) $(ECSLIBOPT) @$(OBJ)symbols.lst -symbols obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX) -o $(call quote_path,$@) + +$(OBJ)$(MODULE).main.c: $(OBJ)$(MODULE).main.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c $(OBJ)$(MODULE).main.ec -o $(OBJ)$(MODULE).main.sym -symbols $(OBJ) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.ec -o $(call quote_path,$@) -symbols $(OBJ) + +$(SYMBOLS): | objdir +$(OBJECTS): | objdir +$(TARGET): $(SOURCES) $(RESOURCES) $(SYMBOLS) $(OBJECTS) | objdir + @$(call rm,$(OBJ)objects.lst) + @$(call touch,$(OBJ)objects.lst) + $(call addtolistfile,$(OBJ)$(MODULE).main$(O),$(OBJ)objects.lst) + $(call addtolistfile,$(ECOBJECTS),$(OBJ)objects.lst) +ifndef STATIC_LIBRARY_TARGET + $(CC) $(OFLAGS) @$(OBJ)objects.lst $(LIBS) -o $(TARGET) $(INSTALLNAME) +ifndef NOSTRIP + $(STRIP) $(STRIPOPT) $(TARGET) +endif +else +ifdef WINDOWS_HOST + $(AR) rcs $(TARGET) @$(OBJ)objects.lst $(LIBS) +else + $(AR) rcs $(TARGET) $(OBJECTS) $(LIBS) +endif +endif + $(call cp,$(TARGET),../../../$(SODESTDIR)) +ifdef SHARED_LIBRARY_TARGET +ifdef LINUX_TARGET +ifdef LINUX_HOST + $(if $(basename $(VER)),ln -sf $(LP)$(MODULE)$(SO)$(VER) $(OBJ)$(LP)$(MODULE)$(SO)$(basename $(VER)),) + $(if $(VER),ln -sf $(LP)$(MODULE)$(SO)$(VER) $(OBJ)$(LP)$(MODULE)$(SO),) +endif +endif +endif + +# SYMBOL RULES + +$(OBJ)EDAdBASE.sym: EDAdBASE.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c $(call quote_path,EDAdBASE.ec) -o $(call quote_path,$@) + +# C OBJECT RULES + +$(OBJ)EDAdBASE.c: EDAdBASE.ec $(OBJ)EDAdBASE.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,EDAdBASE.ec) -o $(call quote_path,$@) -symbols $(OBJ) + +# OBJECT RULES + +$(OBJ)EDAdBASE$(O): $(OBJ)EDAdBASE.c + $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,$(OBJ)EDAdBASE.c) -o $(call quote_path,$@) + +$(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c + $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.c -o $(call quote_path,$@) + +cleantarget: + $(call rm,$(OBJ)$(MODULE).main$(O) $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) + $(call rm,$(OBJ)symbols.lst) + $(call rm,$(OBJ)objects.lst) + $(call rm,$(TARGET)) +ifdef SHARED_LIBRARY_TARGET +ifdef LINUX_TARGET +ifdef LINUX_HOST + $(call rm,$(OBJ)$(LP)$(MODULE)$(SO)$(basename $(VER))) + $(call rm,$(OBJ)$(LP)$(MODULE)$(SO)) +endif +endif +endif + +clean: cleantarget + $(call rm,$(_OBJECTS)) + $(call rm,$(ECOBJECTS)) + $(call rm,$(COBJECTS)) + $(call rm,$(BOWLS)) + $(call rm,$(IMPORTS)) + $(call rm,$(SYMBOLS)) + +realclean: cleantarget + $(call rmr,$(OBJ)) + +wipeclean: + $(call rmr,obj/) + +distclean: + $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs + +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/eda/drivers/sqlite/EDASQLite.ec b/eda/drivers/sqlite/EDASQLite.ec index 0ee27e7a9f..b38e25df60 100644 --- a/eda/drivers/sqlite/EDASQLite.ec +++ b/eda/drivers/sqlite/EDASQLite.ec @@ -6,14 +6,16 @@ public import "ecere" public import "EDA" #endif -#ifdef __linux__ +#if defined(__linux__) && !defined(__LUMIN__) #include #else #include "sqlite3.h" #endif #define uint _uint +#if !defined(__LUMIN__) #include "ffi.h" +#endif #undef uint __attribute__((unused)) static void UnusedFunction() @@ -40,6 +42,11 @@ extern int __ecereVMethodID_class_OnUnserialize; extern int __ecereVMethodID_class_OnFree; private: +static SerialBuffer collationBuffer1 { }; +static SerialBuffer collationBuffer2 { }; +static char storage1[512]; +static char storage2[512]; + int CollationCompare(Class type, int count1, const void * data1, int count2, const void * data2) { if(type.type == normalClass || type.type == noHeadClass) @@ -66,11 +73,30 @@ int CollationCompare(Class type, int count1, const void * data1, int count2, con { void * inst1, * inst2; int result; - SerialBuffer buffer1 { size = count1, count = count1, buffer = (byte *)data1 }; - SerialBuffer buffer2 { size = count2, count = count2, buffer = (byte *)data2 }; - - inst1 = new0 byte[type.structSize]; - inst2 = new0 byte[type.structSize]; + //SerialBuffer buffer1 { size = count1, count = count1, buffer = (byte *)data1 }; + //SerialBuffer buffer2 { size = count2, count = count2, buffer = (byte *)data2 }; + + SerialBuffer buffer1 = collationBuffer1; + SerialBuffer buffer2 = collationBuffer2; + buffer1.buffer = (byte*)data1; + buffer1.size = count1; + buffer1.count = count1; + buffer1.pos = 0; + buffer2.buffer = (byte*)data2; + buffer2.size = count2; + buffer2.count = count2; + buffer2.pos = 0; + + if(type.structSize > 512) + { + inst1 = new0 byte[type.structSize]; + inst2 = new0 byte[type.structSize]; + } + else + { + inst1 = storage1; + inst2 = storage2; + } ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, inst1, buffer1); ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, inst2, buffer2); @@ -78,10 +104,13 @@ int CollationCompare(Class type, int count1, const void * data1, int count2, con buffer1.buffer = null; buffer2.buffer = null; - delete buffer1; - delete buffer2; - delete inst1; - delete inst2; + //delete buffer1; + //delete buffer2; + if(type.structSize > 512) + { + delete inst1; + delete inst2; + } return result; } else @@ -119,6 +148,7 @@ class SQLiteDataSource : DirFilesDataSourceDriver char command[1024]; sqlite3_exec(db, "PRAGMA page_size=4096;", null, null, null); + sqlite3_exec(db, "PRAGMA synchronous=off;", null, null, null); sprintf(command, "CREATE TABLE eda_table_fields(Table_Name TEXT, Name TEXT, Type TEXT, Length INT);"); sqlite3_exec(db, command, null, null, null); @@ -413,7 +443,7 @@ class SQLiteDatabase : Database bool CreateCustomFunction(const char * name, SQLCustomFunction customFunction) { bool result = false; -#if !defined(__EMSCRIPTEN__) +#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) Class cfClass = customFunction._class; customFunction.method = eClass_FindMethod(cfClass, "function", cfClass.module); if(customFunction.method) @@ -496,7 +526,7 @@ __attribute__((unused)) static Iterator dummy; // TOFIX: forward struct declarat public ffi_type * FFIGetType(Class type, bool structByValue) { -#if !defined(__EMSCRIPTEN__) +#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) if(type) switch(type.type) { @@ -504,7 +534,7 @@ public ffi_type * FFIGetType(Class type, bool structByValue) case structClass: if(structByValue) { - MapIterator it { map = structFFITypes }; + MapIterator it { map = (void*)structFFITypes }; ffi_type * ffiType = null; if(it.Index(type, false)) ffiType = (void *)it.data; @@ -557,7 +587,7 @@ public ffi_type * FFIGetType(Class type, bool structByValue) static SerialBuffer staticBuffer { }; void SQLiteFunctionProcessor(sqlite3_context* context, int n, sqlite3_value** values) { -#if !defined(__EMSCRIPTEN__) +#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) SQLCustomFunction sqlFunction = sqlite3_user_data(context); /* // Simple 1 pointer param returning a string @@ -950,7 +980,7 @@ class SQLiteTable : Table bool GenerateIndex(int count, FieldIndex * fieldIndexes, bool init) { - char command[1024]; + char command[4096 + 64]; int c; int result; char indexName[4096]; @@ -1068,7 +1098,7 @@ class SQLiteTable : Table DriverRow CreateRow() { - char command[1024]; + char command[1024*2]; sqlite3_stmt * statement; sqlite3_stmt * sysIDStmt = null, * insertStmt = null, * deleteStmt = null, * selectRowIDsStmt = null, * setRowIDStmt = null; sqlite3_stmt * prevStmt = null, * nextStmt = null, * lastStmt = null, * insertIDStmt = null; diff --git a/eda/drivers/sqlite/EDASQLite.epj b/eda/drivers/sqlite/EDASQLite.epj index 207c47e9f8..ddc54199b6 100644 --- a/eda/drivers/sqlite/EDASQLite.epj +++ b/eda/drivers/sqlite/EDASQLite.epj @@ -109,6 +109,47 @@ ], "TargetType" : "StaticLibrary", "TargetFileName" : "EDASQLiteStatic", + "CompilerOptions" : [ + "-mmmx", + "-msse", + "-msse2", + "-msse3", + "-msse4" + ], + "FastMath" : true + } + }, + { + "Name" : "Android", + "Options" : { + "NoLineNumbers" : true, + "Optimization" : "Speed", + "FastMath" : true + }, + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "NoLineNumbers" : true, + "Optimization" : "Speed", + "Libraries" : [ + "pthread", + "dl" + ] + } + } + ] + }, + { + "Name" : "Lumin", + "Options" : { + "NoLineNumbers" : true, + "Optimization" : "Speed", + "PreprocessorDefinitions" : [ + "ECERE_STATIC" + ], + "TargetType" : "StaticLibrary", + "TargetFileName" : "EDASQLiteLuminStatic", "CompilerOptions" : [ "-mmmx", "-msse3", @@ -131,6 +172,30 @@ "ExcludeFromBuild" : true } } + ], + "Configurations" : [ + { + "Name" : "Android", + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "ExcludeFromBuild" : false + } + } + ] + }, + { + "Name" : "Lumin", + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "ExcludeFromBuild" : false + } + } + ] + } ] }, { @@ -144,8 +209,38 @@ } ] }, - "EDASQLite.ec", - "sqliteDB.ec" + { + "FileName" : "EDASQLite.ec", + "Configurations" : [ + { + "Name" : "Android", + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "FastMath" : true + } + } + ] + } + ] + }, + { + "FileName" : "sqliteDB.ec", + "Configurations" : [ + { + "Name" : "Android", + "Platforms" : [ + { + "Name" : "linux", + "Options" : { + "FastMath" : true + } + } + ] + } + ] + } ], "ResourcesPath" : "", "Resources" : [ diff --git a/eda/drivers/sqlite/Makefile b/eda/drivers/sqlite/Makefile index 877520b884..1a6555b93c 100644 --- a/eda/drivers/sqlite/Makefile +++ b/eda/drivers/sqlite/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -105,8 +109,13 @@ CUSTOM1_PRJ_CFLAGS = \ -DSQLITE_OMIT_AUTHORIZATION ECFLAGS += -module $(MODULE) -ECFLAGS += \ - -nolinenumbers +ifndef DEBUG +ECFLAGS += -nolinenumbers +endif + +ifdef DISABLE_MEMMGR +ECFLAGS += -DDISABLE_MEMMGR +endif # PLATFORM-SPECIFIC OPTIONS @@ -159,7 +168,7 @@ endif all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS) $(ECS) $(ARCH_FLAGS) $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX) -o $(OBJ)$(MODULE).main.ec @@ -182,6 +191,8 @@ else endif $(call cp,$(TARGET),../../../$(SODESTDIR)) ifdef LINUX_TARGET + ln -sf $(LP)$(MODULE)$(SO).0.44 $(OBJ)$(LP)$(MODULE)$(SO).0 + ln -sf $(LP)$(MODULE)$(SO).0.44 $(OBJ)$(LP)$(MODULE)$(SO) ln -sf $(LP)$(MODULE)$(SO).0.44 ../../../$(SODESTDIR)$(LP)$(MODULE)$(SO).0 ln -sf $(LP)$(MODULE)$(SO).0.44 ../../../$(SODESTDIR)$(LP)$(MODULE)$(SO) endif @@ -216,7 +227,7 @@ $(OBJ)EDASQLite.o: $(OBJ)EDASQLite.c $(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.c -o $(OBJ)$(MODULE).main$(O) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -241,9 +252,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/eda/drivers/sqlite/sqliteDB.ec b/eda/drivers/sqlite/sqliteDB.ec index ec2c2543f7..b2177a6b4b 100644 --- a/eda/drivers/sqlite/sqliteDB.ec +++ b/eda/drivers/sqlite/sqliteDB.ec @@ -10,10 +10,12 @@ public import "EDA" #define __restrict +#ifndef ECERE_STATIC #include static regex_t regex; static String lastRegex; +#endif static void sqlite_regexp(sqlite3_context * context, int argc, sqlite3_value ** values) { @@ -82,7 +84,10 @@ public: { SQLiteDB result = null; sqlite3 * db; - SQLiteResult r = (SQLiteResult)sqlite3_open_v2(path, &db, readOnly ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0)), null); + bool isArchive = path[0] == '<' || path[0] == ':'; + SQLiteResult r = (SQLiteResult)sqlite3_open_v2(path, &db, + readOnly || isArchive ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0)), + isArchive ? "ecere" : null); if(r == ok) { result = eInstance_New(_class ? _class : class(SQLiteDB)); @@ -122,12 +127,13 @@ public: bool begin() { inBegin = true; return exec("BEGIN;") == done; } bool commit() { inBegin = false; return exec("COMMIT;") == done; } property const String lockingMode { set { execf("PRAGMA locking_mode=%s; BEGIN IMMEDIATE; COMMIT;", value); } } + int64 lastInsertRowID() { return sqlite3_last_insert_rowid(db); } ~SQLiteDB() { +#ifndef ECERE_STATIC // TOFIX: Doing this here now delete lastRegex; -#ifndef ECERE_STATIC regfree(®ex); #endif @@ -154,11 +160,22 @@ public class SQLiteStmt : struct public: property SQLiteDB db { set { db = value; } get { return db; } } - property const String query { set { if(stmt) finalize(); sqlite3_prepare_v2(db.db, value, -1, &stmt, null); } } + property const String query + { + set + { + if(stmt) finalize(); + sqlite3_prepare_v2(db.db, value, -1, &stmt, null); +#ifdef _DEBUG + if(!stmt) + printf($"SQLite Error preparing statement:\n %s\n", value); +#endif + } + } ~SQLiteStmt() { finalize(); } - void queryf(const String format, ...) + bool queryf(const String format, ...) { char cmd[MAX_F_STRING]; va_list args; @@ -167,9 +184,10 @@ public: cmd[sizeof(cmd)-1] = 0; query = cmd; va_end(args); + return stmt != null; } - void reset() { sqlite3_reset(stmt); } - void finalize() { sqlite3_finalize(stmt); stmt = null; } + void reset() { if(stmt) sqlite3_reset(stmt); } + void finalize() { if(stmt) sqlite3_finalize(stmt); stmt = null; } SQLiteResult step() { return (SQLiteResult)sqlite3_step(stmt); } void bind_null(int pos) { sqlite3_bind_null(stmt, pos); } @@ -184,13 +202,21 @@ public: sqlite3_bind_null(stmt, pos); } + void bind_blob(int pos, const void * d, uint size) + { + if(d) + sqlite3_bind_blob(stmt, pos, d, size, SQLITE_TRANSIENT); + else + sqlite3_bind_null(stmt, pos); + } + void bind_value(int pos, FieldValue value) { // TODO: 64 bit ints? switch(value.type.type) { case nil: bind_null (pos); break; - case integer: bind_int (pos, value.i); break; + case integer: bind_int64 (pos, value.i); break; case real: bind_double(pos, value.r); break; case text: bind_text (pos, value.s); break; } @@ -201,6 +227,7 @@ public: int64 column_int64(int pos) { return sqlite3_column_int64(stmt, pos); } double column_double(int pos) { return sqlite3_column_double(stmt, pos); } int column_bytes(int pos) { return sqlite3_column_bytes(stmt, pos); } + const void * column_blob(int pos) { return (const void *)sqlite3_column_blob(stmt, pos); } const String column_text(int pos) { return (const String)sqlite3_column_text(stmt, pos); } String column_text_copy(int pos) { @@ -215,10 +242,170 @@ public: { switch(column_type(pos)) { - case integer: value = { { integer }, i = column_int (pos) }; break; + case integer: value = { { integer }, i = column_int64 (pos) }; break; case real: value = { { real }, r = column_double (pos) }; break; case text: value = { { text, true }, s = column_text_copy(pos) }; break; default: value = { { nil } }; break; } } } + +// Virtual File System to access SQLite databases inside Ecere archives +static int ecereClose(sqlite3_file *pFile) +{ + File f = *(File *)(pFile+1); + delete f; + return SQLITE_OK; +} + +static int ecereRead(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst) +{ + int result = SQLITE_IOERR_READ; + File f = *(File *)(pFile+1); + if(f.Seek(iOfst, start)) + { + int nRead = f.Read(zBuf, 1, iAmt); + if(nRead == iAmt) + result = SQLITE_OK; + else if(nRead >= 0) + { + if(nRead < iAmt) + memset((char*)zBuf + nRead, 0, iAmt - nRead); + result = SQLITE_IOERR_SHORT_READ; + } + } + return result; +} + +static int ecereWrite(sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst) { return SQLITE_IOERR_WRITE; } +static int ecereTruncate(sqlite3_file *pFile, sqlite_int64 size) { return SQLITE_IOERR_TRUNCATE; } +static int ecereSync(sqlite3_file *pFile, int flags) { return SQLITE_OK; } +static int ecereFileSize(sqlite3_file *pFile, sqlite_int64 *pSize) +{ + File f = *(File *)(pFile+1); + *pSize = f.GetSize(); + return SQLITE_OK; +} + +static int ecereLock(sqlite3_file *pFile, int eLock) { return SQLITE_OK; } +static int ecereUnlock(sqlite3_file *pFile, int eLock) { return SQLITE_OK; } +static int ecereCheckReservedLock(sqlite3_file *pFile, int *pResOut) { *pResOut = 0; return SQLITE_OK; } +static int ecereFileControl(sqlite3_file *pFile, int op, void *pArg) { return SQLITE_NOTFOUND; } +static int ecereSectorSize(sqlite3_file *pFile) { return 0; } +static int ecereDeviceCharacteristics(sqlite3_file *pFile) { return 0; } + +static int ecereOpen(sqlite3_vfs *pVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags) +{ + int result = SQLITE_IOERR; + static const sqlite3_io_methods ecereIO = + { + 1, + ecereClose, + ecereRead, + ecereWrite, + ecereTruncate, + ecereSync, + ecereFileSize, + ecereLock, + ecereUnlock, + ecereCheckReservedLock, + ecereFileControl, + ecereSectorSize, + ecereDeviceCharacteristics + }; + if(zName) + { + File * fp = (File *)(pFile+1); + *fp = FileOpen(zName, read); + if(*fp) + { + result = SQLITE_OK; + if( pOutFlags ) + *pOutFlags = SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_READONLY; + pFile->pMethods = &ecereIO; + } + else + result = SQLITE_CANTOPEN; + } + return result; +} + +static int ecereDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync) +{ + return SQLITE_IOERR_DELETE; +} + +static int ecereAccess(sqlite3_vfs *pVfs, const char *zPath, int flags, int *pResOut) +{ + *pResOut = FileExists(zPath) ? 1 : 0; + return SQLITE_OK; +} + +static int ecereFullPathname(sqlite3_vfs *pVfs, const char *zPath, int nPathOut, char *zPathOut) +{ + char zDir[MAX_FILENAME]; + zDir[0] = 0; + PathCat(zDir, zPath); + strncpy(zPathOut, zDir, nPathOut-1); + zPathOut[nPathOut-1] = 0; + return SQLITE_OK; +} + +static void *ecereDlOpen(sqlite3_vfs *pVfs, const char *zPath) { return 0; } +static void ecereDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg) +{ + sqlite3_snprintf(nByte, zErrMsg, "Loadable extensions are not supported"); + zErrMsg[nByte-1] = '\0'; +} +static void (*ecereDlSym(sqlite3_vfs *pVfs, void *pH, const char *z))(void) { return 0; } +static void ecereDlClose(sqlite3_vfs *pVfs, void *pHandle) { } +static int ecereRandomness(sqlite3_vfs *pVfs, int nByte, char *zByte) { return SQLITE_OK; } + +static int ecereSleep(sqlite3_vfs *pVfs, int nMicro) +{ + Sleep(nMicro / 1000000.0); + return nMicro; +} + +static int ecereCurrentTime(sqlite3_vfs *pVfs, double *pTime) +{ + *pTime = GetTime(); + return SQLITE_OK; +} + +static sqlite3_vfs ecereVFS = +{ + 1, + sizeof(sqlite3_file) + sizeof(File), + MAX_FILENAME-1, + 0, + "ecere", + 0, + ecereOpen, + ecereDelete, + ecereAccess, + ecereFullPathname, + ecereDlOpen, + ecereDlError, + ecereDlSym, + ecereDlClose, + ecereRandomness, + ecereSleep, + ecereCurrentTime +}; + +__on_register_module() +{ + sqlite3_vfs_register(&ecereVFS, 0); +} + +static class SQLiteDBUnregisterModule +{ + ~SQLiteDBUnregisterModule() + { + sqlite3_vfs_unregister(&ecereVFS); + } +} + +// FIXME: No __on_unregister_module() ? +static SQLiteDBUnregisterModule unregisterModule { }; diff --git a/eda/drivers/sqliteCipher/Makefile b/eda/drivers/sqliteCipher/Makefile index 1144080738..92c3b3a9e5 100644 --- a/eda/drivers/sqliteCipher/Makefile +++ b/eda/drivers/sqliteCipher/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean openssl_debug +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean openssl_debug # CORE VARIABLES @@ -103,8 +107,13 @@ CUSTOM1_PRJ_CFLAGS = \ -DSQLITE_HAS_CODEC ECFLAGS += -module $(MODULE) -ECFLAGS += \ - -nolinenumbers +ifndef DEBUG +ECFLAGS += -nolinenumbers +endif + +ifdef DISABLE_MEMMGR +ECFLAGS += -DDISABLE_MEMMGR +endif # PLATFORM-SPECIFIC OPTIONS @@ -159,7 +168,7 @@ endif all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS) $(ECS) $(ARCH_FLAGS) $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX) -o $(OBJ)$(MODULE).main.ec @@ -182,6 +191,8 @@ else endif $(call cp,$(TARGET),../../../$(SODESTDIR)) ifdef LINUX_TARGET + ln -sf $(LP)$(MODULE)$(SO).0.44 $(OBJ)$(LP)$(MODULE)$(SO).0 + ln -sf $(LP)$(MODULE)$(SO).0.44 $(OBJ)$(LP)$(MODULE)$(SO) ln -sf $(LP)$(MODULE)$(SO).0.44 ../../../$(SODESTDIR)$(LP)$(MODULE)$(SO).0 ln -sf $(LP)$(MODULE)$(SO).0.44 ../../../$(SODESTDIR)$(LP)$(MODULE)$(SO) endif @@ -223,7 +234,7 @@ openssl_debug: @$(call echo,OPENSSL_BIN_DIR $(OPENSSL_BIN_DIR)) @$(call echo,OPENSSL_LIB_DIR $(OPENSSL_LIB_DIR)) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -248,9 +259,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/eda/libeda/Makefile b/eda/libeda/Makefile index 793d85d208..765002d764 100644 --- a/eda/libeda/Makefile +++ b/eda/libeda/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -93,11 +97,17 @@ endif PRJ_CFLAGS += \ $(if $(WINDOWS_TARGET), \ -I../../deps/libffi-3.0.11/i686-pc-mingw32/include,) \ - $(if $(DEBUG), -g, -O2 -ffast-math) $(FPIC) -w + $(if $(DEBUG), -g, -O2 -ffast-math) $(FPIC) -Wall ECFLAGS += -module $(MODULE) -ECFLAGS += \ - -nolinenumbers -defaultns eda +ECFLAGS += -defaultns eda +ifndef DEBUG +ECFLAGS += -nolinenumbers +endif + +ifdef DISABLE_MEMMGR +ECFLAGS += -DDISABLE_MEMMGR +endif # PLATFORM-SPECIFIC OPTIONS @@ -125,7 +135,7 @@ endif all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS) $(ECS) $(ARCH_FLAGS) $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX) -o $(OBJ)$(MODULE).main.ec @@ -148,6 +158,8 @@ else endif $(call cp,$(TARGET),../../$(SODESTDIR)) ifdef LINUX_TARGET + ln -sf $(LP)$(MODULE)$(SO).0.44 $(OBJ)$(LP)$(MODULE)$(SO).0 + ln -sf $(LP)$(MODULE)$(SO).0.44 $(OBJ)$(LP)$(MODULE)$(SO) ln -sf $(LP)$(MODULE)$(SO).0.44 ../../$(SODESTDIR)$(LP)$(MODULE)$(SO).0 ln -sf $(LP)$(MODULE)$(SO).0.44 ../../$(SODESTDIR)$(LP)$(MODULE)$(SO) endif @@ -251,7 +263,7 @@ $(OBJ)fieldValue$(O): $(OBJ)fieldValue.c $(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.c -o $(OBJ)$(MODULE).main$(O) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -276,9 +288,12 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/eda/libeda/src/EDB.ec b/eda/libeda/src/EDB.ec index a130a5359a..a88b8d0b5b 100644 --- a/eda/libeda/src/EDB.ec +++ b/eda/libeda/src/EDB.ec @@ -171,7 +171,15 @@ static struct EAREntry static class EDBArchive : Archive { + // NOTE: This needs to be kept in sync with ecere::sys::EARArchive which is currently private +private: File f; + + // Data layout mismatches for 'f' were happening without these as well: + uint64 archiveStart; + uint64 rootDir; + OldList freeBlocks; + bool writeAccess; }; static class EDBArchiveDir : ArchiveDir diff --git a/eda/libeda/src/ers.ec b/eda/libeda/src/ers.ec index 6548f86ade..9619758af0 100644 --- a/eda/libeda/src/ers.ec +++ b/eda/libeda/src/ers.ec @@ -477,6 +477,9 @@ private: } else { + if(destination._class == class(CSVReport)) + detail.creationActivation = activate; // Work around to maintain proper order in exported reports + detail.anchor = Anchor { left = 0, top = pageTop, right = 0 }; pageTop += detailSize; @@ -956,6 +959,8 @@ public: bool isLast; int level; + // NOTE: Not activating messed up order for CSV export mode, but will be turned off in AddDetailToPage(). + // Does it speed things up in preview / print? creationActivation = doNothing; noConsequential = true; diff --git a/eda/libeda/src/fieldValue.ec b/eda/libeda/src/fieldValue.ec index 3d94b5adc3..9efaf20743 100644 --- a/eda/libeda/src/fieldValue.ec +++ b/eda/libeda/src/fieldValue.ec @@ -13,11 +13,21 @@ public enum FieldType // Note: these match SQLiteType nil = 5 }; +// TOCHECK: Move this into FieldTypeEx ? +public enum FieldValueFormat +{ + // boolean allows treating a value as a boolean, writing it as either true or false + // each type is allowed to disregard this and use a default format instead + decimal, hex, octal, binary, exponential, boolean +}; + public class FieldTypeEx : FieldType { public: FieldType type:3; bool mustFree:1; + FieldValueFormat format:3; + bool isUnsigned:1; }; public struct FieldValue @@ -25,7 +35,7 @@ public struct FieldValue FieldTypeEx type; union { - int i; + int64 i; double r; String s; void * b; @@ -71,9 +81,14 @@ public struct FieldValue char temp[128]; switch(type.type) { - case integer: sprintf(temp, "%d", i); return CopyString(temp); - case real: return CopyString(r.OnGetString(temp, null, null)); - case text: return s; + case integer: + formatInteger(temp, i, type.format, type.isUnsigned); + return CopyString(temp); + case real: + formatFloat(temp, r, type.format, false); + return CopyString(temp); + case text: + return s; } return null; } @@ -101,9 +116,589 @@ public struct FieldValue } } + void OnCopy(FieldValue b) + { + this = b; + if(type.type == text && type.mustFree) + s = CopyString(b.s); + } + void OnFree() { if(type.mustFree) delete s; } + + const char * OnGetString(char * stringOutput, void * fieldData, ObjectNotationType * onType) + { + switch(type.type) + { + case integer: + return formatInteger(stringOutput, i, type.format, type.isUnsigned); + case real: + { + return formatFloat(stringOutput, r, type.format, true); + } + case text: + { + if(onType && *onType != none) + { + TempFile f { }; + WriteONString(f, s, *onType == econ, 0); + f.Putc('\0'); // make f.buffer into a null-terminated string + strcpy(stringOutput, (const char *)f.buffer); + delete f; + } + else + sprintf(stringOutput, "\"%s\"", s); + return stringOutput; + } + case nil: return "null"; + } + stringOutput[0] = 0; + return stringOutput; + } + + bool OnGetDataFromString(const char * string) + { + if(string[0] == '\"') + { + int len = strlen(string + 1); + if(len > 0) len--; + s = new char[len + 1]; + UnescapeCString(s, string+1, len); + type = { text, true }; + return true; + } + else if(!strcmpi(string, "null")) + { + type = { nil }; + s = null; + return true; + } + else if(!strcmpi(string, "false")) + { + type = { type=integer, format = boolean}; + i = 0; + return true; + } + else if(!strcmpi(string, "true")) + { + type = { type=integer, format = boolean}; + i = 1; + return true; + } + else if(strchr(string, '.') || strchr(string, 'E')) + { + type = { real }; + r = strtod(string, null); + return true; + } + else + { + type = { integer }; + i = (int64) strtoll(string, null, 0); + return true; + } + return false; + } +}; + +// BEGIN ::: Formating functions shared between FieldValue and FlexyField + +String formatFloat(char * stringOutput, double r, FieldValueFormat format, bool fixDot) +{ + switch (format) + { + // For now boolean is not active for float numbers, and uses the default + /* case boolean: sprintf(stringOutput, "%s", (r)?"true":"false"); break; */ + case exponential: + sprintf(stringOutput, "%e", r); + return stringOutput; + default: + { + String st = (String)r.OnGetString(stringOutput, null, null); + if(fixDot && !strchr(st, '.') && !strchr(st, 'E') && !strchr(st, 'e')) + strcat(st, ".0"); + return st; + } + } +} + +String formatInteger(char * stringOutput, int64 i, FieldValueFormat format, bool isUnsigned) +{ + switch(format) + { + // case binary: sprintf(stringOutput, "%b", i); break; // TODO: proper binary support + case octal: sprintf(stringOutput, "%o", (uint)i); break; + case hex: sprintf(stringOutput, FORMAT64HEX, i); break; + case boolean: sprintf(stringOutput, "%s", (i)?"true":"false"); break; + default: sprintf(stringOutput, isUnsigned ? FORMAT64U : FORMAT64D, i); break; + } + return stringOutput; +} + +// END ::: Formating functions shared between FieldValue and FlexyField + +// Add array and map to the enumeration of known field types. +// Note that the new values are not compatible with SQLiteType objects. +public enum FlexyType : FieldType { + array, // The blob points to an Array object + map // the blob points to a Map object +}; + + +public class FlexyTypeEx : FlexyType +{ +public: + FlexyType type:3; + bool mustFree:1; + FieldValueFormat format:3; + bool isUnsigned:1; +}; + + +class MapGetHelper { + // Little helper class to read a Map object from string in + // FlexyField OnGetDataFromString. +public: + property Map M {get{return M;} set{M = value;}}; +private: + Map M ; +} + + +public struct FlexyField +{ + FlexyTypeEx type; + union + { + int64 i; + double r; + String s; + void * b; + Array a; + Map m; + }; +public: + + // Access to the union members, will be hndled via via properties. + // The setter can help to ensure that the value of type.type and type.mustFree are consistent. + // Setting a new value for one of the pointer union members [s,m,b,a] does + // not delete the previous from memory, since it could well be referenced + // elsewhere. + // + // Note: be careful when adding a FlexyField to the m attribute of another FlexyField, as in: + // + // Array an_array {} + // FlexyField a {a=an_array}; + // Map a_map {}; + // a_map["key"] = a; + // FlexyField b {m=a_map}; + // delete a; // deallocates an_array, but b.m["key"] still points at it. + // delete b; // attempts to call OnFree() with the deleted b.m["key"]. + // + // The values of type can be altered after assignment if necessary (eg: set + // type = {nil, false}, since there is no property for nil values, or set + // mustFree to false for a string that we know is referenced elsewhere): + // + // FlexyField f {s=aString}; + // f.type.mustFree = false + // FlexyField n {b=null, {nil, false}}; + // + // Trying to get data using the wrong property (eg: using s when + // type.type==array) should return 0, 0.0 or null, according to the property type. + + // Property to access data as pointer to void: + property void * b { + get{ return (type.type == blob) ? b : null;} + set{b = value; type = {blob, true};} + isset { return type.type == blob && b != null;} + } + + // Property to access data as String (aka char *): + property String s { + get{ return (type.type == text) ? s : null;} + set{ s = value; type = {text, true};} + isset { return type.type == text && s != null;} + } + // Property to access data as integer: + property int64 i { + get{ return (type.type == integer) ? i : 0;} + set{ i = value; type = {integer, false};} + isset { return type.type == integer;} + } + + // Property to access data as real: + property double r { + get{ return (type.type == real) ? r : 0.0;} + set{ r = value; type = {real, false};} + isset { return type.type == real;} + } + + // Property to access data as array: + property Array a { + get{ return (type.type == array) ? a : null;} + set{ a = value; type = {array, true};} + isset { return type.type == array && a != null;} + } + + // Property to access the data as map: + property Map m { + get{ return (type.type == map) ? m : null;} + set{ m = value; type = {map, true};} + isset { return type.type == map && m != null;} + } + + int OnCompare(FlexyField other) + { + // Return -1, 0, 1 if this is respectively smaller equal or larger than other. + + if(type.type < other.type.type) return -1; + if(type.type > other.type.type) return 1; + switch(type.type) + { + case integer: + return ((i > other.i) - (i < other.i)); + case real: + return ((r > other.r) - (r < other.r)); + case text: + return compareText(other); + case blob: + // At the moment, we assume that all blobs + // are actually text that must be stored verbatim, + // we do not treat bynary data here. + return compareText(other); + case array: + case map: + default: + // We consider arrays and maps to compare equal + // until a sensible ordering is devised. + return 0; + } + return 0; + } + + int compareText(FlexyField other) + { + if(!s && other.s) return -1; + if(s && !other.s) return 1; + if(!s && !other.s) return 0; + return strcmp(s, other.s); + } + + void OnFree() + { + if(type.mustFree) + { + if(type.type == text && s) + { + delete s; + } + else if(type.type == blob) + { + delete b; + } + else if (type.type == array && a) + { + a.OnFree(); + a = null; + } + else if (type.type == map && m) + { + m.OnFree(); + m = null; + } + } + } + + void OnCopy(FlexyField other) + { + this = other; + if(type.mustFree) + { + switch (type.type) + { + case text: + s = CopyString(other.s); + break; + case blob: + // At the moment, we assume that all raw blobs + // are actually text that must be stored verbatim, + // we do not treat binary data here. + s = CopyString((String)other.b); + break; + case array : + { + Array at {}; + int n; + for ( n=0; n< a.count; n++ ) + { + at.Add({}); + at[n].OnCopy(a[n]); + } + a = at; + break; + } + case map: + { + Map mt {}; + MapIterator iter {map = other.m}; + for (iter.Next(); iter.pointer; iter.Next()) + { + const String key = iter.key; + FlexyField val = iter.value; + mt[key] = {}; + mt[key].OnCopy(val); + } + m = mt; + break; + } + } + } + } + + void OnSerialize(IOChannel f) + { + f.Put(type); + switch(type.type) + { + case integer: + f.Put(i); break; + case real: + f.Put(r); break; + case text: + f.Put(s); break; + case blob: + // At the moment, we assume that all blobs + // are actually text that must be stored verbatim, + // we do not treat binary data here. + f.Put((String)b); break; + case array: + a.OnSerialize(f); break; + case map: + m.OnSerialize(f); break; + } + } + + void OnUnserialize(IOChannel f) + { + // First free any allocated space. + this.OnFree(); + f.Get(type); + switch(type.type) + { + case integer: + f.Get(i); + break; + case real: + f.Get(r); + break; + case text: + f.Get(s); + case blob: + // At the moment, we assume that all blobs + // are actually text that must be stored verbatim, + // we do not treat binary data here. + f.Get(s); + break; + case array: + { + Array at {}; + at.OnUnserialize(f); + a = at; + break; + } + case map: + { + Map mt {}; + mt.OnUnserialize(f); + m = mt; + break; + } + default: + r = 0; + break; + } + } + + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) + { + // Arrays are formatted on a single line, for now. + // If a map has up to three elements they are formatted + // on one line, otherwise each on its own. + + tempString[0] = 0; + switch(type.type) + { + case integer: + formatInteger(tempString, i, type.format, type.isUnsigned); + break; + case real: + formatFloat(tempString, r, type.format, true); + break; + case text: + sprintf(tempString, "\"%s\"", s); + break; + case blob: + // At the moment, we assume that all blobs + // are actually text that must be stored verbatim, + // we do not treat binary data here. + strcat(tempString, (String)b); + break; + case 0: // Handle zero values as 'nil' + case nil: + strcat(tempString, "null"); + break; + case array: + formatArray(tempString, fieldData, onType); + break; + case map: + formatMap(tempString, fieldData, onType); + break; + } + return tempString; + } + + String formatArray(char * tempString, void * fieldData, ObjectNotationType * onType) + { + String temp = PrintObjectNotationString( a._class, a, *onType, 0, false, keepCase); + strcat(tempString, temp); + delete temp; + + return tempString; + } + + String formatMap(char * tempString, void * fieldData, ObjectNotationType * onType) + { + // Depending on the object notation and number of elements: + String temp = PrintObjectNotationString( m._class, m, *onType, 0, false, keepCase); + strcat(tempString, temp); + delete temp; + + return tempString; + } + + String stringify() + { + // Return a string representation of the value: + // A new String is allocated and must be deleted by the caller. + // The resulting string may not be suitable for json/econ files. + + char temp[MAX_LOCATION]; + switch(type.type) + { + case integer: + formatInteger(temp, i, type.format, type.isUnsigned); + return CopyString(temp); + case real: + formatFloat(temp, r, type.format, false); + return CopyString(temp); + case text: + return CopyString(s); + case blob: + // At the moment, we assume that all blobs + // are actually text that must be stored verbatim, + // we do not treat binary data here. + return CopyString((String)b); + case array: + case map: + { + ObjectNotationType on = econ; + this.OnGetString(temp, null, &on); + return CopyString(temp); + } + } + return null; + } + + bool OnGetDataFromString(const char * string) + { + bool result = false; + + if(string[0] == '{') + { + MapGetHelper tempMap = null; + String working = new char[strlen(string)+9]; + sprintf(working, "{ M = %s }", string); + + result = getArrayOrMap(working, class(MapGetHelper), (void*)&tempMap); + delete working; + if(result) + { + property::m = tempMap.M; + tempMap.M = null; + } + delete tempMap; + return result; + } + if(string[0] == '[') + { + Array tempArray = null; + result = getArrayOrMap(string, class(Array), (void*)&tempArray); + if(!result) + delete tempArray; + property::a = tempArray; + return result; + } + else if(string[0] == '\"') + { + int len = strlen(string + 1); + if(len > 0) len--; + property::s = new char[len + 1]; + UnescapeCString(s, string+1, len); + return true; + } + else if(!strcmpi(string, "null")) + { + s = null; + type = {nil, false}; + return true; + } + else if(!strcmpi(string, "false")) + { + property::i = 0; + type.format = boolean; + return true; + } + else if(!strcmpi(string, "true")) + { + property::i = 1; + type.format = boolean; + return true; + } + else{ + // Attempt to treat the string as a number + char * rest; + if(strchr(string, '.') || strchr(string, 'E') || strchr(string, 'e') ) + { + property::r = strtod(string, &rest); + } + else + { + property::i = (int64) strtoll(string, &rest, 0); + } + + // If rest points to the start of string, + // this was not a number, so it is a blob. + if((rest == string)) + property::b = CopyString(string); + return true; + } + + } + + bool getArrayOrMap(const char * string, Class destClass, void **destination) + { + bool result = false; + TempFile tmp {buffer = (byte *)string, size = strlen(string)}; + ECONParser parser {tmp}; + result = parser.GetObject(destClass, destination) == success; + delete parser; + // It is the caller's responsibility to delete or keep + // the input string as needed, so we steal it back from tmp. + tmp.StealBuffer(); + delete tmp; + return result; + } }; diff --git a/eda/libeda/src/idList.ec b/eda/libeda/src/idList.ec index 6d168a8f4d..3e96b8f5d1 100644 --- a/eda/libeda/src/idList.ec +++ b/eda/libeda/src/idList.ec @@ -164,7 +164,7 @@ public class Id : uint64 return dropBox; } - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { if(&this) { @@ -219,6 +219,7 @@ public class Id : uint64 } else { + strcpy(tempString, "(error)"); PrintLn("Id::OnGetString -- data type"/*, this._class.name, */" has no class_data(nameField)"); } } @@ -358,7 +359,7 @@ public: } */ - const char * OnGetString(char * stringOutput, void * fieldData, bool * needClass) + const char * OnGetString(char * stringOutput, void * fieldData, ObjectNotationType * onType) { stringOutput[0] = 0; if(this) diff --git a/epj2make/Makefile b/epj2make/Makefile index 1e02373326..cd794f5dbd 100644 --- a/epj2make/Makefile +++ b/epj2make/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean # CORE VARIABLES @@ -39,7 +43,7 @@ include $(_CF_DIR)default.cf OBJ = obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/ -RES = +RES = TARGET = obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)/epj2make$(E) @@ -49,6 +53,7 @@ _ECSOURCES = \ ../ide/src/project/ProjectNode.ec \ ../ide/src/IDESettings.ec \ ../ide/src/OldIDESettings.ec \ + ../ide/src/designer/SyntaxColorScheme.ec \ ../extras/gui/controls/StringsBox.ec \ ../extras/types/DynamicString.ec \ epj2make.ec @@ -88,8 +93,13 @@ PRJ_CFLAGS += \ -DECERE_EPJ2MAKE ECFLAGS += -module $(MODULE) -ECFLAGS += \ - -nolinenumbers +ifndef DEBUG +ECFLAGS += -nolinenumbers +endif + +ifdef DISABLE_MEMMGR +ECFLAGS += -DDISABLE_MEMMGR +endif # PLATFORM-SPECIFIC OPTIONS @@ -110,7 +120,7 @@ endif all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS) $(ECS) -console $(ARCH_FLAGS) $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX) -o $(OBJ)$(MODULE).main.ec @@ -145,6 +155,9 @@ $(OBJ)ProjectConfig.sym: ../ide/src/project/ProjectConfig.ec $(OBJ)ProjectNode.sym: ../ide/src/project/ProjectNode.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c ../ide/src/project/ProjectNode.ec -o $(OBJ)ProjectNode.sym +$(OBJ)SyntaxColorScheme.sym: ../ide/src/designer/SyntaxColorScheme.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c ../ide/src/designer/SyntaxColorScheme.ec -o $(OBJ)SyntaxColorScheme.sym + $(OBJ)IDESettings.sym: ../ide/src/IDESettings.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c ../ide/src/IDESettings.ec -o $(OBJ)IDESettings.sym @@ -174,6 +187,9 @@ $(OBJ)ProjectNode.c: ../ide/src/project/ProjectNode.ec $(OBJ)ProjectNode.sym | $ $(OBJ)IDESettings.c: ../ide/src/IDESettings.ec $(OBJ)IDESettings.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c ../ide/src/IDESettings.ec -o $(OBJ)IDESettings.c -symbols $(OBJ) +$(OBJ)SyntaxColorScheme.c: ../ide/src/designer/SyntaxColorScheme.ec $(OBJ)SyntaxColorScheme.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c ../ide/src/designer/SyntaxColorScheme.ec -o $(OBJ)SyntaxColorScheme.c -symbols $(OBJ) + $(OBJ)OldIDESettings.c: ../ide/src/OldIDESettings.ec $(OBJ)OldIDESettings.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c ../ide/src/OldIDESettings.ec -o $(OBJ)OldIDESettings.c -symbols $(OBJ) @@ -200,6 +216,9 @@ $(OBJ)ProjectNode.o: $(OBJ)ProjectNode.c $(OBJ)IDESettings.o: $(OBJ)IDESettings.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)IDESettings.c -o $(OBJ)IDESettings.o +$(OBJ)SyntaxColorScheme.o: $(OBJ)SyntaxColorScheme.c + $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)SyntaxColorScheme.c -o $(OBJ)SyntaxColorScheme.o + $(OBJ)OldIDESettings.o: $(OBJ)OldIDESettings.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)OldIDESettings.c -o $(OBJ)OldIDESettings.o @@ -215,7 +234,7 @@ $(OBJ)epj2make.o: $(OBJ)epj2make.c $(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.c -o $(OBJ)$(MODULE).main$(O) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -240,9 +259,13 @@ clean: cleantarget realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +# filtering out crossplatform.mk which already has an empty recipe via $(MAKEFILE_LIST) +$(filter-out $(_CF_DIR)crossplatform.mk,$(RESOURCES)): ; diff --git a/epj2make/epj2make.ec b/epj2make/epj2make.ec index ab991bef37..9b971f54f7 100644 --- a/epj2make/epj2make.ec +++ b/epj2make/epj2make.ec @@ -94,7 +94,21 @@ class epj2makeApp : GuiApplication const char * arg = argv[c]; if(arg[0] == '-') { - if(!strcmpi(arg+1, "make")) + if(!strcmpi(arg+1, "compiler-config")) + { + if(++c < argc) + { + const String path = argv[c]; + delete optionsCompiler; + if(FileExists(path)) + optionsCompiler = CompilerConfig::read(path); + else + printf($"Error: Project compiler configuration file (%s) was not found.\n", path); + } + else + valid = false; + } + else if(!strcmpi(arg+1, "make")) { if(++c < argc) optionsCompiler.makeCommand = argv[c]; @@ -239,6 +253,7 @@ class epj2makeApp : GuiApplication printf("%s", $"Syntax:\n"); printf("%s", $" epj2make [-t ] [-c ] [toolchain] [directories] [options] [-o ] \n"); printf("%s", $" toolchain:\n"); + printf("%s", $" [-compiler-config ]\n"); printf("%s", $" [-make ]\n"); printf("%s", $" [-cpp ]\n"); printf("%s", $" [-cc ]\n"); diff --git a/epj2make/epj2make.epj b/epj2make/epj2make.epj index 6c772210b0..472f94e910 100644 --- a/epj2make/epj2make.epj +++ b/epj2make/epj2make.epj @@ -86,7 +86,8 @@ "../ide/src/project/ProjectConfig.ec", "../ide/src/project/ProjectNode.ec", "../ide/src/IDESettings.ec", - "../ide/src/OldIDESettings.ec" + "../ide/src/OldIDESettings.ec", + "../ide/src/designer/SyntaxColorScheme.ec" ] }, "../extras/gui/controls/StringsBox.ec", diff --git a/extras/BinaryTriangle.ec b/extras/BinaryTriangle.ec new file mode 100644 index 0000000000..db10d5eff1 --- /dev/null +++ b/extras/BinaryTriangle.ec @@ -0,0 +1,109 @@ +/* + Binary triangle trees, partly inspired by tutorial and guidance + from Seumas McNally of Longbow Digital Arts: + + https://www.longbowgames.com/seumas/progbintri.html + + and "ROAMing Terrain: Real-time Optimally Adapting Meshes" white paper by Mark Duchaineau et al. + from Los Alamos National Laboratory & Lawrence Livermore National Laboratory, proceedings from VIS '97: + + http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.22.1811&rep=rep1&type=pdf + https://dl.acm.org/citation.cfm?id=266989.267028 +*/ +public import IMPORT_STATIC "ecere" + +public struct BinaryTriangle +{ + BinaryTriangle * left, * right, * bottom; + BinaryTriangle * leftTriangle, * rightTriangle; + uint tv0, tva, tv1, tvc; + int index; + + private void createChildTriangles(BinaryTriangle * stack, int * stackIndex) + { + BinaryTriangle * r = right, * l = left; + BinaryTriangle * lt = stack + (*stackIndex)++; + BinaryTriangle * rt = stack + (*stackIndex)++; + + leftTriangle = lt; + rightTriangle = rt; + + if(l) + { + if(l->bottom == this) + l->bottom = lt; + else if(l->left == this) + l->left = lt; + else + l->right = lt; + } + + if(r) + { + if(r->bottom == this) + r->bottom = rt; + else if(r->right == this) + r->right = rt; + else + r->left = rt; + } + + *lt = + { + index = index << 1, + left = rt, bottom = l, + tv0 = tva, tva = tvc, tv1 = tv0, + tvc = (tv0 + tva) >> 1 + }; + + *rt = + { + index = (index << 1) + 1, + right = lt, bottom = r, + tv0 = tv1, tva = tvc, tv1 = tva, + tvc = (tv1 + tva) >> 1 + }; + } + + public void clear() + { + leftTriangle = null; + rightTriangle = null; + left = null; + right = null; + bottom = null; + } + + public bool split(BinaryTriangle * stack, int * stackIndex) + { + bool result = false; + + if(bottom) + { + // Opposite triangle needs to be split first to avoid T-junction + if(bottom->bottom == this || bottom->split(stack, stackIndex)) + { + BinaryTriangle * b, * blt, * brt; + + createChildTriangles(stack, stackIndex); + bottom->createChildTriangles(stack, stackIndex); + b = bottom; + blt = b->leftTriangle; + brt = b->rightTriangle; + leftTriangle->right = brt; + rightTriangle->left = blt; + blt->right = rightTriangle; + brt->left = leftTriangle; + result = true; + } + } + else + { + createChildTriangles(stack, stackIndex); + leftTriangle->right = null; + rightTriangle->left = null; + result = true; + } + return result; + } +}; diff --git a/extras/CSVParser.ec b/extras/CSVParser.ec index ec60e9eb56..da0aafd318 100644 --- a/extras/CSVParser.ec +++ b/extras/CSVParser.ec @@ -7,36 +7,8 @@ public import"ecere" #endif public int UnescapeString(char * d, char * s, int len) -{ - int j = 0, k = 0; - char ch; - while(j < len && (ch = s[j])) - { - switch(ch) - { - case '\\': - switch((ch = s[++j])) - { - case 'n': d[k] = '\n'; break; - case 't': d[k] = '\t'; break; - case 'a': d[k] = '\a'; break; - case 'b': d[k] = '\b'; break; - case 'f': d[k] = '\f'; break; - case 'r': d[k] = '\r'; break; - case 'v': d[k] = '\v'; break; - case '\\': d[k] = '\\'; break; - case '\"': d[k] = '\"'; break; - case '\'': d[k] = '\''; break; - default: d[k] = '\\'; d[k] = ch; - } - break; - default: - d[k] = ch; - } - j++, k++; - } - d[k] = '\0'; - return k; +{ // Does not preserve the escape character in non-standard sequences : \z => z + return UnescapeCString(d, s, len) ; } public struct CSVParserParameters diff --git a/extras/FileSystemIterator.ec b/extras/FileSystemIterator.ec index ba65bb8e9b..b51f8ab97a 100644 --- a/extras/FileSystemIterator.ec +++ b/extras/FileSystemIterator.ec @@ -9,6 +9,19 @@ public import "ecere" #endif #endif +// todo: move this in the appropriate extras file +#ifdef __linux__ +#include +#endif +void getRealPath(const String path, String output) +{ +#ifdef __WIN32__ + strcpy(output, path); // todo: a windows implementation should be possible for windows 10 which has symlinks +#else + realpath(path, output); +#endif +} + public class NormalFileSystemIterator : FileSystemIterator { public: diff --git a/extras/XMLParser.ec b/extras/XMLParser.ec index 2db6921473..ff80dfd9a6 100644 --- a/extras/XMLParser.ec +++ b/extras/XMLParser.ec @@ -19,6 +19,7 @@ static WordStatus GetKeyWordEx(char ** input, char * keyWord, int maxSize, bool char ch; int c = 0; bool quoted = false, start = true, wasQuoted = false; + char quoteChar = 0; for(; (ch = *string); string++) { @@ -33,8 +34,12 @@ static WordStatus GetKeyWordEx(char ** input, char * keyWord, int maxSize, bool { if(!quoted && ((ch == ',' || (treatEqual && ch == '=')) || ch == '>') ) break; - else if(ch == '\"' /*|| ch == '\''*/) + // TOCHECK: Why weren't single quotes accepted here? How was this working? + else if((ch == '\"' || (/*acceptSingleQuote && */ch == '\'')) && (!quoteChar || quoteChar == ch)) + // else if(ch == '\"' /*|| ch == '\''*/) { + if(!wasQuoted) + quoteChar = ch; quoted ^= true; wasQuoted = true; start = false; @@ -90,14 +95,14 @@ class XMLParser return false; } - bool Parse(const char * inputString, int count) + bool Parse(const char * inputString, int64 count) { int insideTag = 0; char tag[MAX_TAG_LEN]; int tagLen = 0; bool commented = false; byte lastCh = ' '; - int stringPos; + int64 stringPos; char * characterData = this.characterData; int charLen = 0; int oldDepth = xmlDepth; @@ -281,11 +286,15 @@ class XMLParser else { if(!characterData) + { this.characterData = characterData = new byte[charBufSize]; + characterData[0] = 0; + } if(ch == '<' || charLen == charBufSize - 1) { ProcessCharacterData(characterData); charLen = 0; + characterData[0] = 0; } if(ch == '<') { diff --git a/extras/ecom.ec b/extras/ecom.ec new file mode 100644 index 0000000000..72dbe8d242 --- /dev/null +++ b/extras/ecom.ec @@ -0,0 +1,766 @@ +import "ecere" // must keep this or compilation outside this file will fail + +char * copyNamespaceFullName(NameSpace * ns, Application addModuleName) +{ + char * fullName = null; + if(ns) + { + int mlen, len = 0; + bool ecereCOM = false; + const char * moduleName = null; + Module m = addModuleName ? searchModulesForNamespace(addModuleName, ns, &ecereCOM) : null; + NameSpace * n; + for(n = ns; n; n = n->parent) + { + if(n->name) + len += len ? 2 : 0 + strlen(n->name); + } + if(addModuleName) + { + if(m && m.name) moduleName = m.name; + else moduleName = "0"; + mlen = strlen(moduleName) + 3; + len += mlen; + } + fullName = new char[len + 128]; + if(addModuleName) + { + //sprintf(fullName, "[%s]", moduleName); + //sprintf(fullName, "%s/", moduleName); // why is this causing a crash in unrelated memory allocation? :S + sprintf(fullName, "[%s]/", moduleName); + printNamespaceFullName(fullName + mlen, ns); + } + else + { + *fullName = 0; + printNamespaceFullName(fullName, ns); + } + } + else + fullName = CopyString("(null)"); + return fullName; +} + +void printNamespaceFullName(char * output, NameSpace * ns) +{ + if(ns->parent) printNamespaceFullName(output, ns->parent); + if(ns->name && ns->name[0]) + { + if(*output) strcat(output, "::"); + strcat(output, ns->name); + } +} + +Module searchModulesForNamespace(Module start, NameSpace * space, bool * markEcereCOM) +{ + Module module = null; + IterNamespace ns { module = start }; + while(ns.next()) + { + if(ns.ns == space) + { + module = start; + break; + } + } + ns.cleanup(); + if(!module) + { + if(!strcmp(start._class.name, "Application")) // if(start._class == class(Application)) doesn't work :( + { + Module m; + for(m = ((Application)start).allModules.first; !module && m; m = m.next) + module = searchModulesForNamespace(m, space, markEcereCOM); + } + else + { + SubModule m; + for(m = start.modules.first; !module && m; m = m.next) + module = searchModulesForNamespace(m.module, space, markEcereCOM); + } + } + if(!module && start.name && !strcmp(start.name, "ecere")) + { + IterNamespace ns { module = start, ecereCOM = true }; + while(ns.next()) + { + if(ns.ns == space) + { + module = start; + *markEcereCOM = true; + break; + } + } + ns.cleanup(); + } + return module; +} + +uint classTypeLen(ClassType ct) +{ + switch(ct) + { + case bitClass: return 8; + case unitClass: + case enumClass: return 9; + case unionClass: return 10; + case normalClass: + case structClass: + case noHeadClass: + case systemClass: return 11; + } + return 0; +} + +const char * fixedLengthNamesOfClassTypes[9 * 5] = { + "er", "nm", "st", "bt", "ut", "en", "nh", "un", "st", + "err", "nor", "str", "bit", "unt", "enm", "nhd", "uni", "sys", + "eror", "norm", "strc", "bitc", "unit", "enum", "nohd", "unio", "syst", + "error", "norml", "struc", "bitcl", "unitc", "enumc", "nohed", "union", "systm", + "error", "normal", "struct", "bits", "unit", "enum", "nohead", "union", "system" +}; + +const char * classTypeFixedLengthString(ClassType ct, int len) +{ + if(len >= 2 && len <= 5) + { + int n = len - 2; + switch(ct) + { + case normalClass: return fixedLengthNamesOfClassTypes[1 + n * 9]; + case structClass: return fixedLengthNamesOfClassTypes[2 + n * 9]; + case bitClass: return fixedLengthNamesOfClassTypes[3 + n * 9]; + case unitClass: return fixedLengthNamesOfClassTypes[4 + n * 9]; + case enumClass: return fixedLengthNamesOfClassTypes[5 + n * 9]; + case noHeadClass: return fixedLengthNamesOfClassTypes[6 + n * 9]; + case unionClass: return fixedLengthNamesOfClassTypes[7 + n * 9]; + case systemClass: return fixedLengthNamesOfClassTypes[8 + n * 9]; + } + return fixedLengthNamesOfClassTypes[0 + n * 9]; + } + return null; +} + +const char * classTypeToSimpleString(ClassType ct) +{ + switch(ct) + { + case normalClass: return fixedLengthNamesOfClassTypes[4 * 9 + 1]; + case structClass: return fixedLengthNamesOfClassTypes[4 * 9 + 2]; + case bitClass: return fixedLengthNamesOfClassTypes[4 * 9 + 3]; + case unitClass: return fixedLengthNamesOfClassTypes[4 * 9 + 4]; + case enumClass: return fixedLengthNamesOfClassTypes[4 * 9 + 5]; + case noHeadClass: return fixedLengthNamesOfClassTypes[4 * 9 + 6]; + case unionClass: return fixedLengthNamesOfClassTypes[4 * 9 + 7]; + case systemClass: return fixedLengthNamesOfClassTypes[4 * 9 + 8]; + } + return fixedLengthNamesOfClassTypes[4 * 9]; +} + +bool classIsFromModule(Class c, Module m, Application a) +{ + bool ecereCOM = false; + Module mod = searchModulesForNamespace(a, c.nameSpace, &ecereCOM); + return mod == m && !ecereCOM; +} + +List getClassLineage(Class c) +{ + List lineage { }; + Class cl; + for(cl = c; cl; cl = cl.base) + { + if(cl.templateClass) cl = cl.templateClass; + lineage.Insert(null, cl); + if(cl.inheritanceAccess == privateAccess) + break; + } + return lineage; +} + +List getCorrectClassLineage(Class c) // todo: review getClassLineage usage and replace with this implementation +{ + List lineage { }; + Class cl; + for(cl = c; cl; cl = cl.templateClass ? cl.templateClass : cl.base) + { + lineage.Insert(null, cl); + if(cl.inheritanceAccess == privateAccess) + break; + } + return lineage; +} + +List getFilteredClassLineage(Class c, ClassFilter filter) +{ + List lineage { }; + Class cl; + for(cl = c; cl; cl = cl.templateClass ? cl.templateClass : cl.base) + { + if(!filter.match(cl.type)) + break; + lineage.Insert(null, cl); + if(cl.inheritanceAccess == privateAccess) + break; + } + return lineage; +} + +public struct IterNamespace +{ +public: + property Module module { set { module = value; ecereCOM = module.name && !strcmp(module.name, "ecereCOM"); } } + bool insideFirst; + bool siblingsOnly; + bool processFullName; + NameSpace * ns; + //property NameSpace * { get { return ns; } } + Module itModule; + char * fullName; + int depth; + void ready() { ns = null; cleanup(); } + NameSpace * next() + { + bool push = false; + if(!ns) + { + if(ecereCOM) + { + ns = &module.application.systemNameSpace; + itModule = null; + } + else + { + ns = &module.publicNameSpace; + itModule = module; + } + if(processFullName) + { + if(!fullName) fullName = new char[MAX_LOCATION]; + *fullName = 0; + if(ns && ns->name) strcpy(fullName, ns->name); + } + push = insideFirst && !siblingsOnly; + } + else + { + if(ns->nameSpaces.first && !siblingsOnly && !insideFirst) + push = true; + else + { + for(;;) + { + if(((BTNode)ns).next) + { + ns = (NameSpace *)((BTNode)ns).next; + push = insideFirst && !siblingsOnly; + if(processFullName) + { + fullName[stack.count ? stack.lastIterator.data.cut : 0] = 0; + if(*fullName) strcat(fullName, "::"); + strcat(fullName, ns->name); + } + break; + } + else if(stack && stack.count) + { + INFrame frame = stack.lastIterator.data; + ns = (NameSpace *)frame.ns; + depth--; + stack.lastIterator.Remove(); + if(processFullName) + fullName[frame.cut] = 0; + if(insideFirst) + break; + } + else + { + ns = null; + break; + } + } + } + } + if(push) + { + if(!stack) stack = { }; + while(ns->nameSpaces.first) + { + depth++; + stack.Add({ (uintptr)ns, processFullName ? strlen(fullName) : 0 }); + ns = (NameSpace *)ns->nameSpaces.first; + if(processFullName) + { + if(*fullName) strcat(fullName, "::"); + strcat(fullName, ns->name); + } + if(!insideFirst) + break; + } + } + return ns; + } + + void cleanup() + { + delete fullName; + delete stack; + } +private: + Module module; + bool ecereCOM; + List stack; +}; +struct INFrame { uintptr ns; uint cut; }; + +public class BlackWhiteList : AVLTree +{ +public: + bool black; + bool match(const String name) + { + return !this || black == (Find(name) == null); + } +}; + +public struct IterDefine +{ +public: + NameSpace * ns; + BlackWhiteList list; + DefinedExpression df; + property DefinedExpression { get { return df; } } + void reset() { df = null; _it = null; } + DefinedExpression next() + { + _it = _it ? (BTNamedLink)((BTNode)_it).next : (BTNamedLink)ns->defines.first; + while(_it && !list.match(((DefinedExpression)_it.data).name)) + _it = (BTNamedLink)((BTNode)_it).next; + df = _it ? _it.data : null; + return df; + } +private: + BTNamedLink _it; +}; + +public struct IterFunction +{ +public: + NameSpace * ns; + BlackWhiteList list; + GlobalFunction fn; + property GlobalFunction { get { return fn; } } + void reset() { fn = null; _it = null; } + GlobalFunction next() + { + _it = _it ? (BTNamedLink)((BTNode)_it).next : (BTNamedLink)ns->functions.first; + while(_it && !list.match(((GlobalFunction)_it.data).name)) + _it = (BTNamedLink)((BTNode)_it).next; + fn = _it ? _it.data : null; + return fn; + } +private: + BTNamedLink _it; +}; + +public enum ClassFilter +{ + all, normalOnly, structOnly, bitOnly, unitOnly, enumOnly, noHeadOnly, systemOnly; +public: + bool match(ClassType classType) + { + switch(this) + { + case all: return true; + case normalOnly: return classType == normalClass; + case structOnly: return classType == structClass; + case bitOnly: return classType == bitClass; + case unitOnly: return classType == unitClass; + case enumOnly: return classType == enumClass; + case noHeadOnly: return classType == noHeadClass; + case systemOnly: return classType == systemClass; + } + return false; + } +}; + +public struct IterClass +{ +public: + NameSpace * ns; + BlackWhiteList list; + Class cl; + property Class { get { return cl; } } + void reset() { cl = null; _it = null; } + Class next(ClassFilter filter) + { + _it = _it ? (BTNamedLink)((BTNode)_it).next : (BTNamedLink)ns->classes.first; + while(_it && !(filter.match(((Class)_it.data).type) && list.match(((Class)_it.data).name))) + _it = (BTNamedLink)((BTNode)_it).next; + cl = _it ? _it.data : null; + return cl; + } +private: + BTNamedLink _it; +}; + +public struct IterAllClass +{ +public: + // issue with doing: + // IterAllClass itacl { module = g.mod }; + // have to do this instead: + // IterAllClass itacl { itn.module = g.mod }; + // it's not using the property as first thing to initialize it goes directly to the first member aka itn + property Module module { get { return itn.module; } set { itn.module = value; } } + property bool ecereCOM { get { return itn.ecereCOM; } set { itn.ecereCOM = value; } } + IterNamespace itn { }; + BlackWhiteList list; + Class cl; + property Class { get { return cl; } } + void reset() { cl = null; _it = null; } + Class next(ClassFilter filter) + { + while(_next() && !filter.match(((Class)_it.data).type) && list.match(((Class)_it.data).name)); + cl = _it ? _it.data : null; + return cl; + } + void cleanup() + { + itn.cleanup(); + } +private: + bool _next() + { + if(!itn.ns) itn.next(); + while(itn.ns && !(_it = _it ? (BTNamedLink)((BTNode)_it).next : (BTNamedLink)itn.ns->classes.first)) + { + itn.next(); + _it = null; + } + return itn.ns != null; + } + BTNamedLink _it; +}; + +public enum MethodFilter +{ + all, publicOnly, privateOnly, privateAndPublic, publicNormal, publicVirtual; +public: + bool match(AccessMode accessMode, MethodType methodType) + { + switch(this) + { + case all: return true; + case publicOnly: return accessMode == publicAccess; + case privateOnly: return accessMode == privateAccess; + case privateAndPublic: return accessMode == privateAccess || accessMode == publicAccess; + case publicNormal: return accessMode == publicAccess && methodType == normalMethod; + case publicVirtual: return accessMode == publicAccess && methodType == virtualMethod; + } + return false; + } +}; + +public struct IterMethod +{ +public: + Class cl; + Method md; + property Method { get { return md; } } + void reset() { md = null; } + Method next(MethodFilter filter) + { + md = md ? (Method)((BTNode)md).next : (Method)cl.methods.first; + while(md && !filter.match(md.memberAccess, md.type)) + md = (Method)((BTNode)md).next; + return md; + } +}; + +public enum AccessModeFilter +{ + all, publicOnly, privateOnly, privateAndPublic; +public: + bool match(AccessMode accessMode) + { + switch(this) + { + case all: return true; + case publicOnly: return accessMode == publicAccess; + case privateOnly: return accessMode == privateAccess; + case privateAndPublic: return accessMode == privateAccess || accessMode == publicAccess; + // defaultAccess, staticAccess, baseSystemAccess + } + return false; + } +}; + +public class MemberOrProperty : struct +{ +public: + class_fixed + class_no_expansion + MemberOrProperty prev, next; + const char * name; + bool isProperty; + AccessMode memberAccess; + int id; + Class _class; + const char * dataTypeString; + Class dataTypeClass; + //Type dataType; +} + +public struct IterMemberOrProperty +{ +public: + Class cl; + MemberOrProperty or; + property MemberOrProperty { get { return or; } } + void reset() { or = null; } + MemberOrProperty next(AccessModeFilter filter) + { + or = or ? or.next : (MemberOrProperty)cl.membersAndProperties.first; + while(or && !filter.match(or.memberAccess)) + or = or.next; + return or; + } +}; + +public struct IterProperty +{ +public: + Class cl; + Property pt; + property Property { get { return pt; } } + void reset() { pt = null; } + Property next(AccessModeFilter filter) + { + MemberOrProperty it = (MemberOrProperty)pt; + it = it ? it.next : (MemberOrProperty)cl.membersAndProperties.first; + while(it && (!it.isProperty || !filter.match(it.memberAccess))) + it = it.next; + pt = it ? (Property)it : null; + return pt; + } +}; + +public struct IterDataMember +{ +public: + Class cl; + DataMember dm; + property DataMember { get { return dm; } } + void reset() { dm = null; } + DataMember next(AccessModeFilter filter) + { + MemberOrProperty it = (MemberOrProperty)dm; + it = it ? it.next : (MemberOrProperty)cl.membersAndProperties.first; + while(it && (it.isProperty || !filter.match(it.memberAccess))) + it = it.next; + dm = it ? (DataMember)it : null; + return dm; + } +}; + +public struct IterDataMemberProper +{ + Class cl; + DataMember dm; + bool unionFirstsFirstAndFollowingsLast; + property DataMember { get { return dm; } } + void reset() { dm = null; it = null; cleanup();} + List parents; + DataMember next(AccessModeFilter filter) + { + while(_next() && (it.isProperty || !filter.match(it.memberAccess))); + dm = (DataMember)it; + if(dm && (dm.type == unionMember || dm.type == structMember)) + stack.Add(dm.members.first); + return dm; + } + void cleanup() + { + delete parents; + delete stack; + } +private: + MemberOrProperty it; + List stack; + bool _next() + { + if(!stack) stack = { }; + if(!it) + { + it = (MemberOrProperty)cl.membersAndProperties.first; + if(it) stack.Add(it); + } + else + { + bool first = dm && (dm.type == unionMember || dm.type == structMember) && dm.members.first; + if(first) + { + if(!parents) + parents = { }; + parents.Add(dm); + } + while(stack.count) + { + it = first ? stack.lastIterator.data : stack.lastIterator.data.next; + if(!first && it) stack.lastIterator.data = it; + if(it) + break; + else if(stack.count) + { + stack.lastIterator.Remove(); + if(parents && parents.count) + parents.lastIterator.Remove(); + first = false; + } + } + } + return it != null; + } +}; + +public enum MemberOrPropertyFilter +{ + all, privateOnly, publicOnly, privateAndPublic, normalOnly, unionOnly, structOnly, publicNormal, privateNormal; +public: + bool match(AccessMode accessMode, DataMemberType type) + { + switch(this) + { + case all: return true; + case publicOnly: return accessMode == publicAccess; + case privateOnly: return accessMode == privateAccess; + case privateAndPublic: return accessMode == privateAccess || accessMode == publicAccess; + // defaultAccess, staticAccess, baseSystemAccess + case normalOnly: return type == normalMember; + case unionOnly: return type == unionMember; + case structOnly: return type == structMember; + case publicNormal: return accessMode == publicAccess && type == structMember; + case privateNormal: return accessMode == privateAccess && type == structMember; + } + return false; + } +}; + +public struct IterMemberOrPropertyPlus +{ + Class cl; + Property pt; + DataMember dm; + MemberOrProperty mp; + bool unionFirstsFirstAndFollowingsLast; + property MemberOrProperty { get { return mp; } } + void reset() { pt = null; dm = null; mp = null; cleanup(); } + MemberOrProperty next(MemberOrPropertyFilter filter) + { + while(_next() && !filter.match(mp.memberAccess, mp.isProperty ? normalMember : ((DataMember)mp).type)); + pt = mp && mp.isProperty ? (Property)mp : null; + dm = mp && !mp.isProperty ? (DataMember)mp : null; + if(dm && (dm.type == unionMember || dm.type == structMember)) + stack.Add(dm.members.first); + return mp; + } + void cleanup() + { + delete stack; + } +private: + List stack; + bool _next() + { + if(!stack) stack = { }; + if(!mp) + { + mp = (MemberOrProperty)cl.membersAndProperties.first; + if(mp) stack.Add(mp); + } + else + { + bool first = dm && (dm.type == unionMember || dm.type == structMember) && dm.members.first; + while(stack.count) + { + mp = first ? stack.lastIterator.data : stack.lastIterator.data.next; + if(!first && mp) stack.lastIterator.data = mp; + if(mp) + break; + else if(stack.count) + { + stack.lastIterator.Remove(); + first = false; + } + } + } + return mp != null; + } +}; + +public struct IterConversion +{ +public: + Class cl; + Property cn; + property Property { get { return cn; } } + void reset() { cn = null; } + Property next(AccessModeFilter filter) + { + cn = cn ? cn.next : (Property)cl.conversions.first; + while(cn && !filter.match(cn.memberAccess)) + cn = cn.next; + return cn; + } +}; + +bool getClassIsTemplate(Class cl) +{ + Class c; + for(c = cl; c; c = c.base) + { + if(classTypeIsTemplatable(c.type)) + { + if(c.templateParams.count) + return true; + if(c.base && c.base.templateClass) + break; + } + else + break; + } + return false; +} + +bool classTypeIsTemplatable(ClassType ct) +{ + return ct == normalClass || ct == noHeadClass || ct == structClass; +} + +bool classTypeHasNativeSubType(ClassType ct) +{ + switch(ct) + { + case normalClass: return false; + case structClass: return false; + case bitClass: return true; + case unitClass: return true; + case enumClass: return true; + case noHeadClass: return false; + case unionClass: return false; + case systemClass: return false; + } + return false; // error +} + +bool classIsTemplatable(Class cl) +{ + Class c; + for(c = cl; c; c = c.base) + if(c.templateParams.count) + return c.templateClass ? false : true; + else if(c.templateClass) + return false; + return false; +} diff --git a/extras/gui/GuiConfigData.ec b/extras/gui/GuiConfigData.ec index e67024cdb0..5010c3f56c 100644 --- a/extras/gui/GuiConfigData.ec +++ b/extras/gui/GuiConfigData.ec @@ -1,7 +1,7 @@ #ifdef ECERE_STATIC public import static "ecere" #else -public import "ecere" +public import IMPORT_STATIC "ecere" #endif public class GuiConfigData diff --git a/extras/gui/controls/slider.ec b/extras/gui/controls/slider.ec new file mode 100644 index 0000000000..a283e8f98e --- /dev/null +++ b/extras/gui/controls/slider.ec @@ -0,0 +1,96 @@ +public import IMPORT_STATIC "ecere" + +class Slider : Window +{ + ScrollDirection direction; + opacity = 0; + bool dragging; + double percent; + int offset; + + void OnRedraw(Surface surface) + { + bool disabled = !isEnabled; + surface.SetBackground(disabled ? darkGray : foreground); + surface.SetForeground(disabled ? darkGray : foreground); + if(direction == horizontal) + { + int pos = (int)((clientSize.w-7) * percent + 0.5) + 3; + surface.Area(0, clientSize.h / 2 - 3, clientSize.w-1, clientSize.h/2 + 3); + surface.Rectangle(1, clientSize.h / 2 - 2, clientSize.w-2, clientSize.h/2 + 2); + + surface.SetBackground(darkGray); + surface.Area(pos - 3, 0, pos + 3, clientSize.h-1); + surface.SetForeground(white); + surface.Rectangle(pos - 3, 0, pos + 3, clientSize.h-1); + surface.VLine(2, clientSize.h-3, pos); + } + else + { + int pos = (int)((clientSize.h-7) * (1.0-percent) + 0.5) + 3; + surface.Area(clientSize.w / 2 - 3, 0, clientSize.w/2 + 3, clientSize.h-1); + surface.Rectangle(clientSize.w / 2 - 2, 1, clientSize.w/2 + 2, clientSize.h-2); + + surface.SetBackground(darkGray); + surface.Area(0, pos - 3, clientSize.w-1, pos + 3); + surface.SetForeground(white); + surface.Rectangle(0, pos - 3, clientSize.w-1, pos + 3); + surface.HLine(2, clientSize.w-3, pos); + } + } + + bool OnLeftButtonDown(int x, int y, Modifiers mods) + { + dragging = true; + Capture(); + OnMouseMove(x, y, mods); + return true; + } + + bool OnMouseMove(int x, int y, Modifiers mods) + { + if(dragging) + { + if(direction == horizontal) + percent = ((double)x - offset - 3) / (double)(clientSize.w-7); + else + percent = 1.0-((double)y - offset - 3) / (double)(clientSize.h-7); + // TO FIX: Max(Min didn't work with double + if(percent < 0) percent = 0; + else if(percent > 1) percent = 1; + Update(null); + NotifySlide(master, this, percent); + } + return true; + } + + void OnMouseCaptureLost() + { + OnLeftButtonUp(0,0,0); + } + + bool OnLeftButtonUp(int x, int y, Modifiers mods) + { + if(dragging) + { + dragging = false; + ReleaseCapture(); + NotifySlideUp(master, this, percent); + } + return true; + } + +public: + virtual bool Window::NotifySlide(Slider slider, double percent); + virtual bool Window::NotifySlideUp(Slider slider, double percent); + property double percent + { + get { return percent; } + set { if(!dragging) { percent = value; Update(null); } } + } + property ScrollDirection direction + { + set { direction = value; } + get { return direction; } + }; +} diff --git a/extras/gui/genericEditor.ec b/extras/gui/genericEditor.ec index a67aa71a49..ec0132e0bf 100644 --- a/extras/gui/genericEditor.ec +++ b/extras/gui/genericEditor.ec @@ -1,3 +1,4 @@ +import "ecere" // FIXME: Why is this crashing now? import "EDA" class GenericEditor : Window diff --git a/extras/html/HTMLView.ec b/extras/html/HTMLView.ec index 4b9a928490..1ac4edf009 100644 --- a/extras/html/HTMLView.ec +++ b/extras/html/HTMLView.ec @@ -120,7 +120,7 @@ class ObjectThread : Thread strcpy(path, entry.src); //strcpy(referer, browserWindow.location ? browserWindow.location : ""); - strcpy(referer, entry.referer); //browserWindow.location ? browserWindow.location : ""); + strcpy(referer, entry.referer ? entry.referer : ""); //browserWindow.location ? browserWindow.location : ""); //((GuiApplication)__thisModule).Unlock(); @@ -1002,18 +1002,14 @@ class HTMLView : Window NotifyPageOpened(master); } - void OpenFile(File f, char * firstReferer) + void OpenFile(File f, const char * firstReferer) { - char referer[MAX_LOCATION] = ""; bool opened = false; clickedLink = null; overLink = null; overBlock = null; - if(firstReferer) - strcpy(referer, firstReferer); - delete html; html = HTMLFile {}; @@ -1021,8 +1017,9 @@ class HTMLView : Window Update(null); opened = true; - this.location = null; - fileName = this.location; + delete location; + location = CopyString(firstReferer); + fileName = location; if(opened) { diff --git a/extras/html/htmlParser.ec b/extras/html/htmlParser.ec index 81c00720e3..0a64f93e60 100644 --- a/extras/html/htmlParser.ec +++ b/extras/html/htmlParser.ec @@ -173,7 +173,7 @@ String ParseURL(String input) static int GetKeyWord(char ** input, char * keyWord, int maxSize) { - return GetKeyWordEx(input, keyWord, maxSize, true, false); + return GetKeyWordEx(input, keyWord, maxSize, true, true); //false); } /*static char * GetString(char * string, char * what, int count) @@ -535,7 +535,7 @@ class HTMLFile } subBlock.size = fontSizes[c]; } - else if(size && size < NUM_FONT_SIZES) + else if(size && size <= NUM_FONT_SIZES) { subBlock.size = fontSizes[size-1]; } diff --git a/extras/html/lines.ec b/extras/html/lines.ec index 362382098e..1d888d6332 100644 --- a/extras/html/lines.ec +++ b/extras/html/lines.ec @@ -345,8 +345,7 @@ void RenderLine(HTMLView browser, Surface surface, int x, int y, int w, int h, B { int bw = block.pWidth ? (w * block.pWidth / 100) : block.w; int bh = block.pHeight ? (h * block.pHeight / 100) : block.h; - - int dx, dy; + int dx = 0, dy = 0; ColorAlpha fg = surface.GetForeground(); surface.SetForeground(white); @@ -499,7 +498,7 @@ bool PickLine(HTMLView browser, Surface surface, int x, int y, int w, int h, Blo { int bw = block.pWidth ? (w * block.pWidth / 100) : block.w; int bh = block.pHeight ? (h * block.pHeight / 100) : block.h; - int dx, dy; + int dx = 0, dy = 0; switch(block.halign) { @@ -549,10 +548,33 @@ bool PickLine(HTMLView browser, Surface surface, int x, int y, int w, int h, Blo if(block.text[0] == ' ' && block.text[1] == 0) pickX += tw; else if(pickX >= x && pickY >= y+h-th && pickX < x + tw + 2 && pickY < y+h) { - result = true; + // TODO: Binary search? +#define UTF8_NUM_BYTES(x) (__extension__({ byte b = x; (b & 0x80 && b & 0x40) ? ((b & 0x20) ? ((b & 0x10) ? 4 : 3) : 2) : 1; })) + + int c; + char ch; + int numBytes = 1; + bool half = false; + int space; + + *pickTextPos = textPos; + + surface.TextExtent(" ", 1, &space, null); + for(c = textPos; (ch = block.text[c]); c += numBytes) + { + int w, th; + + numBytes = UTF8_NUM_BYTES(ch); + surface.TextExtent(block.text + textPos, c - textPos, &w, &th); + if(th && pickX < x + (half ? w/2 : w) - space) + break; + *pickTextPos = c; + } + if(!block.text[c]) + *pickTextPos = c; + *pickBlock = block; - // Have to properly compute this - *pickTextPos = 0; + result = true; } textPos += len; x += tw; diff --git a/extras/stringTools.ec b/extras/stringTools.ec index 33ca8cc6d1..138174deab 100644 --- a/extras/stringTools.ec +++ b/extras/stringTools.ec @@ -1,8 +1,11 @@ +#ifndef IMPORT_STATIC #ifdef ECERE_STATIC -public import static "ecere" +#define IMPORT_STATIC static #else -public import "ecere" +#define IMPORT_STATIC #endif +#endif +public import IMPORT_STATIC "ecere" public enum Trim { no, left = 1, right = 2, ends = 3, middle = 4, all = 7 }; @@ -98,3 +101,59 @@ char * CopyAllCapsString(const char * string) *o = 0; return output; } + +// String Escape Copy +static void strescpy(char * output, char * string) +{ + char * s = string; + char * d = output; + while(*s) + { + switch(*s) + { + // case '\n': *d = '\\'; d++; *d = 'n' ; break; + // case '\t': *d = '\\'; d++; *d = 't' ; break; + // case '\a': *d = '\\'; d++; *d = 'a' ; break; + // case '\b': *d = '\\'; d++; *d = 'b' ; break; + // case '\f': *d = '\\'; d++; *d = 'f' ; break; + // case '\r': *d = '\\'; d++; *d = 'r' ; break; + // case '\v': *d = '\\'; d++; *d = 'v' ; break; + case '\'': *d = '\\'; d++; *d = '\''; break; + // case '\"': *d = '\\'; d++; *d = '\"'; break; + default: *d = *s; + } + s++; + d++; + } + *d = '\0'; +} + + // NOTE: This only escapes single quotes? + // TODO: Use new String.ec EscapeCString() instead? +String copyEscapeString(String string) +{ + String result = null; + if(string) + { + String buffer = new char[strlen(string) * 2 + 1]; + strescpy(buffer, string); + result = CopyString(buffer); + delete buffer; + } + return result; +} + +// replace characters for cached name not permitted on Windows systems +void replaceInvalidFileNameChars(char *cachedName) +{ + int i = 0; + if(strchr(cachedName, ':') || strchr(cachedName, '*')) + { + while(cachedName[i] != '\0') + { + if(cachedName[i] == ':' || cachedName[i] == '*') //|| cachedName[i] == '*' || cachedName[i] == '?' + cachedName[i] = '_'; + i++; + } + } +} diff --git a/extras/threadedProcessing.ec b/extras/threadedProcessing.ec new file mode 100644 index 0000000000..fb62c7bab3 --- /dev/null +++ b/extras/threadedProcessing.ec @@ -0,0 +1,617 @@ +#ifdef ECERE_STATIC +public import static "ecere" +#else +public import "ecere" +#endif + +class ProcessingThread : Thread +{ + ProcessingTask activeTask; + ProcessingStage stage; + int number; + + bool terminate; + bool canceled; + + unsigned int Main() + { + ProcessingStage stage = this.stage; + while(!terminate) + if(!stage.performTask(this) && !terminate) + stage.semaphore.Wait(); + return 0; + } +} + +class ProcessingTaskStatus : uint32 +{ +public: + int stage:8; + int threadID:16; + bool active:1; + bool ready:1; + bool cancel:1; + bool waitedOn:1; +} + +class ProcessingTask : ListItem +{ + class_no_expansion; + ProcessingTaskStatus status; + int priority; +} + +class ProcessingStage +{ + ThreadedProcessing processing; + Array threads { size = 1 }; + LinkList tasks { }; + LinkList readyTasks { }; + Mutex mutex { }; + Semaphore semaphore { }; + int thisStage; + + bool performTask(ProcessingThread thread) + { + bool result = false; + ProcessingTask task; + + if(!thread) + { + thread = threads[0]; + if(!thread) + { + thread = threads[0] = { stage = this }; + incref threads[0]; + } + } + + // PrintLn("Thread ", thread.number, " working!"); + + mutex.Wait(); + task = tasks.first; + if(task) + { + ProcessingAction action; + + if(thread.terminate || task.status.cancel) + { + action = clear; + tasks.Remove(task); + } + else + { + thread.activeTask = task; + task.status.active = true; + tasks.Remove(task); + task.status.threadID = thread.number; + + mutex.Release(); + action = processing.onPerformTask(task); + mutex.Wait(); + + task.status.threadID = 0; + task.status.active = false; + thread.activeTask = null; + if(thread.terminate || task.status.cancel) + action = clear; + } + + if(!thread.canceled) + { + switch(action) + { + case awaitProcessing: + task.status.ready = true; + readyTasks.Add(task); + break; + case clear: + mutex.Release(); + if(!(task.status.cancel && task.status.waitedOn)) + { + processing.onTaskCleared(task); + delete task; + } + mutex.Wait(); + // TO REVIEW: This was leaking? + break; + default: + mutex.Release(); + processing.addTask(task, action, task.priority); + /* + if(action > processing.stages.count) + processing.setup(action, 0); + task.status.stage = action; + processing.stages[action-1].addTask(task, task.priority); + */ + mutex.Wait(); + break; + } + } + thread.canceled = false; + result = true; + } + mutex.Release(); + return result; + } + + bool performSpecificTask(ProcessingTask task, ProcessingThread thread) + { + bool result = false; + + mutex.Wait(); + if(task && !task.status.active) + { + ProcessingAction action; + + if(task.status.cancel) + { + action = clear; + tasks.Remove(task); + } + else + { + tasks.Remove(task); + if(thread) + { + thread.activeTask = task; + task.status.threadID = thread.number; + } + mutex.Release(); + + action = processing.onPerformTask(task); + mutex.Wait(); + if(thread) + thread.activeTask = null; + task.status.threadID = 0; + task.status.active = false; + if(task.status.cancel) + action = clear; + } + + switch(action) + { + case awaitProcessing: + task.status.ready = true; + readyTasks.Add(task); + break; + case clear: + mutex.Release(); + // TO REVIEW: // if(task.status.waitedOn) This was leaking? -- turned this around completely? + if(!task.status.cancel || !task.status.waitedOn) + { + processing.onTaskCleared(task); + delete task; + } + mutex.Wait(); + break; + default: + mutex.Release(); + processing.addTask(task, action, task.priority); + mutex.Wait(); + break; + } + result = true; + } + mutex.Release(); + return result; + } + + bool processTasks(int maxTasks) + { + bool result = false; + int i; + + for(i = 0; !maxTasks || i < maxTasks; i++) + { + ProcessingTask task = null; + bool hadTask = false; + + mutex.Wait(); + task = readyTasks.first; + if(task) + { + ProcessingAction action; + + hadTask = true; + readyTasks.Remove(task); + task.status.active = true; + + mutex.Release(); + action = processing.onProcess(task); + mutex.Wait(); + + task.status.active = false; + if(task.status.cancel) action = clear; + + switch(action) + { + case awaitProcessing: // error to mark for processing again... + case clear: + if(!task.status.cancel || !task.status.waitedOn) + { + processing.onTaskCleared(task); + delete task; + } + break; + default: + mutex.Release(); + processing.addTask(task, action, task.priority); + mutex.Wait(); + } + + result = true; + } + mutex.Release(); + if(!hadTask) break; + } + return result; + } + + void init(int numThreads) + { + int i; + + semaphore.maxCount = numThreads; + threads.size = numThreads; + for(i = 0; i < numThreads; i++) + { + if(!threads[i]) + { + threads[i] = { stage = this, number = i+1 }; + incref threads[i]; + } + threads[i].Create(); + } + } + + void terminate() + { + int i; + + for(i = 0; i < threads.count; i++) + { + ProcessingThread t = threads[i]; + if(t) + t.terminate = true; + } + for(i = 0; i < threads.count; i++) + semaphore.Release(); + + for(i = 0; i < threads.count; i++) + { + ProcessingThread t = threads[i]; + if(t) + { + semaphore.Release(); + t.Wait(); + delete threads[i]; + } + } + threads.size = 1; + + cancelAllTasks(); + } + + void addTask(ProcessingTask task, int priority) + { + //ProcessingTask t; + + mutex.Wait(); + task.priority = priority; + tasks.Insert(tasks.last, task); + semaphore.Release(); + mutex.Release(); + } + + void cancelTask(ProcessingTask task, bool wait) + { + // Mutex is locked when coming in from ThreadedProcessing::cancelTask() + //mutex.Wait(); + task.status.cancel = true; + if(wait) + { + int lc = mutex.lockCount, i; + bool wasActive = task.status.active; + + task.status.waitedOn = true; + while(wait && task.status.active) + { + for(i = 0; i < lc; i++) mutex.Release(); + Sleep(0.01); + for(i = 0; i < lc; i++) mutex.Wait(); + } + + if(!task.status.active) + { + if(task.status.ready) + readyTasks.Remove(task); + else + { + ProcessingTask t = task; + if(wasActive) + { + for(t = tasks.first; t; t = t.next) + { + if(t == task) + break; + } + } + if(t) + tasks.Remove(task); + else + ; //PrintLn("NOTE: cancelTask() -- task not found"); + } + for(i = 0; i < lc; i++) mutex.Release(); + processing.onTaskCleared(task); + for(i = 0; i < lc; i++) mutex.Wait(); + delete task; + } + } + + //mutex.Release(); + } + + void cancelAllTasks() + { + ProcessingTask task; + + mutex.Wait(); + while((task = tasks.first)) + { + task.status.cancel = true; + task.status.waitedOn = true; + while(task.status.active) + { + mutex.Release(); + Sleep(0.01); + mutex.Wait(); + } + tasks.Remove(task); + mutex.Release(); + processing.onTaskCleared(task); + mutex.Wait(); + + delete task; + } + while((task = readyTasks.first)) + { + readyTasks.Remove(task); + mutex.Release(); + processing.onTaskCleared(task); + mutex.Wait(); + delete task; + } + for(t : threads; t) + { + ProcessingThread thread = t; + while(thread.activeTask) + { + mutex.Release(); + Sleep(0.01); + mutex.Wait(); + } + } + mutex.Release(); + } + + void prioritizeTask(ProcessingTask task, int priority) + { + mutex.Wait(); + if(task.status.stage == thisStage) + { + task.priority = priority; + + if(task.status.ready) + { + if(priority < MAXINT) + { + if(task.prev || task == readyTasks.first) + readyTasks.Move(task, null); + } + else if(task.next) + readyTasks.Move(task, readyTasks.last); + } + else + { + if(priority < MAXINT) + { + if(task.prev || task == tasks.first) // What's with that extra == first check? + tasks.Move(task, null); + } + else if(task.next) + tasks.Move(task, tasks.last); + } + } + mutex.Release(); + } + + ~ProcessingStage() + { + terminate(); + } +} + +enum ProcessingAction : int +{ + clear = -1, + awaitProcessing = 0, + stage1 = 1, + stage2 = 2, + stage3 = 3, + stage4 = 4 +}; + +// - Once added, the task must remain alive until performTask() or process() has been called and 'clear' has been returned, +// or until cancelTask() has been called and returned. +// - cancelTask() and prioritizeTask() must be called on previously added tasks still held by processing system +class ThreadedProcessing +{ +public: + Array stages { }; + + void setup(int stage, int numThreads) + { + if(stage) + { + if(stage > stages.count) + stages.size = stage; + if(!stages[stage-1]) + stages[stage-1] = { processing = this, thisStage = stage }; + if(numThreads >= stages[stage-1].threads.count) + stages[stage-1].init(numThreads); + } + } + + void terminate() + { + int s; + for(s = 0; s < stages.count; s++) + { + ProcessingStage stage = stages[s]; + if(stage) + stage.terminate(); + stages[s] = null; + delete stage; + } + } + + ~ThreadedProcessing() + { + terminate(); + } + + bool addTask(ProcessingTask task, int stage, int priority) + { + bool result = false; + if(stage > 0 && stages.count) // TODO: Should not come here if already destroyed + { + if(stage-1 >= stages.count) + setup(stage, 0); + if(stages[stage-1]) + { + task.status = { stage }; + stages[stage-1].addTask(task, priority); + result = true; + } + } + return result; + } + + void cancelTask(ProcessingTask task, bool wait) + { + if(task) + { + bool done = false; + while(!done && !task.status.cancel) + { + int s = task.status.stage; + ProcessingStage stage = s ? stages[s-1] : null; + if(stage) + { + stage.mutex.Wait(); + if(task.status.stage == s) + { + stage.cancelTask(task, wait); + done = true; + } + stage.mutex.Release(); + } + else + break; + } + } + } + + void cancelAllTasks(int stage) + { + int s; + for(s = stage ? stage - 1 : 0; s < stages.count; s++) + { + stages[s].cancelAllTasks(); + if(stage) break; + } + } + + void prioritizeTask(ProcessingTask task, int priority) + { +#ifdef _DEBUG + if(task.status.stage > 10) + PrintLn("WARNING: Likely invalid task!!"); +#endif + if(task.status.stage) + stages[task.status.stage-1].prioritizeTask(task, priority); + } + + bool processTasks(int stage, int maxTasks) + { + bool result = false; + int s; + // Process all tasks marked as 'ready' for a given stage + for(s = stage ? stage - 1 : 0; stages && s < stages.count; s++) + { + if(stages[s]) + result |= stages[s].processTasks(maxTasks); + if(stage) break; + } + return result; + } + + // Perform tasks of a given stage in calling thread + bool performTasks(int stage, int maxTasks) + { + bool result = false; + int s; + int t = 0; + for(s = stage ? stage - 1 : 0; s < stages.count; s++) + { + while(true) + { + bool r = stages[s].performTask(null); + result |= r; + if(!r || (maxTasks && ++t >= maxTasks)) + break; + } + if(stage) break; + } + return result; + } + + // Migrate tasks to another stage + bool performSpecificTask(int stage, ProcessingTask task, ProcessingThread thread) + { + bool result = false; + if(!task.status.active && task.status.stage == stage) + result = stages[stage-1].performSpecificTask(task, thread); + return result; + } + + // Wait for all tasks of a given stage to be cleared or ready + void wait(int stage) + { + int s; + for(s = stage ? stage - 1 : 0; s < stages.count; s++) + { + ProcessingStage ps = stages[s]; + while(ps.tasks.count) + { + onProgress(s+1, ps.tasks.count); + Sleep(0.01); + } + if(stage) break; + } + } + + // Called back from wait() with how many tasks still queued on that stage + virtual bool onProgress(int stage, int tasksQueued) { return true; } + + // onPerformTask() is called back from the stage threads + virtual ProcessingAction onPerformTask(ProcessingTask task) { return clear; } + + // onProcess() is invoked for each awaiting processing tasks from within processTasks() + virtual ProcessingAction onProcess(ProcessingTask task) { return clear; } + + // onTaskCleared() is called after returning clear from onProcess() or onPerformTasks(), + // as well as from cancelTask(), cancelAllTasks(), terminate() + virtual void onTaskCleared(ProcessingTask task); +} diff --git a/extras/types/DynamicString.ec b/extras/types/DynamicString.ec index e863c84a83..3095314a72 100644 --- a/extras/types/DynamicString.ec +++ b/extras/types/DynamicString.ec @@ -29,7 +29,7 @@ public class DynamicString : Array get { return array; } } - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { return array; } diff --git a/ide/Makefile b/ide/Makefile index 4d3d83fe52..3fa399b972 100644 --- a/ide/Makefile +++ b/ide/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean src/about.ec +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean src/about.ec # CORE VARIABLES @@ -68,6 +72,7 @@ _ECSOURCES = \ src/designer/CodeObject.ec \ src/designer/Designer.ec \ src/designer/SyntaxHighlighting.ec \ + src/designer/SyntaxColorScheme.ec \ src/designer/findCtx.ec \ src/designer/findExp.ec \ src/designer/findParams.ec \ @@ -211,15 +216,20 @@ PRJ_CFLAGS += \ -I/usr/X11R6/include,) \ $(if $(OSX_TARGET), \ -I/usr/X11R6/include,) \ - $(if $(DEBUG), -g, -O2 -ffast-math) $(FPIC) -w \ + $(if $(DEBUG), -g, -O2 -ffast-math) $(FPIC) -Wall \ $(FVISIBILITY) \ -DREPOSITORY_VERSION="\"$(REPOSITORY_VER)\"" \ -I../extras/include \ -DECERE_IDE ECFLAGS += -module $(MODULE) -ECFLAGS += \ - -nolinenumbers +ifndef DEBUG +ECFLAGS += -nolinenumbers +endif + +ifdef DISABLE_MEMMGR +ECFLAGS += -DDISABLE_MEMMGR +endif # PLATFORM-SPECIFIC OPTIONS @@ -260,7 +270,7 @@ endif all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS) $(ECS) $(ARCH_FLAGS) $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX) -o $(OBJ)$(MODULE).main.ec @@ -369,6 +379,9 @@ $(OBJ)Designer.sym: src/designer/Designer.ec $(OBJ)SyntaxHighlighting.sym: src/designer/SyntaxHighlighting.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/designer/SyntaxHighlighting.ec -o $(OBJ)SyntaxHighlighting.sym +$(OBJ)SyntaxColorScheme.sym: src/designer/SyntaxColorScheme.ec + $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/designer/SyntaxColorScheme.ec -o $(OBJ)SyntaxColorScheme.sym + $(OBJ)findCtx.sym: src/designer/findCtx.ec $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/designer/findCtx.ec -o $(OBJ)findCtx.sym @@ -513,6 +526,9 @@ $(OBJ)Designer.c: src/designer/Designer.ec $(OBJ)Designer.sym | $(SYMBOLS) $(OBJ)SyntaxHighlighting.c: src/designer/SyntaxHighlighting.ec $(OBJ)SyntaxHighlighting.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/designer/SyntaxHighlighting.ec -o $(OBJ)SyntaxHighlighting.c -symbols $(OBJ) +$(OBJ)SyntaxColorScheme.c: src/designer/SyntaxColorScheme.ec $(OBJ)SyntaxColorScheme.sym | $(SYMBOLS) + $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/designer/SyntaxColorScheme.ec -o $(OBJ)SyntaxColorScheme.c -symbols $(OBJ) + $(OBJ)findCtx.c: src/designer/findCtx.ec $(OBJ)findCtx.sym | $(SYMBOLS) $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/designer/findCtx.ec -o $(OBJ)findCtx.c -symbols $(OBJ) @@ -653,6 +669,9 @@ $(OBJ)Designer.o: $(OBJ)Designer.c $(OBJ)SyntaxHighlighting.o: $(OBJ)SyntaxHighlighting.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(OBJ)SyntaxHighlighting.c -o $(OBJ)SyntaxHighlighting.o +$(OBJ)SyntaxColorScheme.o: $(OBJ)SyntaxColorScheme.c + $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(OBJ)SyntaxColorScheme.c -o $(OBJ)SyntaxColorScheme.o + $(OBJ)findCtx.o: $(OBJ)findCtx.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(OBJ)findCtx.c -o $(OBJ)findCtx.o @@ -731,7 +750,7 @@ endif $(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(OBJ)$(MODULE).main.c -o $(OBJ)$(MODULE).main$(O) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -759,9 +778,13 @@ endif realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +# filtering out crossplatform.mk which already has an empty recipe via $(MAKEFILE_LIST) +$(filter-out $(_CF_DIR)crossplatform.mk,$(RESOURCES)): ; diff --git a/ide/ide.epj b/ide/ide.epj index e57e9490df..89cc3bb083 100644 --- a/ide/ide.epj +++ b/ide/ide.epj @@ -217,7 +217,8 @@ "findParams.ec", "Sheet.ec", "ToolBox.ec", - "SyntaxHighlighting.ec" + "SyntaxHighlighting.ec", + "SyntaxColorScheme.ec" ] }, { diff --git a/ide/src/IDESettings.ec b/ide/src/IDESettings.ec index bf567af32f..0e92caa339 100644 --- a/ide/src/IDESettings.ec +++ b/ide/src/IDESettings.ec @@ -4,44 +4,9 @@ public import static "ecere" public import "ecere" #endif -enum KeywordType { regular, preprocessor }; - -class SyntaxColorScheme -{ -public: - Color commentColor; - Color charLiteralColor; - Color stringLiteralColor; - Color preprocessorColor; - Color numberColor; - Color typeColor; - Color defColor; - Color fnColor; - - commentColor = dimGray; - charLiteralColor = crimson; - stringLiteralColor = crimson; - preprocessorColor = green; - numberColor = teal; - property::keywordColors = [ blue, blue ]; - typeColor = 0x15CE4C; - defColor = 0xFFCE7A; - fnColor = 0x9CB7FF; - - private Array keywordColors { }; // For each KeywordType - - public property Container keywordColors - { - set - { - keywordColors.Copy((void *)value); - // JSON/ECON Parser expects we'll hang on to this... Better solution with improved ref counting model? - if(value && value._class != class(BuiltInContainer) && !value._refCount) - delete value; - } - get { return keywordColors; } - } -}; +import "StringsBox" +import "OldIDESettings" +import "SyntaxColorScheme" // *** Color Schemes *** class IDEColorScheme @@ -232,10 +197,6 @@ define arDefaultCommand = "ar"; define objectDefaultFileExt = "o"; define outputDefaultFileExt = ""; -import "StringsBox" - -import "OldIDESettings" - #ifdef __WIN32__ #define WIN32_LEAN_AND_MEAN #define Sleep _Sleep @@ -246,7 +207,9 @@ import "OldIDESettings" #undef GetObject #endif -define MaxRecent = 9; +define MaxRecent = 128; +define NumKeyRecent = 9; +define MaxMoreRecent = 16; enum DirTypes { includes, libraries, executables }; @@ -286,7 +249,7 @@ void ReplaceSpaces(char * output, const char * source) } -enum GlobalSettingsChange { none, editorSettings, projectOptions, compilerSettings }; +enum GlobalSettingsChange { none, editorSettings, projectOptions, compilerSettings, compilers }; enum PathRelationship { unrelated, identical, siblings, subPath, parentPath, insuficientInput, pathEmpty, toEmpty, pathNull, toNull, bothEmpty, bothNull }; PathRelationship eString_PathRelated(const char * path, const char * to, char * pathDiff) @@ -1518,6 +1481,79 @@ class RecentPaths : Array } } } + + void updateRecentMenu(Window master, bool(recentNotifySelect)(Window this, MenuItem selection, Modifiers mods), Menu recentMenu) + { + int c = 0; + int addedExistCount = 0; + int addedMissingCount = 0; + int existMoreCount = 0; + int missingMoreCount = 0; + char * itemPath = new char[MAX_LOCATION]; + char * itemName = new char[MAX_LOCATION + 4]; + Menu missingMenu = null; + Menu addToExist = recentMenu; + Menu addToMissing = null; + recentMenu.Clear(); + for(recent : this) + { + bool key = false; + bool exists = false; + strncpy(itemPath, recent, MAX_LOCATION); + itemPath[MAX_LOCATION - 1] = '\0'; + MakeSystemPath(itemPath); + exists = FileExists(itemPath).isFile; + key = exists && addedExistCount < NumKeyRecent; + if(key) + snprintf(itemName, MAX_LOCATION + 4, "%d %s", 1 + addedExistCount, itemPath); + else + snprintf(itemName, MAX_LOCATION + 4, "%s", itemPath); + itemName[MAX_LOCATION + 4 - 1] = '\0'; + { + MenuItem item { copyText = true, text = itemName, id = c, NotifySelect = recentNotifySelect }; + if(key) + item.hotKey = (Key)k1 + addedExistCount; + if(exists) + { + if(addedExistCount == NumKeyRecent) + addToExist.AddDynamic(MenuDivider { }, master, true); + else if(addedExistCount - NumKeyRecent - MaxMoreRecent >= existMoreCount && this.count - 2 > c) + { + addToExist.AddDynamic(MenuDivider { }, master, true); + { + Menu moreMenu { addToExist, $"More", m }; + addToExist = moreMenu; + } + existMoreCount += MaxMoreRecent; + } + addToExist.AddDynamic(item, master, true); + addedExistCount++; + } + else + { + if(!missingMenu) + addToMissing = missingMenu = { null, $"Missing", n }; + if(addedMissingCount - MaxMoreRecent >= missingMoreCount) + { + addToMissing.AddDynamic(MenuDivider { }, master, true); + { + Menu moreMenu { addToMissing, $"More", m }; + addToMissing = moreMenu; + } + missingMoreCount += MaxMoreRecent; + } + addToMissing.AddDynamic(item, master, true); + addedMissingCount++; + } + } + c++; + } + if(missingMenu && addedExistCount) + recentMenu.AddDynamic(MenuDivider { }, master, true); + missingMenu.parent = recentMenu; + delete itemPath; + delete itemName; + } } static const char * compilerTypeNames[CompilerType] = { "GCC", "TCC", "PCC", "VS8", "VS9", "VS10" }; @@ -1562,33 +1598,38 @@ public enum CompilerType } }; - property const char * longName { get { return OnGetString(null, (void*)1, null); } }; - property const char * versionString { get { return OnGetString(null, (void*)2, null); } }; - property const char * yearString { get { return OnGetString(null, (void*)3, null); } }; - property const char * projectFileExtension { get { return OnGetString(null, (void*)4, null); } }; - property const char * solutionFileVersionString { get { return OnGetString(null, (void*)5, null); } }; + property const char * longName { get { return getString(null, 1); } }; + property const char * versionString { get { return getString(null, 2); } }; + property const char * yearString { get { return getString(null, 3); } }; + property const char * projectFileExtension { get { return getString(null, 4); } }; + property const char * solutionFileVersionString { get { return getString(null, 5); } }; - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + private static const char * getString(char * tempString, int stringType) { if(this >= firstCompilerType && this <= lastCompilerType) { if(tempString) strcpy(tempString, compilerTypeNames[this]); - if(fieldData == null) + if(stringType == 0) return compilerTypeNames[this]; - else if(fieldData == (void*)1) + else if(stringType == 1) return compilerTypeLongNames[this]; - else if(fieldData == (void*)2) + else if(stringType == 2) return compilerTypeVersionString[this]; - else if(fieldData == (void*)3) + else if(stringType == 3) return compilerTypeYearString[this]; - else if(fieldData == (void*)4) + else if(stringType == 4) return compilerTypeProjectFileExtension[this]; - else if(fieldData == (void*)5) + else if(stringType == 5) return compilerTypeSolutionFileVersionString[this]; } return null; } + + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) + { + return getString(tempString, 0); + } }; class CompilerConfig @@ -2249,7 +2290,7 @@ struct LanguageOption const String code; BitmapResource res; - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { return name; } diff --git a/ide/src/ProjectSettings.ec b/ide/src/ProjectSettings.ec index c83f7d717a..a22ea930f0 100644 --- a/ide/src/ProjectSettings.ec +++ b/ide/src/ProjectSettings.ec @@ -522,7 +522,7 @@ class OptionBox : CommonControl Iterator it { currentNode.platforms }; if(it.Find(p)) { - it.Remove(p); + it.Remove(); delete p; } } @@ -1320,7 +1320,7 @@ class BuildTab : Tab }; SelectorBar configSelector { - this, text = $"Configurations: ", anchor = { left = 98, top = 8, right = 54 }; size = { 0, 26 }; + this, text = $"Configurations: ", anchor = { left = 90, top = 8, right = 54 }; size = { 0, 26 }; opacity = 0; direction = horizontal; scrollable = true; diff --git a/ide/src/debugger/Debugger.ec b/ide/src/debugger/Debugger.ec index 1f73e08a92..5d0cb33aa9 100644 --- a/ide/src/debugger/Debugger.ec +++ b/ide/src/debugger/Debugger.ec @@ -302,10 +302,10 @@ enum DebuggerReason }; enum BreakpointType { - none, internalMain, internalWinMain, internalModulesLoaded, user, runToCursor, internalModuleLoad, internalEntry; + none, internalMain, internalWinMain, internalModulesLoaded, user, runToCursor, internalModuleLoad, internalEntry, debugBreakpoint; property bool isInternal { get { return (this == internalMain || this == internalWinMain || this == internalModulesLoaded || this == internalModuleLoad || this == internalEntry); } }; - property bool isUser { get { return (this == user || this == runToCursor); } }; + property bool isUser { get { return (this == user || this == runToCursor || this == debugBreakpoint); } }; }; enum DebuggerEvaluationError { none, symbolNotFound, memoryCantBeRead, unknown }; enum DebuggerUserAction @@ -391,6 +391,10 @@ class Debugger bool entryPoint; Map projectsLibraryLoaded { }; + Time targetRunTime; + Time targetLastStopTime; + Time targetStoppedTime; + Timer gdbTimer { delay = 0.0, userData = this; @@ -497,6 +501,8 @@ class Debugger bpInternal = bp; else bpUser = bp; + if(bp.type == debugBreakpoint) + activeFrameLevel = 1; if(stopItem && stopItem.frame) { if(bpInternal && bpRunToCursor && bpRunToCursor.inserted && !strcmp(bpRunToCursor.bp.addr, bp.bp.addr)) @@ -646,6 +652,8 @@ class Debugger #if 0 //def _DPL_ON __dpl(file, line, _dpct, dplchan::debuggerState, 0, state, same ? " *** == *** " : " -> ", value); #endif + if(value == stopped) + targetLastStopTime = GetTime(); state = value; if(!same) ide.AdjustDebugMenus(); } @@ -710,12 +718,17 @@ class Debugger ideProcessId = Process_GetCurrentProcessId(); sysBPs.Add((intBpEntry = Breakpoint { type = internalEntry, enabled = false, level = -1 })); + #if defined(__linux__) + sysBPs.Add((intBpMain = Breakpoint { type = internalMain, function = "_start", enabled = true, level = -1 })); + #endif + sysBPs.Add((intBpMain = Breakpoint { type = internalMain, function = "__main", enabled = true, level = -1 })); sysBPs.Add((intBpMain = Breakpoint { type = internalMain, function = "main", enabled = true, level = -1 })); #if defined(__WIN32__) sysBPs.Add((intBpWinMain = Breakpoint { type = internalWinMain, function = "WinMain", enabled = true, level = -1 })); #endif sysBPs.Add(Breakpoint { type = internalModulesLoaded, enabled = true, level = -1 }); sysBPs.Add(Breakpoint { type = internalModuleLoad, function = "InternalModuleLoadBreakpoint", enabled = true, level = -1 }); + sysBPs.Add(Breakpoint { type = debugBreakpoint, function = "debugBreakpoint", enabled = true, level = -1 }); } ~Debugger() @@ -901,6 +914,9 @@ class Debugger void HandleExit(char * reason, char * code) { char verboseExitCode[128]; + Time targetExitTime = 0; + if(targetRunTime) + targetExitTime = GetTime(); _dpcl(_dpct, dplchan::debuggerCall, 0, "Debugger::HandleExit(", reason, ", ", code, ")"); _ChangeState(loaded); // this state change seems to be superfluous, might be in case of gdb crash @@ -958,7 +974,10 @@ class Debugger else ide.outputView.debugBox.Logf($"The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode); } + if(targetRunTime) + ide.outputView.debugBox.Logf($"It ran for %.1f seconds.\n", targetExitTime - targetRunTime - targetStoppedTime); ide.Update(null); + targetRunTime = 0; } DebuggerState StartSession(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool restart, bool ignoreBreakpoints) @@ -1774,10 +1793,12 @@ class Debugger { bool moduleLoadLine; TrimLSpaces(line, line); - moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load")); + moduleLoadLine = + !strncmp(line, "eModule_Load", strlen("eModule_Load")) || + !strncmp(line, "if(!eModule_Load", strlen("if(!eModule_Load")); if(!moduleLoadBlock && moduleLoadLine) moduleLoadBlock = true; - else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0) + else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0 && strncmp(line, "printf", strlen("printf"))) break; } } @@ -1900,11 +1921,11 @@ class Debugger GdbDataBreakpoint first = null; for(n : bpItem.multipleBPs) { - if(!fstrcmp(n.fullname, bp.absoluteFilePath) && !first) + if(bp.absoluteFilePath && !fstrcmp(n.fullname, bp.absoluteFilePath) && !first) { count++; first = n; - break; + //break; } /*else { @@ -1919,7 +1940,7 @@ class Debugger } if(first) { - address = CopyString(first.addr); + //address = CopyString(first.addr); bpItem.addr = first.addr; bpItem.func = first.func; bpItem.file = first.file; @@ -1943,7 +1964,7 @@ class Debugger if(address) { - UnsetBreakpoint(bp); + //UnsetBreakpoint(bp); bp.address = address; delete address; SetBreakpoint(bp, removePath); @@ -2057,8 +2078,8 @@ class Debugger GdbTargetSet(); if(!usingValgrind) gdbExecution = run; - GdbExecCommon(); ShowDebuggerViews(); + GdbExecCommon(); if(usingValgrind) GdbExecContinue(true); else if(!GdbCommand(3, true, "-exec-run")) @@ -2175,6 +2196,18 @@ class Debugger { //_dpcl(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecCommon()"); BreakpointsMaintenance(); + + if(!targetRunTime) + { + targetRunTime = GetTime(); + targetStoppedTime = 0; + } + if(targetLastStopTime) + { + Time now = GetTime(); + targetStoppedTime = targetStoppedTime + (now - targetLastStopTime); + targetLastStopTime = 0; + } } #ifdef GDB_DEBUG_GUI @@ -2403,6 +2436,7 @@ class Debugger serialSemaphore.Wait(); app.Lock(); + GdbCommand(0, false, "handle SIGPIPE nostop"); GdbCommand(0, false, "-gdb-set verbose off"); //GdbCommand(0, false, "-gdb-set exec-done-display on"); GdbCommand(0, false, "-gdb-set step-mode off"); @@ -2729,7 +2763,10 @@ class Debugger { char tmp[4096]; bool needClass = false; - char * s = ((char * (*)(void *, void *, void *, void *, void *))(void *)c._vTbl[__ecereVMethodID_class_OnGetString])(c, data, tmp, null, &needClass); + char * s = + c._vTbl[__ecereVMethodID_class_OnGetString] ? + ((char * (*)(void *, void *, void *, void *, void *))(void *)c._vTbl[__ecereVMethodID_class_OnGetString])(c, data, tmp, null, &needClass) + : null; if(s) { FreeExpContents(exp); @@ -4272,6 +4309,7 @@ class ValgrindLogThread : Thread { int result = 0; app.Unlock(); + Sleep(0.05); if(vgLogFile) result = (int)vgLogFile.Read(output, 1, sizeof(output)); app.Lock(); @@ -4672,9 +4710,43 @@ class Frame : struct char * from; property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } } char * file; - property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } } + property char * file + { + set + { + delete file; + if(value) + { + const char * rightFile; + file = CopyUnescapedUnixPath(value); + rightFile = ide.workspace.getRightPath(file); + if(rightFile) + { + delete file; + file = CopyString(rightFile); + } + } + } + } char * absoluteFile; - property char * absoluteFile { set { delete absoluteFile; if(value) absoluteFile = CopyUnescapedUnixPath(value); } } + property char * absoluteFile + { + set + { + delete absoluteFile; + if(value) + { + const char * rightFile; + absoluteFile = CopyUnescapedUnixPath(value); + rightFile = ide.workspace.getRightPath(absoluteFile); + if(rightFile) + { + delete absoluteFile; + absoluteFile = CopyString(rightFile); + } + } + } + } int line; void Free() diff --git a/ide/src/debugger/process.ec b/ide/src/debugger/process.ec index 79d7f1d452..d6f01bdac5 100644 --- a/ide/src/debugger/process.ec +++ b/ide/src/debugger/process.ec @@ -1,3 +1,5 @@ +#undef __BlOCKS__ + default: #define uint _uint #define String String_ @@ -93,6 +95,7 @@ class ShowProcessWindowsThread : Thread extern void * IS_XGetDisplay(); static Atom xa_NET_WM_PID, xa_activeWindow; +#if 0 static void WaitForViewableWindow(X11Display * xGlobalDisplay, X11Window window) { int c; @@ -108,6 +111,7 @@ static void WaitForViewableWindow(X11Display * xGlobalDisplay, X11Window window) Sleep(1.0 / 18.2); } } +#endif // 0 static void EnumWindowBringToTop(X11Display * xGlobalDisplay, X11Window window, int processId) { @@ -312,7 +316,7 @@ int Process_GetChildExeProcessId(const int parentProcessId, const char * exeFile #else // This is the current OS X implementation uint pid = 0; - File f = DualPipeOpen({ output = true }, "ps -ajx"); + DualPipe f = DualPipeOpen({ output = true }, "ps -ajx"); if(f) { bool firstLine = true; @@ -350,6 +354,7 @@ int Process_GetChildExeProcessId(const int parentProcessId, const char * exeFile } } } + f.Wait(); delete f; } return pid; diff --git a/ide/src/designer/CodeEditor.ec b/ide/src/designer/CodeEditor.ec index 5e910dd59e..808fedea3c 100644 --- a/ide/src/designer/CodeEditor.ec +++ b/ide/src/designer/CodeEditor.ec @@ -1598,7 +1598,7 @@ class CodeEditor : Window { CodeEditor editor = (CodeEditor) master; - switch(key) + switch((SmartKey)key) { case enter: case tab: { @@ -2818,13 +2818,13 @@ class CodeEditor : Window printf("classes.count: %d\n", globalContext.classes.count); #endif - if(ide.workspace) { - CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler); - SetTargetBits(ide.workspace.bitDepth ? ide.workspace.bitDepth : GetHostBits()); + CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig( + ide.workspace ? ide.workspace.activeCompiler : ideSettings.defaultCompiler); + SetTargetBits(ide.workspace && ide.workspace.bitDepth ? ide.workspace.bitDepth : GetHostBits()); delete compiler; } - this.privateModule = __ecere_COM_Initialize((bool)(false | ((GetTargetBits() == sizeof(uintptr) *8) ? 0 : GetTargetBits() == 64 ? 2 : 4)), 1, null); + this.privateModule = __ecere_COM_Initialize((bool)(false | ((!GetTargetBits() || GetTargetBits() == sizeof(uintptr) *8) ? 0 : GetTargetBits() == 64 ? 2 : 4)), 1, null); ((SyntaxHighlighting)editBox.syntaxHighlighting).privateModule = this.privateModule; SetPrivateModule(privateModule); @@ -3071,7 +3071,7 @@ class CodeEditor : Window Class regClass = eSystem_FindClass(this.privateModule, ((Specifier)_class.baseSpecs->first).name); if(regClass) { - if(eClass_GetDesigner(regClass) && !GetBuildingEcereComModule()) + if(eClass_GetDesigner(regClass) && !GetBuildingEcereComModule() && regClass.structSize) { Instance instance = eInstance_New(regClass); ObjectInfo classObject @@ -3190,7 +3190,7 @@ class CodeEditor : Window // Maintain a list in FunctionDefinition of who is attached to it for(def = _class.definitions->first; def; def = def.next) { - if(def.type == functionClassDef) + if(def.type == functionClassDef && def.function.declarator) { ClassFunction function = def.function; if(!strcmp(function.declarator.symbol.string, propDef.initializer.exp.identifier.string)) @@ -3217,24 +3217,27 @@ class CodeEditor : Window if(instClass && eClass_GetDesigner(instClass)) { Instance control = eInstance_New(instClass); - incref control; - - object = ObjectInfo + if(control) { - oClass = classObject; - instance = control; - instCode = inst; - }; - classObject.instances.Add(object); - if(inst.exp) - // TOCHECK: Why is this needed now? - object.name = CopyString((inst.exp.type == memberExp) ? inst.exp.member.member.string : inst.exp.identifier.string); - def.object = object; - - // if(object.name) { symbol = eList_Add(&curContext.symbols, sizeof(Symbol)); symbol.string = object.name; symbol.type = MkClassType(instClass.name); } - - designer.CreateObject(control, object, false, classObject.instance); - sheet.AddObject(object, object.name ? object.name : inst._class.name, typeData, false); + incref control; + + object = ObjectInfo + { + oClass = classObject; + instance = control; + instCode = inst; + }; + classObject.instances.Add(object); + if(inst.exp) + // TOCHECK: Why is this needed now? + object.name = CopyString((inst.exp.type == memberExp) ? inst.exp.member.member.string : inst.exp.identifier.string); + def.object = object; + + // if(object.name) { symbol = eList_Add(&curContext.symbols, sizeof(Symbol)); symbol.string = object.name; symbol.type = MkClassType(instClass.name); } + + designer.CreateObject(control, object, false, classObject.instance); + sheet.AddObject(object, object.name ? object.name : inst._class.name, typeData, false); + } } break; } @@ -3259,7 +3262,7 @@ class CodeEditor : Window { Instantiation inst = decl.inst; Class instClass = eSystem_FindClass(this.privateModule, inst._class.name); - if(instClass && eClass_GetDesigner(instClass)) + if(instClass && eClass_GetDesigner(instClass) && ((object && object.next) || (!object && classObject.instances.first))) { Instance control; object = object ? object.next : classObject.instances.first; @@ -3478,11 +3481,11 @@ class CodeEditor : Window // Maintain a list in FunctionDefinition of who is attached to it for(def = _class.definitions->first; def; def = def.next) { - if(def.type == functionClassDef) + if(def.type == functionClassDef && def.function.declarator) { ClassFunction function = def.function; Identifier id = (member.initializer.exp.type == memberExp) ? member.initializer.exp.member.member : member.initializer.exp.identifier; - if(function.declarator && !strcmp(function.declarator.symbol.string, id.string)) + if(!strcmp(function.declarator.symbol.string, id.string)) { function.attached.Add(OldLink { data = method }); // Reference this particular instance? @@ -4105,7 +4108,7 @@ class CodeEditor : Window member.initializer.exp.type == memberExp /*ExpIdentifier*/) { if(((this.methodAction == actionDetachMethod || this.methodAction == actionReattachMethod) && this.method == method && this.selected == object) || - (this.methodAction == actionDeleteMethod && !strcmp(function.declarator.symbol.string, member.initializer.exp.identifier.string))) + (this.methodAction == actionDeleteMethod && function.declarator && !strcmp(function.declarator.symbol.string, member.initializer.exp.identifier.string))) { f.Seek(member.loc.start.pos - position, current); f.DeleteBytes(member.loc.end.pos - member.loc.start.pos); @@ -4691,7 +4694,7 @@ class CodeEditor : Window if(method && method.type == virtualMethod && propDef.initializer && propDef.initializer.type == expInitializer && propDef.initializer.exp && propDef.initializer.exp.type == identifierExp) { if(((methodAction == actionDetachMethod || methodAction == actionReattachMethod) && method == this.method && selected == classObject) || - (methodAction == actionDeleteMethod && !strcmp(function.declarator.symbol.string, propDef.initializer.exp.identifier.string))) + (methodAction == actionDeleteMethod && function.declarator && !strcmp(function.declarator.symbol.string, propDef.initializer.exp.identifier.string))) { f.Seek(propDef.loc.start.pos - position, current); f.DeleteBytes(propDef.loc.end.pos - propDef.loc.start.pos); @@ -5069,7 +5072,7 @@ class CodeEditor : Window { for(def = classDef.definitions->first; def; def = def.next) { - if(def.type == functionClassDef) + if(def.type == functionClassDef && def.function.declarator) { if(!strcmp(def.function.declarator.symbol.string, propDef.initializer.exp.identifier.string)) { @@ -5125,9 +5128,9 @@ class CodeEditor : Window { for(def = classDef.definitions->first; def; def = def.next) { - if(def.type == functionClassDef) + if(def.type == functionClassDef && def.function.declarator) { - if(def.function.declarator && !strcmp(def.function.declarator.symbol.string, member.initializer.exp.identifier.string)) + if(!strcmp(def.function.declarator.symbol.string, member.initializer.exp.identifier.string)) { function = def.function; break; @@ -5780,7 +5783,7 @@ class CodeEditor : Window } row.icon = bitmap ? bitmap : icons[(member.memberAccess == publicAccess && !isPrivate) ? typeData : typeDataPrivate]; } - else + else if(_class.type != bitClass) // TOFIX: This was not handled properly? ListSubDataMembers(member, member.memberAccess == privateAccess); } } @@ -6153,7 +6156,7 @@ class CodeEditor : Window listedEnums = ListEnumsModule(this.privateModule, destType); } - if(destType && destType.kind == classType && destType._class.registered && destType._class.registered.type == enumClass) + if(destType && destType.kind == classType && destType._class && destType._class.registered && destType._class.registered.type == enumClass) { ListEnumValues(destType._class.registered); diff --git a/ide/src/designer/CodeObject.ec b/ide/src/designer/CodeObject.ec index a5ff03553d..672ad1a0e9 100644 --- a/ide/src/designer/CodeObject.ec +++ b/ide/src/designer/CodeObject.ec @@ -107,7 +107,7 @@ class CodeObject : struct delete this; } - const char * OnGetString(char * string, void * fieldData, bool * needClass) + const char * OnGetString(char * string, void * fieldData, ObjectNotationType * onType) { return name ? name : ""; } diff --git a/ide/src/designer/SyntaxColorScheme.ec b/ide/src/designer/SyntaxColorScheme.ec new file mode 100644 index 0000000000..07ec816cbc --- /dev/null +++ b/ide/src/designer/SyntaxColorScheme.ec @@ -0,0 +1,44 @@ +#ifdef ECERE_STATIC +public import static "ecere" +#else +public import "ecere" +#endif + +enum KeywordType { regular, preprocessor }; + +class SyntaxColorScheme +{ +public: + Color commentColor; + Color charLiteralColor; + Color stringLiteralColor; + Color preprocessorColor; + Color numberColor; + Color typeColor; + Color defColor; + Color fnColor; + + commentColor = dimGray; + charLiteralColor = crimson; + stringLiteralColor = crimson; + preprocessorColor = green; + numberColor = teal; + property::keywordColors = [ blue, blue ]; + typeColor = 0x15CE4C; + defColor = 0xFFCE7A; + fnColor = 0x9CB7FF; + + private Array keywordColors { }; // For each KeywordType + + public property Container keywordColors + { + set + { + keywordColors.Copy((void *)value); + // JSON/ECON Parser expects we'll hang on to this... Better solution with improved ref counting model? + if(value && value._class != class(BuiltInContainer) && !value._refCount) + delete value; + } + get { return keywordColors; } + } +}; diff --git a/ide/src/designer/SyntaxHighlighting.ec b/ide/src/designer/SyntaxHighlighting.ec index e67828cdb8..c33f2c07a5 100644 --- a/ide/src/designer/SyntaxHighlighting.ec +++ b/ide/src/designer/SyntaxHighlighting.ec @@ -1,16 +1,10 @@ +#ifdef ECERE_STATIC +public import static "ecere" +#else public import "ecere" +#endif -import "IDESettings" - -char * strchrmax(const char * s, int c, int max) -{ - int i; - char ch; - for(i = 0; i < max && (ch = s[i]); i++) - if(ch == c) - return (char *)s + i; - return null; -} +import "SyntaxColorScheme" class SyntaxState : int { @@ -92,6 +86,9 @@ public class SyntaxHighlighting : EditSyntaxHL bool cppSingle, cMultiLine, cPrep, hashTagComments, cNumbers, singleQuotes; + bool allowHashInKeyword; + bool allowDashInKeyword; + void InitDraw() { currentState = viewLineState; @@ -374,9 +371,13 @@ public class SyntaxHighlighting : EditSyntaxHL { unichar ch = buffer[c]; unichar bf = (wordLen == 1) ? buffer[c-1] : 0; + bool chHash = ch == '#' && allowHashInKeyword; + bool bfHash = bf == '#' && allowHashInKeyword; + bool chDash = ch == '-' && allowDashInKeyword; + bool bfDash = bf == '-' && allowDashInKeyword; if(CharMatchCategories(ch, separators) || ch == '\t' || - (wordLen && !CharMatchCategories(ch, numbers|letters|marks|connector) && ch != '#' ) || - (bf && !CharMatchCategories(bf, numbers|letters|separators|marks|connector) && bf != '#' && bf != '\t')) + (wordLen && !CharMatchCategories(ch, numbers|letters|marks|connector) && !chHash && !chDash) || + (bf && !CharMatchCategories(bf, numbers|letters|separators|marks|connector) && !bfHash && !bfDash && bf != '\t')) break; wordLen++; } @@ -530,7 +531,7 @@ class MakeSHL : SyntaxHighlighting "for", "in", "do", "done", // built-in functions "subst", "patsubst", "strip", "findstring", "filter", "filter-out", "sort", - "word", "wordlist", "words", "firstword", "lastword", "dir", "suffix", "basename", + "word", "wordlist", "words", "firstword", "lastword", "dir", "notdir", "suffix", "basename", "addsuffix", "addprefix", "join", "wildcard", "realpath", "abspath", "if", "or", "and", "foreach", "file", "call", "value", "eval", "origin", "flavor", "error", "warning", "info", "shell", // special variables @@ -542,6 +543,7 @@ class MakeSHL : SyntaxHighlighting ]; hashTagComments = true; + allowDashInKeyword = true; } static const char * cExtensions[] = { "c", "h", null }; @@ -563,7 +565,8 @@ class CSHL : SyntaxHighlighting "size_t", "ssize_t", // Preprocessor - "#include", "#define", "#pragma", "#if", "#else", "#elif", "#ifdef", "#ifndef", "#endif", "#undef", "#line", "#cpu" + "#include", "#define", "#pragma", "#if", "#else", "#elif", "#ifdef", "#ifndef", "#endif", "#undef", "#line", "#cpu", + "#error", "#warning" ]; kwPrep = [ @@ -577,6 +580,7 @@ class CSHL : SyntaxHighlighting cNumbers = true; cPrep = true; singleQuotes = true; + allowHashInKeyword = true; } static const char * cxxExtensions[] = { "cxx", "hxx", "cpp", "hpp", "cc", "hh", null }; @@ -721,7 +725,7 @@ class PythonSHL : SyntaxHighlighting "self", "super", "isinstance", "getattr", "type", - "__init__", "__name__", "__neg__", "__int__", "__long__", "__float__", "__truediv__", "__rtruediv__", + "__init__", "__file__", "__name__", "__neg__", "__int__", "__long__", "__float__", "__truediv__", "__rtruediv__", "__mul__", "__rmul__", "__add__", "__radd__", "__sub__", "__rsub__", "__lt__", "__gt__", "__le__", "__ge__", "__ne__", "__eq__", "__new__", "__metaclass__","__getitem__","__len__", "__bases__" ]; @@ -929,6 +933,7 @@ class GLSLSHL : SyntaxHighlighting cMultiLine = true; cPrep = true; singleQuotes = true; + allowHashInKeyword = true; } static const char * cssExtensions[] = { "css", null }; @@ -1206,7 +1211,7 @@ class XMLSHL : SyntaxHighlighting } } -static const char * econExtensions[] = { "econ", "json", "geoecon", "geojson", "topojson", "epj", null }; +static const char * econExtensions[] = { "econ", "json", "geoecon", "geojson", "topojson", "epj", "ews", "cmss", null }; class ECONSHL : SyntaxHighlighting { class_property(extensions) = econExtensions; @@ -1340,17 +1345,15 @@ static subclass(SyntaxHighlighting) FindHL(Class c, const char * ext) SyntaxHighlighting SHLFromFileName(const String fileName) { - subclass(SyntaxHighlighting) hlClass; + subclass(SyntaxHighlighting) hlClass = null; char name[MAX_FILENAME]; + char ext[MAX_EXTENSION]; GetLastDirectory(fileName, name); - if(strstr(name, "Makefile") == name) - hlClass = class(MakeSHL); - else - { - char ext[MAX_EXTENSION]; - GetExtension(name, ext); + GetExtension(name, ext); + if(ext[0]) hlClass = FindHL(class(SyntaxHighlighting), ext); - } + if(!hlClass && strstr(name, "Makefile") == name) + hlClass = class(MakeSHL); if(!hlClass) hlClass = class(ConfigSHL); return eInstance_New(hlClass); diff --git a/ide/src/dialogs/FindInFilesDialog.ec b/ide/src/dialogs/FindInFilesDialog.ec index 5b558cab38..2c15b65041 100644 --- a/ide/src/dialogs/FindInFilesDialog.ec +++ b/ide/src/dialogs/FindInFilesDialog.ec @@ -12,7 +12,7 @@ class FindInFilesDialog : Window maxClientSize = { 640, 208 }; hasClose = true; tabCycle = true; - size = { 440, 208 }; + size = { 640, 208 }; autoCreate = false; public: @@ -58,8 +58,7 @@ public: if(!numFilters) filterDrop.AddString($"All files"); - //if(fileFilter >= numFilters) - // fileFilter = 0; + if(fileFilter >= numFilters) fileFilter = 0; filterDrop.currentRow = filterDrop.FindRow(fileFilter); } get { return sizeFilters; } @@ -246,6 +245,7 @@ private: findWhere.disabled = !inDir; findWherePrjNode.visible = inPrj; subDirs.disabled = inWrk; + objDirs.disabled = gitDirs.disabled = !inDir; llfindWhere.size = { llfindWhere.size.w, llfindWhere.size.h }; if(row) @@ -332,6 +332,14 @@ private: { llsubDirs, this, $"Include Subdirectories", altU, isCheckbox = true, checked = true; }; + Button objDirs + { + llsubDirs, this, $"Include 'obj' directories", altO, isCheckbox = true, checked = false; + }; + Button gitDirs + { + llsubDirs, this, $"Include '.git' directories", altG, isCheckbox = true, checked = false; + }; Label lfilter { llfilter, this, size.w = 72, labeledWindow = filterDrop }; DropBox filterDrop @@ -512,6 +520,8 @@ private: searchThread.project = null; searchThread.projectNode = null; searchThread.subDirs = subDirs.checked; + searchThread.objDirs = objDirs.checked; + searchThread.gitDirs = gitDirs.checked; if(findIn.currentRow == inDirectoryRow) { @@ -607,7 +617,7 @@ static define stackSize = 1024; class SearchThread : Thread { public: - bool active, subDirs/*, nameMatchCase, nameWholeWord*/, contentMatchCase, contentWholeWord; + bool active, subDirs, objDirs, gitDirs/*, nameMatchCase, nameWholeWord*/, contentMatchCase, contentWholeWord; char dir[MAX_DIRECTORY], contentCriteria[1024], contentReplace[1024], nameCriteria[1024]; FileFilter filter; FindInFilesDialog findDialog; @@ -648,7 +658,7 @@ private: unsigned int Main() { - int frame; + int frame = 0; int globalFindCount = 0, filesSearchedCount = 0, filesMatchedCount = 0, dirsMatchedCount = 0; //double lastTime = GetTime(); FindInFilesMode mode = this.mode; @@ -787,7 +797,9 @@ private: app.Unlock(); } - if(subDirs && stack[frame].fileList.stats.attribs.isDirectory && strcmp(stack[frame].fileList.name, ".git")) + if(subDirs && stack[frame].fileList.stats.attribs.isDirectory && + (objDirs || strcmp(stack[frame].fileList.name, "obj")) && + (gitDirs || strcmp(stack[frame].fileList.name, ".git"))) { int lastFrame = frame; /*double thisTime = GetTime(); @@ -870,7 +882,7 @@ private: { if(relative && mode == workspace && prj != ide.project) { - char special[MAX_LOCATION]; + char special[MAX_FILENAME + MAX_LOCATION + 2]; sprintf(special, "(%s)%s", prj.name, fileRelative); strcpy(fileRelative, special); } @@ -916,7 +928,7 @@ private: { if(relative && mode == workspace && prj != ide.project) { - char special[MAX_LOCATION]; + char special[MAX_FILENAME + MAX_LOCATION + 2]; sprintf(special, "(%s)%s", prj.name, fileRelative); strcpy(fileRelative, special); } diff --git a/ide/src/dialogs/GlobalSettingsDialog.ec b/ide/src/dialogs/GlobalSettingsDialog.ec index 370a770b0c..bbc65309c4 100644 --- a/ide/src/dialogs/GlobalSettingsDialog.ec +++ b/ide/src/dialogs/GlobalSettingsDialog.ec @@ -68,6 +68,7 @@ class GlobalSettingsDialog : Window { bool editorSettingsChanged = false; bool compilerSettingsChanged = false; + bool compilersChanged = false; bool projectOptionsChanged = false; AVLTree cfgsToWrite = null; if(editorTab.modifiedDocument) @@ -106,7 +107,10 @@ class GlobalSettingsDialog : Window if(compilersTab.modifiedDocument) { if(strcmp(compilersTab.compilerConfigsDir.path, ideSettings.compilerConfigsDir)) + { ideSettings.compilerConfigsDir = compilersTab.compilerConfigsDir.path; + compilerSettingsChanged = true; + } if(compilersTab.compilerConfigs.OnCompare(ideConfig.compilers)) { cfgsToWrite = compilersTab.compilerConfigs.getWriteRequiredList(ideConfig.compilers); @@ -115,7 +119,7 @@ class GlobalSettingsDialog : Window { ideConfig.compilers.Add(compiler.Copy()); } - compilerSettingsChanged = true; + compilersChanged = true; } } @@ -142,20 +146,23 @@ class GlobalSettingsDialog : Window } } - if(editorSettingsChanged || projectOptionsChanged) + if(editorSettingsChanged || compilerSettingsChanged || projectOptionsChanged) settingsContainer.Save(); + if(editorSettingsChanged) + OnGlobalSettingChange(GlobalSettingsChange::editorSettings); if(compilerSettingsChanged) + OnGlobalSettingChange(GlobalSettingsChange::compilerSettings); + if(projectOptionsChanged) + OnGlobalSettingChange(GlobalSettingsChange::projectOptions); + + if(compilersChanged) { ideConfig.compilers.write(settingsContainer, cfgsToWrite); - OnGlobalSettingChange(GlobalSettingsChange::compilerSettings); + OnGlobalSettingChange(GlobalSettingsChange::compilers); cfgsToWrite.Free(); delete cfgsToWrite; } - if(editorSettingsChanged) - OnGlobalSettingChange(GlobalSettingsChange::editorSettings); - if(projectOptionsChanged) - OnGlobalSettingChange(GlobalSettingsChange::projectOptions); editorTab.modifiedDocument = false; compilersTab.modifiedDocument = false; diff --git a/ide/src/dialogs/NewProjectDialog.ec b/ide/src/dialogs/NewProjectDialog.ec index f5205f4f5d..2dfa8dd54d 100644 --- a/ide/src/dialogs/NewProjectDialog.ec +++ b/ide/src/dialogs/NewProjectDialog.ec @@ -5,8 +5,8 @@ FileDialog fileDialog { type = selectDir, text = $"Select project directory" }; class NewProjectDialog : Window { background = formColor; - minClientSize = { 316, 170 }; - maxClientSize = { 640, 170 }; + minClientSize = { 316, 186 }; + maxClientSize = { 640, 186 }; borderStyle = sizable; tabCycle = true; hasClose = true; @@ -31,9 +31,33 @@ class NewProjectDialog : Window }; Label { this, position = { 10, 60 }, labeledWindow = locationEditBox }; - DropBox targetType { this, position = { 10, 130 }, size = { 130 }, hotKey = altT, text = $"Target Type" }; + DropBox targetType + { + this, position = { 10, 130 }, size = { 130 }, hotKey = altT, text = $"Target Type"; + bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods) + { + bool dis = !(row && row.tag == TargetTypes::executable); + if(consoleApp.disabled != dis) + { + if(dis) + { + consoleApp.id = consoleApp.checked; + consoleApp.checked = false; + } + else + consoleApp.checked = (bool)consoleApp.id; + consoleApp.disabled = dis; + } + return true; + } + }; Label { this, position = { 10, 110 }, labeledWindow = targetType }; + Button consoleApp + { + parent = this, text = $"Console Application", hotKey = altC, position = { 10, 160 }; + isCheckbox = true; + }; Button okBtn { @@ -86,7 +110,7 @@ class NewProjectDialog : Window project = Project { property::filePath = filePath; - moduleName = CopyString(name); + moduleName = CopyString(prjName); topNode.type = NodeTypes::project; config = debug; }; @@ -95,8 +119,10 @@ class NewProjectDialog : Window warnings = all; // TOFIX: Precomp problems withou the extra ( ) targetType = ((TargetTypes)targetType.GetTag()); - targetFileName = /*CopyString(*/name/*)*/; + targetFileName = (char*)prjName; + console = consoleApp.checked ? true : unset; }; + if(project.options.targetType != staticLibrary) { project.options.libraries = { [ CopyString("ecere") ] }; @@ -348,8 +374,8 @@ class NewProjectDialog : Window class QuickProjectDialog : Window { background = formColor; - minClientSize = { 316, 110 }; - maxClientSize = { 640, 110 }; + minClientSize = { 316, 124 }; + maxClientSize = { 640, 124 }; borderStyle = sizable; tabCycle = true; hasClose = true; @@ -360,8 +386,32 @@ class QuickProjectDialog : Window Label message { this, position = { 10, 10 }, text = $"Do you want to quickly create a temporary project?" }; - DropBox targetType { this, position = { 10, 70 }, size = { 130 }, hotKey = altT, text = $"Target Type" }; + DropBox targetType + { + this, position = { 10, 70 }, size = { 130 }, hotKey = altT, text = $"Target Type"; + bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods) + { + bool dis = !(row && row.tag == TargetTypes::executable); + if(consoleApp.disabled != dis) + { + if(dis) + { + consoleApp.id = consoleApp.checked; + consoleApp.checked = false; + } + else + consoleApp.checked = (bool)consoleApp.id; + consoleApp.disabled = dis; + } + return true; + } + }; Label { this, position = { 10, 50 }, labeledWindow = targetType }; + Button consoleApp + { + parent = this, text = $"Console Application", hotKey = altC, position = { 10, 100 }; + isCheckbox = true; + }; Button okBtn { @@ -435,7 +485,7 @@ class QuickProjectDialog : Window project = Project { - filePath = filePath; + property::filePath = filePath; moduleName = CopyString(prjName); topNode.type = NodeTypes::project; config = debug; @@ -445,7 +495,8 @@ class QuickProjectDialog : Window warnings = all; // TOFIX: Precomp problems withou the extra ( ) targetType = ((TargetTypes)targetType.GetTag()); - targetFileName = /*CopyString(*/prjName/*)*/; + targetFileName = prjName; + console = consoleApp.checked ? true : unset; }; if(project.options.targetType != staticLibrary) @@ -458,6 +509,7 @@ class QuickProjectDialog : Window strcpy(workspaceFile, filePath); ChangeExtension(workspaceFile, WorkspaceExtension, workspaceFile); workspace = Workspace { activeCompiler = ideSettings.defaultCompiler, workspaceFile = workspaceFile }; + workspace.Init(); } workspace.Init(); diff --git a/ide/src/documents/PictureEdit.ec b/ide/src/documents/PictureEdit.ec index 3c65fe8b12..4764548626 100644 --- a/ide/src/documents/PictureEdit.ec +++ b/ide/src/documents/PictureEdit.ec @@ -69,6 +69,8 @@ class PictureEdit : Window OnLoadGraphics(); displaySystem.Unlock(); + imageModeRGBItem.disabled = false; + imageModeIndexedItem.disabled = true; imageModeColorTableItem.disabled = false; Update(null); modifiedDocument = true; @@ -89,6 +91,8 @@ class PictureEdit : Window OnLoadGraphics(); displaySystem.Unlock(); + imageModeRGBItem.disabled = true; + imageModeIndexedItem.disabled = false; imageModeColorTableItem.disabled = true; Update(null); modifiedDocument = true; @@ -179,11 +183,11 @@ class PictureEdit : Window { case pixelFormat8: imageModeIndexedItem.checked = true; - imageModeRGBItem.disabled = true; + imageModeIndexedItem.disabled = true; break; case pixelFormat888: imageModeRGBItem.checked = true; - imageModeColorTableItem.disabled = true; + imageModeRGBItem.disabled = true; break; } } @@ -299,8 +303,9 @@ class PictureEdit : Window bool result = false; if(bitmap) { + int compression = 100; // TODO: Clarify whether options are driver specific or not if(bitmap.Save(fileName, - ((FileType *)pictureEditFileDialog.types)[pictureEditFileDialog.fileType].typeExtension, (void *) bool::true)) + ((FileType *)pictureEditFileDialog.types)[pictureEditFileDialog.fileType].typeExtension, &compression)) { modifiedDocument = false; result = true; diff --git a/ide/src/ide.ec b/ide/src/ide.ec index 2ba35e4b0c..f72508db25 100644 --- a/ide/src/ide.ec +++ b/ide/src/ide.ec @@ -141,11 +141,11 @@ static Array fileFilters { $"Source Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx, *.m, *.mm, *.frag, *.glsl, *.vert, *.py, *.java, *.cs, *.go, *.rs, *.swift, *.js, *.php, *.y, *.l)", "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx, py, java, cs, js, go, rs, swift, php, m, mm, frag, glsl, vert, y, l" }, { $"Swift Source Files (*.swift)", "swift" }, - { $"Text files (*.txt, *.text, *.nfo, *.info)", "txt, text, nfo, info" }, - { $"Web files (*.html, *.htm, *.xhtml, *.css, *.php, *.js, *.jsi, *.rb, *.xml)", "html, htm, xhtml, css, php, js, jsi, rb, xml" }, + { $"Text Files (*.txt, *.text, *.nfo, *.info)", "txt, text, nfo, info" }, + { $"Web Files (*.html, *.htm, *.xhtml, *.css, *.php, *.js, *.jsi, *.rb, *.xml)", "html, htm, xhtml, css, php, js, jsi, rb, xml" }, { $"Image Files (*.jpg, *.jpeg, *.bmp, *.pcx, *.png, *.gif)", "jpg, jpeg, bmp, pcx, png, gif" }, { $"3D Studio Model Files (*.3ds)", "3ds" }, - { $"All files", null } + { $"All Files", null } ] }; static Array fileTypes @@ -168,12 +168,31 @@ static Array projectTypes static Array findInFilesFileFilters { [ + { $"Project and Workspace Files (*.epj, *.ews)", "epj, ews" }, { $"eC Files (*.ec, *.eh)", "ec, eh" }, + { $"C Files (*.c, *.h)", "c, h" }, + { $"C++ Files (*.cpp, *.cc, *.cxx, *.hpp, *.hh, *.hxx)", "cpp, cc, cxx, hpp, hh, hxx" }, { $"C/C++/eC Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx)", "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx" }, - { $"Header Files for eC/C/C++ (*.eh, *.h, *.hpp, *.hh, *.hxx)", "eh, h, hpp, hh, hxx" }, + { $"Header Files for C/C++/eC (*.eh, *.h, *.hpp, *.hh, *.hxx)", "eh, h, hpp, hh, hxx" }, { $"C/C++/eC Source Files (*.ec, *.c, *.cpp, *.cc, *.cxx)", "ec, c, cpp, cc, cxx" }, - { $"Text files (*.txt)", "txt" }, - { $"All files", null } + { $"Objective-C Source Files (*.m, *.mm)", "m, mm" }, + { $"GLSL Source Files (*.glsl, *.vert, *.frag)", "glsl, vert, frag" }, + { $"Python Source Files (*.py)", "py" }, + { $"Java Source Files (*.java)", "java" }, + { $"C# Source Files (*.cs)", "cs" }, + { $"Rust Source Files (*.rs)", "rs" }, + { $"Go Source Files (*.go)", "go" }, + { $"Ruby Source Files (*.rb)", "rb" }, + { $"JavaScript Source Files (*.js)", "js" }, + { $"PHP Source Files (*.php)", "php" }, + { $"Bison & Flex Source Files (*.y, *.l)", "y, l" }, + { $"Source Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx, *.m, *.mm, *.frag, *.glsl, *.vert, *.py, *.java, *.cs, *.go, *.rs, *.swift, *.js, *.php, *.y, *.l)", + "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx, py, java, cs, js, go, rs, swift, php, m, mm, frag, glsl, vert, y, l" }, + { $"Swift Source Files (*.swift)", "swift" }, + { $"Web Files (*.html, *.htm, *.xhtml, *.css, *.php, *.js, *.jsi, *.rb, *.xml)", "html, htm, xhtml, css, php, js, jsi, rb, xml" }, + { $"Text Files (*.txt)", "txt" }, + { $"More Text Files (*.txt, *.text, *.nfo, *.info)", "txt, text, nfo, info" }, + { $"All Files", null } ] }; FileDialog ideFileDialog @@ -215,6 +234,8 @@ GlobalSettingsDialog globalSettingsDialog case projectOptions: break; case compilerSettings: + break; + case compilers: { ide.UpdateCompilerConfigs(true); break; @@ -380,6 +401,10 @@ class IDEToolbar : ToolBar } }; + Window spacer65 { this, size = { 4 } }; + + Button forceSingleJob { this, text = $"Force Single Job Compiling", isCheckbox = true; }; + Window spacer7 { this, size = { 4 } }; void IDEToolbar() @@ -526,7 +551,8 @@ class IDEWorkSpace : Window eb.selectionText = cs.selectionText; eb.background = cs.codeEditorBG; eb.foreground = cs.codeEditorFG; - ((SyntaxHighlighting)eb.syntaxHighlighting).syntaxColorScheme = cs.syntaxColors; + if((SyntaxHighlighting)eb.syntaxHighlighting) + ((SyntaxHighlighting)eb.syntaxHighlighting).syntaxColorScheme = cs.syntaxColors; } if(projectView) @@ -578,6 +604,13 @@ class IDEWorkSpace : Window outputView.findBox.foreground = cs.outputText; outputView.findBox.selectionColor = cs.selectionColor; outputView.findBox.selectionText = cs.selectionText; + +#ifdef GDB_DEBUG_OUTPUT + outputView.findBox.background = cs.outputBackground; + outputView.findBox.foreground = cs.outputText; + outputView.findBox.selectionColor = cs.selectionColor; + outputView.findBox.selectionText = cs.selectionText; +#endif } ProjectView projectView; @@ -586,19 +619,23 @@ class IDEWorkSpace : Window { parent = this; - void OnGotoError(const char * line, bool noParsing) + void OnGotoError(const char * line, bool openAsText, bool noParsing) { CompilerConfig compiler = ide.workspace ? ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler) : null; const char * objectFileExt = compiler ? compiler.objectFileExt : objectDefaultFileExt; - ide.GoToError(line, noParsing, objectFileExt); + ide.GoToError(line, openAsText, noParsing, objectFileExt); delete compiler; } - void OnCodeLocationParseAndGoTo(const char * line) + void OnCodeLocationParseAndGoTo(const char * line, bool openAsText, bool noParsing) { CompilerConfig compiler = ide.workspace ? ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler) : null; const char * objectFileExt = compiler ? compiler.objectFileExt : objectDefaultFileExt; - ide.CodeLocationParseAndGoTo(line, ide.findInFilesDialog.findProject, ide.findInFilesDialog.findDir, objectFileExt); + bool inFind = ide.outputView.activeBox == ide.outputView.findBox; + if(inFind) + ide.CodeLocationParseAndGoTo(line, ide.findInFilesDialog.findProject, ide.findInFilesDialog.findDir, openAsText, noParsing, objectFileExt); + else + ide.CodeLocationParseAndGoTo(line, null, null, openAsText, noParsing, objectFileExt); delete compiler; } @@ -911,7 +948,7 @@ class IDEWorkSpace : Window bool isProjectFile; char extension[MAX_EXTENSION] = ""; GetExtension(file, extension); - isProjectFile = (!strcmpi(extension, "epj") || !strcmpi(extension, "ews")); + isProjectFile = (!strcmpi(extension, ProjectExtension) || !strcmpi(extension, WorkspaceExtension)); if(mods.ctrl && !mods.shift) { char * command = PrintString("ecere-ide ", isProjectFile ? "-t " : "", file); @@ -1668,8 +1705,8 @@ class IDEWorkSpace : Window Menu windowMenu { menu, $"Window", w }; MenuItem { windowMenu, $"Close All", l, NotifySelect = MenuWindowCloseAll }; MenuDivider { windowMenu }; - MenuItem { windowMenu, $"Next", n, f6, NotifySelect = MenuWindowNext }; - MenuItem { windowMenu, $"Previous", p, shiftF6, NotifySelect = MenuWindowPrevious }; + MenuItem { windowMenu, $"Next", n, Key { right, ctrl = true, alt = true }, NotifySelect = MenuWindowNext }; + MenuItem { windowMenu, $"Previous", p, Key { left, ctrl = true, alt = true }, NotifySelect = MenuWindowPrevious }; MenuDivider { windowMenu }; MenuItem { windowMenu, $"Cascade", c, NotifySelect = MenuWindowCascade }; MenuItem { windowMenu, $"Tile Horizontally", h, NotifySelect = MenuWindowTileHorz }; @@ -1789,7 +1826,6 @@ class IDEWorkSpace : Window { master = this, filters = findInFilesFileFilters.array, sizeFilters = findInFilesFileFilters.count * sizeof(FileFilter); - filter = 1; }; bool noParsing; @@ -1975,8 +2011,16 @@ class IDEWorkSpace : Window void DocumentSaved(Window document, const char * fileName) { - ideConfig.recentFiles.addRecent(fileName); - ideConfig.recentFiles.write(settingsContainer); + if(ide.workspace) + { + ide.workspace.recentFiles.addRecent(fileName); + ide.workspace.Save(); + } + else + { + ideConfig.recentFiles.addRecent(fileName); + ideConfig.recentFiles.write(settingsContainer); + } ide.updateRecentFilesMenu(); ide.AdjustFileMenus(); } @@ -2403,16 +2447,48 @@ class IDEWorkSpace : Window return false; } + Window getOpenedFile(const char * filePath) + { + Window document = null; + for(document = firstChild; document; document = document.next) + { + const char * fileName = document.fileName; + if(document.isDocument && fileName && !fstrcmp(fileName, filePath) && document.created) + break; + } + return document; + } + Window OpenFile(const char * origFilePath, bool dontMaximize, bool visible, const char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod, bool noParsing) { char extension[MAX_EXTENSION] = ""; Window document = null; bool isProject = false; bool needFileModified = true; - char winFilePath[MAX_LOCATION]; - const char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath); Window currentDoc = activeClient; bool maximizeDoc = !dontMaximize && ((currentDoc && currentDoc.state == maximized) || (!currentDoc && !projectView)); + bool isUrl = strstr(origFilePath, "http://") == origFilePath; + char slashPath[MAX_LOCATION]; + char filePathBuffer[MAX_LOCATION]; + char rightPathBuffer[MAX_LOCATION]; + const char * filePath; + const char * rightPath = null; + + if(isUrl) + strcpy(filePathBuffer, origFilePath); + else + { + if(workspace) + { + GetSlashPathBuffer(slashPath, origFilePath); + rightPath = workspace.getRightPath(slashPath); + if(rightPath) + rightPath = GetSystemPathBuffer(rightPathBuffer, rightPath); + } + GetSystemPathBuffer(filePathBuffer, origFilePath); + } + filePath = rightPath ? rightPathBuffer : filePathBuffer; + if(!type) { GetExtension(filePath, extension); @@ -2423,16 +2499,15 @@ class IDEWorkSpace : Window if(strcmp(extension, ProjectExtension)) { - for(document = firstChild; document; document = document.next) + document = getOpenedFile(filePath); + if(!document && rightPath) + document = getOpenedFile(filePathBuffer); + if(document) { - const char * fileName = document.fileName; - if(document.isDocument && fileName && !fstrcmp(fileName, filePath) && document.created) - { - document.visible = true; - if(visible) - document.Activate(); - return document; - } + document.visible = true; + if(visible) + document.Activate(); + return document; } } @@ -2698,8 +2773,9 @@ class IDEWorkSpace : Window if(!document && createIfFails != no) { + String msg = PrintString($"\nFile doesn't exist.\n\nLocation: ", filePath, $"\n\nCreate?"); if(createIfFails != yes && !needFileModified && - MessageBox { type = yesNo, master = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes) + MessageBox { type = yesNo, master = this, text = $"File not found", contents = msg }.Modal() == yes) createIfFails = yes; if(createIfFails == yes || createIfFails == whatever) { @@ -2707,6 +2783,7 @@ class IDEWorkSpace : Window if(document) document.fileName = filePath; } + delete msg; } if(document) @@ -2727,7 +2804,10 @@ class IDEWorkSpace : Window ide.updateRecentProjectsMenu(); } else if(workspace) + { workspace.recentFiles.addRecent(document.fileName); + workspace.Save(); + } else { ideConfig.recentFiles.addRecent(document.fileName); @@ -2805,10 +2885,10 @@ class IDEWorkSpace : Window return true; } - void GoToError(const char * line, bool noParsing, const char * objectFileExt) + void GoToError(const char * line, bool openAsText, bool noParsing, const char * objectFileExt) { if(projectView) - projectView.GoToError(line, noParsing, objectFileExt); + projectView.GoToError(line, openAsText, noParsing, objectFileExt); } FileAttribs GoToCodeSelectFile(const char * filePath, const char * dir, Project prj, ProjectNode * node, char * selectedPath, const char * objectFileExt) @@ -2817,6 +2897,7 @@ class IDEWorkSpace : Window FileAttribs fileAttribs; if(filePath[0]) { + bool done = false; if(prj) strcpy(selectedPath, prj.topNode.path); else if(dir && dir[0]) @@ -2826,10 +2907,40 @@ class IDEWorkSpace : Window PathCat(selectedPath, filePath); if((fileAttribs = FileExists(selectedPath)).isFile) + { result = fileAttribs; - else if(workspace) + done = true; + } + else + { + char * path = CopyString(selectedPath); + StripLastDirectory(path, path); + if((fileAttribs = FileExists(path)) && (fileAttribs.isDirectory || fileAttribs.isDrive)) + { + int len = strlen(selectedPath); + char * d = &selectedPath[len - 1]; + char * stop = d - (len - strlen(path)); + while(d != stop) + { + *d = 0; + while(d != stop) + { + d--; + if(!isspace(*d)) break; + *d = 0; + } + if((fileAttribs = FileExists(selectedPath)).isFile) + { + result = fileAttribs; + done = true; + break; + } + } + } + delete path; + } + if(!done && workspace) { - bool done = false; for(p : workspace.projects) { strcpy(selectedPath, p.topNode.path); @@ -2870,7 +2981,7 @@ class IDEWorkSpace : Window return result; } - void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir, const char * objectFileExt) + void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir, bool openAsText, bool noParsing, const char * objectFileExt) { char *s = null; const char *path = text; @@ -2965,7 +3076,7 @@ class IDEWorkSpace : Window strcpy(filePath, path); } - if(filePath[0] && strstr(filePath, "$(")) + if(prj && filePath[0] && strstr(filePath, "$(")) { DirExpression pathExp { }; CompilerConfig compiler = GetCompilerConfig(); @@ -2978,10 +3089,10 @@ class IDEWorkSpace : Window } if((fileAttribs = GoToCodeSelectFile(filePath, dir, prj, null, completePath, objectFileExt))) - CodeLocationGoTo(completePath, fileAttribs, line, col); + CodeLocationGoTo(completePath, fileAttribs, line, col, openAsText, noParsing); } - void CodeLocationGoTo(const char * path, const FileAttribs fileAttribs, int line, int col) + void CodeLocationGoTo(const char * path, const FileAttribs fileAttribs, int line, int col, bool openAsText, bool noParsing) { if(fileAttribs.isFile) { @@ -2999,7 +3110,8 @@ class IDEWorkSpace : Window } else { - CodeEditor codeEditor = (CodeEditor)OpenFile(path, false, true, !strcmpi(ext, "epj") ? "txt" : ext, no, normal, false); + bool asText = (openAsText || !strcmpi(ext, ProjectExtension) || !strcmpi(ext, WorkspaceExtension)); + CodeEditor codeEditor = (CodeEditor)OpenFile(path, false, true, asText ? "txt" : ext, no, normal, noParsing); if(codeEditor && codeEditor._class == class(CodeEditor) && line) { EditBox editBox = codeEditor.editBox; @@ -3307,7 +3419,7 @@ class IDEWorkSpace : Window PathCat(fullPath, app.argv[c]); StripLastDirectory(fullPath, parentPath); GetExtension(app.argv[c], ext); - isProject = !openAsText && !strcmpi(ext, "epj"); + isProject = !openAsText && !strcmpi(ext, ProjectExtension); if(isProject && c > 1 + (ide.debugStart ? 1 : 0)) continue; @@ -3517,11 +3629,16 @@ class IDEWorkSpace : Window for(item : compiler.libraryDirs) { + char path[MAX_LOCATION]; if(!libPathExists[item]) // fstrcmp should be used { - String s = CopyString(item); - newLibPaths.Add(s); - libPathExists[s] = true; + DirExpression pathExp { }; + pathExp.Evaluate(item, projectView.project, compiler, config, bitDepth); + path[0] = '\0'; + PathCatSlash(path, pathExp.dir); + newLibPaths.Add(CopyString(path)); + libPathExists[path] = true; + delete pathExp; } } @@ -3641,40 +3758,14 @@ class IDEWorkSpace : Window void updateRecentFilesMenu() { - int c = 0; - char * itemPath = new char[MAX_LOCATION]; - char * itemName = new char[MAX_LOCATION+4]; Workspace ws = workspace; RecentPaths recentFiles = ws ? ws.recentFiles : ideConfig.recentFiles; - recentFilesMenu.Clear(); - for(recent : recentFiles) - { - strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0'; - MakeSystemPath(itemPath); - snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0'; - recentFilesMenu.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true); - c++; - } - delete itemPath; - delete itemName; + recentFiles.updateRecentMenu(ide, ide.FileRecentFile, recentFilesMenu); } void updateRecentProjectsMenu() { - int c = 0; - char * itemPath = new char[MAX_LOCATION]; - char * itemName = new char[MAX_LOCATION+4]; - recentProjectsMenu.Clear(); - for(recent : ideConfig.recentWorkspaces) - { - strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0'; - MakeSystemPath(itemPath); - snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0'; - recentProjectsMenu.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true); - c++; - } - delete itemPath; - delete itemName; + ideConfig.recentWorkspaces.updateRecentMenu(ide, ide.FileRecentProject, recentProjectsMenu); } ~IDEWorkSpace() diff --git a/ide/src/panels/OutputView.ec b/ide/src/panels/OutputView.ec index db7e1a35cd..c1795c24eb 100644 --- a/ide/src/panels/OutputView.ec +++ b/ide/src/panels/OutputView.ec @@ -9,6 +9,44 @@ enum OutputViewTab { build, debug, find #endif }; +enum CompilerMessageType +{ + nil, any, error, other, warning, note, location, unusedFunc, unusedVar, varSetButNotUsed; + + bool match(CompilerMessageType b) + { + CompilerMessageType a = this; + if(b != nil && (b == any || + (b == other && (a == unusedFunc || a == unusedVar || a == varSetButNotUsed)) || + b == a)) + return true; + return false; + } + + CompilerMessageType ::fromKeyCode(KeyCode code) + { + switch(code) + { + case a: return any; + case e: return error; + case o: return other; + case w: return warning; + case n: return note; + case l: return location; + case f: return unusedFunc; + case v: return unusedVar; + case s: return varSetButNotUsed; + } + return nil; + } +}; + +struct BuildOutputLineMark +{ + CompilerMessageType type; + int lineNumber; +}; + class OutputView : Window { visible = false; @@ -21,8 +59,8 @@ class OutputView : Window size.h = 240; background = formColor; - virtual void OnGotoError(const char * line, bool noParsing); - virtual void OnCodeLocationParseAndGoTo(const char * line); + virtual void OnGotoError(const char * line, bool openAsText, bool noParsing); + virtual void OnCodeLocationParseAndGoTo(const char * line, bool openAsText, bool noParsing); FindDialog findDialog { master = this, editBox = buildBox, isModal = true, autoCreate = false, text = $"Find" }; @@ -81,6 +119,12 @@ class OutputView : Window }; #endif + Button autoGo + { + this, inactive = true, text = $"Automatic Go To Line", isCheckbox = true, hotKey = ctrlSpace, checked = true; + anchor = { top = 4, right = 4 }; + }; + void SelectTab(OutputViewTab tab) { Button activeBtn = null; @@ -94,6 +138,7 @@ class OutputView : Window else if(tab == gdb) activeBtn = gdbBtn, activeBox = gdbBox; #endif + autoGo.visible = tab == build; if(activeBtn && activeBox) { activeBtn.checked = true; @@ -131,21 +176,96 @@ class OutputView : Window bool NotifyDoubleClick(EditBox editBox, EditLine line, Modifiers mods) { - OnGotoError(editBox.line.text, mods.ctrl && mods.shift); + OnGotoError(editBox.line.text, mods.ctrl && mods.shift, mods.ctrl && !mods.shift); return false; } + bool OnKeyHit(Key key, unichar ch) + { + CompilerMessageType t = key.modifiers & { alt = true, ctrl = true } ? 0 : + CompilerMessageType::fromKeyCode(key.code); + if(t) + { + LogBox buildBox = this; + OutputView outputView = (OutputView)buildBox.parent; + Array marks = outputView.marks; + if(marks.count) + { + bool reverse = key.shift; + int count = marks.count; + int increment = reverse ? -1 : 1; + int bound = reverse ? -1 : count; + int firstPos = reverse ? count - 1 : 0; + int lastPos = reverse ? 0 : count - 1; + int lineNumber = buildBox.lineNumber + 1; + int nextPos = -1; + int endPos; + int pos; + for(pos = firstPos; pos != bound; pos += increment) + { + int num = marks[pos].lineNumber; + if((reverse && num < lineNumber) || (!reverse && num > lineNumber)) + { + nextPos = pos; + break; + } + } + if(nextPos == -1) + nextPos = firstPos; + endPos = nextPos - increment; + if(endPos == -1) + endPos = lastPos; + else if(endPos == bound) + endPos = firstPos; + while(1) + { + if(nextPos == bound) + nextPos = firstPos; + if(marks[nextPos].type.match(t)) + break; + if(nextPos == endPos) + { + nextPos = -1; + break; + } + nextPos += increment; + } + if(nextPos == -1 && marks.count && marks[marks.count - 1].type == nil) + nextPos = marks.count - 1; + if(nextPos != -1) + { + buildBox.GoToLineNum(marks[nextPos].lineNumber - 1); + if(outputView.autoGo.checked) + { + outputView.OnGotoError(this.line.text, false, false); + Activate(); + } + } + return true; + } + } + return EditBox::OnKeyHit(key, ch); + } + bool NotifyKeyDown(EditBox editBox, Key key, unichar ch) { if(key.code == enter || key.code == keyPadEnter) { - OnGotoError(editBox.line.text, key.ctrl && key.shift); + OnGotoError(editBox.line.text, key.ctrl && key.shift, key.ctrl && !key.shift); return false; } return true; } }; + void buildClear() + { + buildBox.Clear(); + marks.RemoveAll(); + } + + Array marks { }; + LogBox debugBox { parent = this, freeCaret = true, autoEmpty = true, multiLine = true; @@ -161,15 +281,15 @@ class OutputView : Window bool NotifyDoubleClick(EditBox editBox, EditLine line, Modifiers mods) { - OnCodeLocationParseAndGoTo(editBox.line.text); + OnCodeLocationParseAndGoTo(editBox.line.text, mods.ctrl && mods.shift, mods.ctrl && !mods.shift); return false; } bool NotifyKeyDown(EditBox editBox, Key key, unichar ch) { - if((SmartKey)key == enter) + if(key.code == enter || key.code == keyPadEnter) { - OnCodeLocationParseAndGoTo(editBox.line.text); + OnCodeLocationParseAndGoTo(editBox.line.text, key.ctrl && key.shift, key.ctrl && !key.shift); return false; } return true; @@ -191,15 +311,15 @@ class OutputView : Window bool NotifyDoubleClick(EditBox editBox, EditLine line, Modifiers mods) { - OnCodeLocationParseAndGoTo(editBox.line.text); + OnCodeLocationParseAndGoTo(editBox.line.text, mods.ctrl && mods.shift, mods.ctrl && !mods.shift); return false; } bool NotifyKeyDown(EditBox editBox, Key key, unichar ch) { - if((SmartKey)key == enter) + if(key.code == enter || key.code == keyPadEnter) { - OnCodeLocationParseAndGoTo(editBox.line.text); + OnCodeLocationParseAndGoTo(editBox.line.text, key.ctrl && key.shift, key.ctrl && !key.shift); return false; } return true; @@ -260,7 +380,7 @@ class OutputView : Window { Show(); if(tab == build) - buildBox.Clear(); + buildClear(); else if(tab == debug) debugBox.Clear(); else if(tab == find) diff --git a/ide/src/project/Project.ec b/ide/src/project/Project.ec index e43cece938..231a265852 100644 --- a/ide/src/project/Project.ec +++ b/ide/src/project/Project.ec @@ -27,7 +27,7 @@ default: static __attribute__((unused)) void DummyFunction() { -int a; +int a = 0; a.OnFree(); } @@ -71,7 +71,7 @@ void ParseArrayValue(Array array, char * equal) void ProjectNode::LegacyBinaryLoadNode(File f) { - int len, count, c; + int len = 0, count, c; int fileNameLen; f.Read(&len, sizeof(len), 1); @@ -608,7 +608,9 @@ void OutputFlags(File f, ToolchainFlag flag, Array list, LineOutputMetho if(lineMethod == lineEach) f.Puts(" \\\n\t"); f.Printf(" %s", flagNames[flag]); - if(flag != _D && flag != _Wl && flag != any) + if(flag == _D) + f.Printf("%s", item); + else if(flag != _Wl && flag != any) { char * tmp = new char[strlen(item)*2+1]; EscapeForMake(tmp, item, false, true, false); @@ -866,7 +868,7 @@ private: String lastBuildConfigName; String lastBuildCompilerName; - Map> configsNameCollisions { }; + Map> configsNameCollisions { }; #if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE) && !defined(TEST_SUITE) FileMonitor fileMonitor @@ -933,6 +935,8 @@ private: CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(projectView.workspace.activeCompiler); projectView.AddNode(topNode, null); topNode.row.Move(prev); + projectView.updateModified(); + projectView.Update(null); projectView.ShowOutputBuildLog(true); projectView.DisplayCompiler(compiler, false); @@ -1352,11 +1356,17 @@ private: void CatMakeFileName(char * string, ProjectConfig config) { - char projectName[MAX_LOCATION]; + char projectName[MAX_FILENAME]; strcpy(projectName, name); sprintf(string, "%s%s%s.Makefile", projectName, config ? "-" : "", config ? config.name : ""); } + void resolvePaths() + { + topNode.resolvePaths(); + resNode.resolvePaths(); + } + #if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE) && !defined(TEST_SUITE) ProjectNode GetObjectFileNode(const char * filePath, const char * objectFileExt) { @@ -1415,8 +1425,8 @@ private: void ModifiedAllConfigs(bool making, bool compiling, bool linking, bool symbolGen) { - Map cfgNameCollision; - MapIterator> it { map = configsNameCollisions }; + Map cfgNameCollision; + MapIterator> it { map = configsNameCollisions }; if(it.Index("", false)) { cfgNameCollision = it.data; @@ -1525,7 +1535,14 @@ private: int linePos = 0; bool compiling = false, linking = false, precompiling = false; int compilingEC = 0; - int numErrors = 0, numWarnings = 0; + int numErrors = 0; + int numWarnings = 0; + int numWarnNote = 0; + int numWarnLocation = 0; + int numWarnUnusedVar = 0; + int numWarnUnusedFunc = 0; + int numWarnSetButNotUsed = 0; + // todo: int counts[CompilerMessageType::enumSize]; bool loggedALine = false; int lenMakeCommand = strlen(compiler.makeCommand); int testLen = 0; @@ -1622,12 +1639,14 @@ private: ide.outputView.buildBox.Logf($" %s: No such file or directory\n", module); // ide.outputView.buildBox.Logf("error: %s\n No such file or directory\n", module); numErrors++; + ide.outputView.marks.Add({ error, ide.outputView.buildBox.numLines }); } } //else //{ //ide.outputView.buildBox.Logf("error: %s\n", line); //numErrors++; + //ide.outputView.marks.Add({ error, ide.outputView.buildBox.numLines }) //} } else if(strstr(test, "mkdir ") == test); @@ -1693,11 +1712,48 @@ private: } else { - ide.outputView.buildBox.Logf("%s\n", line); - if(strstr(line, "warning:") || strstr(line, "note:")) - numWarnings++; - else if(!gotCC && !strstr(line, "At top level") && !strstr(line, "In file included from") && !strstr(line, stringFrom)) + if(strstr(line, "warning:")) + { + char * sub; + if(strstr(line, " defined but not used") || strstr(line, "[-Wunused-function]")) + { + numWarnUnusedFunc++; + ide.outputView.marks.Add({ unusedFunc, ide.outputView.buildBox.numLines }); + } + if(strstr(line, ": unused variable ") || strstr(line, "[-Wunused-variable]")) + { + numWarnUnusedVar++; + ide.outputView.marks.Add({ unusedVar, ide.outputView.buildBox.numLines }); + } + else if(((sub = strstr(line, ": variable ")) && strstr(sub, " set but not used")) || + strstr(line, "[-Wunused-but-set-variable]")) + { + numWarnSetButNotUsed++; + ide.outputView.marks.Add({ varSetButNotUsed, ide.outputView.buildBox.numLines }); + } + else + { + numWarnings++; + ide.outputView.marks.Add({ warning, ide.outputView.buildBox.numLines }); + } + } + else if(strstr(line, "At top level:") || strstr(line, "In function ") || + strstr(line, "In file included from")) + { + numWarnLocation++; + ide.outputView.marks.Add({ location, ide.outputView.buildBox.numLines }); + } + else if(strstr(line, "note:")) + { + numWarnNote++; + ide.outputView.marks.Add({ note, ide.outputView.buildBox.numLines }); + } + else if(!gotCC && !strstr(line, stringFrom)) + { numErrors++; + ide.outputView.marks.Add({ error, ide.outputView.buildBox.numLines }); + } + ide.outputView.buildBox.Logf("%s\n", line); } if(compilingEC) compilingEC--; @@ -1798,6 +1854,7 @@ private: else { numErrors++; + ide.outputView.marks.Add({ CompilerMessageType::error, ide.outputView.buildBox.numLines }); message = $"Linker Error: "; } } @@ -1809,11 +1866,13 @@ private: { message = $"Linker "; numWarnings++; + ide.outputView.marks.Add({ warning, ide.outputView.buildBox.numLines }); } else if(strstr(line, "error:")) { message = $"Linker Error: "; numErrors++; + ide.outputView.marks.Add({ CompilerMessageType::error, ide.outputView.buildBox.numLines }); } } else @@ -1824,7 +1883,10 @@ private: } error = strstr(line, "error:"); if(!message && error && error > colon) + { numErrors++; + ide.outputView.marks.Add({ CompilerMessageType::error, ide.outputView.buildBox.numLines }); + } else { // Silence warnings for compiled eC @@ -1852,12 +1914,12 @@ private: } else //if(compilingEC == 1 || (objDir && objDir == moduleName)) { - bool skip = false; - // Filter out these warnings caused by eC generated C code: // Declaration ordering bugs -- Awaiting topo sort implementation + /* + bool skip = false; if(strstr(line, "declared inside parameter list")) skip = true; else if(strstr(line, "its scope is only this definition")) skip = true; else if(strstr(line, "from incompatible pointer type")) skip = true; @@ -1887,9 +1949,50 @@ private: else if(strstr(line, "In function '") || strstr(line, "In function ‘") ) skip = true; else if(strstr(line, "At top level")) skip = true; else if(strstr(line, "(near initialization for '") || strstr(line, "(near initialization for ‘")) skip = true; - */ if(skip) continue; - numWarnings++; + */ + + if(strstr(line, "warning:")) + { + char * sub; + if(strstr(line, " defined but not used") || strstr(line, "[-Wunused-function]")) + { + numWarnUnusedFunc++; + ide.outputView.marks.Add({ unusedFunc, ide.outputView.buildBox.numLines }); + } + else if(strstr(line, ": unused variable ") || strstr(line, "[-Wunused-variable]")) + { + numWarnUnusedVar++; + ide.outputView.marks.Add({ unusedVar, ide.outputView.buildBox.numLines }); + } + else if(((sub = strstr(line, ": variable ")) && strstr(sub, " set but not used")) || + strstr(line, "[-Wunused-but-set-variable]")) + { + numWarnSetButNotUsed++; + ide.outputView.marks.Add({ varSetButNotUsed, ide.outputView.buildBox.numLines }); + } + else + { + numWarnings++; + ide.outputView.marks.Add({ warning, ide.outputView.buildBox.numLines }); + } + } + else if(strstr(line, "At top level:") || strstr(line, "In function ") || + strstr(line, "In file included from")) + { + numWarnLocation++; + ide.outputView.marks.Add({ location, ide.outputView.buildBox.numLines }); + } + else if(strstr(line, "note:")) + { + numWarnNote++; + ide.outputView.marks.Add({ note, ide.outputView.buildBox.numLines }); + } + else + { + numWarnings++; + ide.outputView.marks.Add({ warning, ide.outputView.buildBox.numLines }); + } } /*else if(strstr(line, "warning:")) { @@ -2019,22 +2122,48 @@ private: } else if(buildType != install) { + int numOthers = numWarnUnusedFunc + numWarnUnusedVar + numWarnSetButNotUsed; + int numMore = numWarnNote + numWarnLocation + numOthers; + int numAllWarn = numWarnings + numOthers; + if(!onlyNodes || numErrors || numAllWarn) + ide.outputView.buildBox.Logf("\n"); + ide.outputView.marks.Add({ nil, ide.outputView.buildBox.numLines }); if(!onlyNodes) { char targetFileName[MAX_LOCATION]; targetFileName[0] = '\0'; CatTargetFileName(targetFileName, compiler, config); - ide.outputView.buildBox.Logf("\n%s (%s) - ", targetFileName, lastBuildConfigName); + ide.outputView.buildBox.Logf("%s (%s) - ", targetFileName, lastBuildConfigName); } if(numErrors) - ide.outputView.buildBox.Logf("%d %s, ", numErrors, (numErrors > 1) ? $"errors" : $"error"); + ide.outputView.buildBox.Logf("%d %s", numErrors, (numErrors > 1) ? $"[e]rrors" : $"[e]rror"); else - ide.outputView.buildBox.Logf($"no error, "); - - if(numWarnings) - ide.outputView.buildBox.Logf("%d %s\n", numWarnings, (numWarnings > 1) ? $"warnings" : $"warning"); + ide.outputView.buildBox.Logf("%s", $"no error"); + if(numAllWarn) + ide.outputView.buildBox.Logf(", %d %s", numAllWarn, (numAllWarn > 1) ? $"warnings" : $"warning"); else - ide.outputView.buildBox.Logf($"no warning\n"); + ide.outputView.buildBox.Logf(", %s", $"no warning"); + + if(numMore) + { + const char * comma = ""; + ide.outputView.buildBox.Logf(" ("); + if(numWarnings) + ide.outputView.buildBox.Logf("%s%d %s", comma, numWarnings, (numWarnings > 1) ? $"important [w]arnings" : $"important [w]arning"), comma = ", "; + if(numWarnSetButNotUsed) + ide.outputView.buildBox.Logf("%s%d %s", comma, numWarnSetButNotUsed, (numWarnSetButNotUsed > 1) ? $"variables [s]et but not used" : $"variable [s]et but not used"), comma = ", "; + if(numWarnUnusedFunc) + ide.outputView.buildBox.Logf("%s%d %s", comma, numWarnUnusedFunc, (numWarnUnusedFunc > 1) ? $"unused [f]unctions" : $"unused [f]unction"), comma = ", "; + if(numWarnUnusedVar) + ide.outputView.buildBox.Logf("%s%d %s", comma, numWarnUnusedVar, (numWarnUnusedVar > 1) ? $"unused [v]ariables" : $"unused [v]ariable"), comma = ", "; + if(numWarnLocation) + ide.outputView.buildBox.Logf("%s%d %s", comma, numWarnLocation, (numWarnLocation > 1) ? $"locations" : $"location"), comma = ", "; + if(numWarnNote) + ide.outputView.buildBox.Logf("%s%d %s", comma, numWarnNote, (numWarnNote > 1) ? $"notes" : $"note"), comma = ", "; + ide.outputView.buildBox.Logf("%s%d %s", comma, numOthers, (numOthers > 1) ? $"[o]ther warnings" : $"[o]ther warning"); + ide.outputView.buildBox.Logf(")"); + } + ide.outputView.buildBox.Logf("\n"); } } @@ -2106,10 +2235,11 @@ private: bool eC_Debug = mode.eC_ToolsDebug; bool singleProjectOnlyNode = onlyNodes && onlyNodes.count == 1 && onlyNodes[0].type == project; - int numJobs = compiler.numJobs; - char command[MAX_F_STRING*4]; + int numJobs = ide.toolBar.forceSingleJob.checked == true ? 1 : compiler.numJobs; + char * workspaceEnvironmentVars = ide.workspace.getEnvVarsString(); + char command[MAX_LOCATION*8 + strlen(workspaceEnvironmentVars)]; char * compilerName = CopyString(compiler.name); - Map cfgNameCollisions; + Map cfgNameCollisions; delete lastBuildConfigName; lastBuildConfigName = CopyString(config ? config.name : "Common"); @@ -2129,6 +2259,11 @@ private: CatMakeFileName(makeFile, config); PathCatSlash(makeFilePath, makeFile); +#ifdef __APPLE__ + const char dyldPath[2048]; + GetEnvironment("DYLD_LIBRARY_PATH", dyldPath, sizeof(dyldPath)); +#endif + // TODO: TEST ON UNIX IF \" around makeTarget is ok if(buildType == install) makeTargets.concat(" install"); @@ -2148,12 +2283,18 @@ private: // Create object dir if it does not exist already if(!FileExists(objDirExp.dir).isDirectory) { - sprintf(command, "%s CF_DIR=\"%s\"%s%s%s%s%s COMPILER=%s objdir -C \"%s\"%s%s -f \"%s\"", +#ifdef __APPLE__ + sprintf(command, "%s CF_DIR=\"%s\" DYLD_LIBRARY_PATH=\"%s\" %s%s%s%s%s%s COMPILER=%s objdir -C \"%s\"%s%s -f \"%s\"", + compiler.makeCommand, cfDir, dyldPath, +#else + sprintf(command, "%s CF_DIR=\"%s\"%s%s%s%s%s%s COMPILER=%s objdir -C \"%s\"%s%s -f \"%s\"", compiler.makeCommand, cfDir, +#endif crossCompiling ? " TARGET_PLATFORM=" : "", targetPlatform, bitDepth ? " ARCH=" : "", bitDepth == 32 ? "32" : bitDepth == 64 ? "64" : "", /*(bitDepth == 64 && compiler.targetPlatform == win32) ? " GCC_PREFIX=x86_64-w64-mingw32-" : (bitDepth == 32 && compiler.targetPlatform == win32) ? " GCC_PREFIX=i686-w64-mingw32-" : */"", + workspaceEnvironmentVars, compilerName, topNode.path, outputMode == verbose ? " V=1" : "", outputMode == justPrint ? " -n" : "", makeFilePath); if(outputMode != normal) @@ -2191,6 +2332,7 @@ private: if((f = DualPipeOpen(PipeOpenMode { output = true, error = true/*, input = true*/ }, command))) { ProcessPipeOutputRaw(f); + f.Wait(); delete f; //result = true; } @@ -2206,7 +2348,11 @@ private: GccVersionInfo cxxVersion = GetGccVersionInfo(compiler, compiler.cxxCommand); char cfDir[MAX_LOCATION]; GetIDECompilerConfigsDir(cfDir, true, true); - sprintf(command, "%s%s %sCF_DIR=\"%s\"%s%s%s%s%s%s COMPILER=%s%s %s%s%s-j%d %s%s%s -C \"%s\"%s%s -f \"%s\"", +#ifdef __APPLE__ + sprintf(command, "%s%s %sCF_DIR=\"%s\" DYLD_LIBRARY_PATH=\"%s\" %s%s%s%s%s%s%s COMPILER=%s%s %s%s%s-j%d %s%s%s -C \"%s\"%s%s -f \"%s\"", +#else + sprintf(command, "%s%s %sCF_DIR=\"%s\"%s%s%s%s%s%s%s COMPILER=%s%s %s%s%s-j%d %s%s%s -C \"%s\"%s%s -f \"%s\"", +#endif #if defined(__WIN32__) "", #else @@ -2215,12 +2361,16 @@ private: compiler.makeCommand, mode == debugPrecompile ? "ECP_DEBUG=y " : mode == debugCompile ? "ECC_DEBUG=y " : mode == debugGenerateSymbols ? "ECS_DEBUG=y " : "", cfDir, +#ifdef __APPLE__ + dyldPath, +#endif crossCompiling ? " TARGET_PLATFORM=" : "", targetPlatform, bitDepth ? " ARCH=" : "", bitDepth == 32 ? "32" : bitDepth == 64 ? "64" : "", ide.workspace.useValgrind ? " DISABLED_POOLING=1" : "", /*(bitDepth == 64 && compiler.targetPlatform == win32) ? " GCC_PREFIX=x86_64-w64-mingw32-" : (bitDepth == 32 && compiler.targetPlatform == win32) ? " GCC_PREFIX=i686-w64-mingw32-" :*/ "", + workspaceEnvironmentVars, compilerName, compiler.noStripTarget ? " NOSTRIP=y" : "", eC_Debug ? "--always-make " : "", @@ -2271,6 +2421,7 @@ private: result = ProcessPipeOutputRaw(f); else result = ProcessBuildPipeOutput(f, objDirExp, buildType, onlyNodes, compiler, config, bitDepth); + f.Wait(); delete f; if(error) ide.outputView.buildBox.Logf("%s\n", command); @@ -2287,6 +2438,7 @@ private: delete objDirExp; delete compilerName; delete makeTargets; + delete workspaceEnvironmentVars; return result; } @@ -2294,7 +2446,8 @@ private: { char makeFile[MAX_LOCATION]; char makeFilePath[MAX_LOCATION]; - char command[MAX_LOCATION]; + char * workspaceEnvironmentVars = ide.workspace.getEnvVarsString(); + char command[MAX_LOCATION*8 + strlen(workspaceEnvironmentVars)]; char * compilerName; DualPipe f; PathBackup pathBackup { }; @@ -2324,6 +2477,7 @@ private: if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command))) { ProcessPipeOutputRaw(f); + f.Wait(); delete f; //result = true; } @@ -2334,10 +2488,11 @@ private: { char cfDir[MAX_LOCATION]; GetIDECompilerConfigsDir(cfDir, true, true); - sprintf(command, "%s CF_DIR=\"%s\"%s%s%s%s COMPILER=%s %sclean%s -C \"%s\"%s%s -f \"%s\"", + sprintf(command, "%s CF_DIR=\"%s\"%s%s%s%s%s COMPILER=%s %sclean%s -C \"%s\"%s%s -f \"%s\"", compiler.makeCommand, cfDir, crossCompiling ? " TARGET_PLATFORM=" : "", targetPlatform, bitDepth ? " ARCH=" : "", bitDepth == 32 ? "32" : bitDepth == 64 ? "64" : "", + workspaceEnvironmentVars, compilerName, cleanType == realClean ? "real" : "", cleanType == cleanTarget ? "target" : "", topNode.path, outputMode == verbose ? " V=1" : "", outputMode == justPrint ? " -n": "", makeFilePath); @@ -2355,12 +2510,14 @@ private: ide.outputView.buildBox.Logf($"%s%s deleted\n", cleanType == realClean ? $"Intermediate objects directory" : $"Target", cleanType == clean ? $" and object files" : ""); + f.Wait(); delete f; } } delete pathBackup; delete compilerName; + delete workspaceEnvironmentVars; } void Run(const char * args, CompilerConfig compiler, ProjectConfig config, int bitDepth, bool shellOpen) @@ -2377,7 +2534,7 @@ private: PathCatSlash(target, targetDirExp.dir); CatTargetFileName(target, compiler, config); if(args[0] && (executableLauncher || !shellOpen)) - sprintf(target, "%s %s", target, args); + strcatf(target, " %s", args); GetWorkingDir(oldDirectory, MAX_LOCATION); if(ide.workspace.debugDir && strlen(ide.workspace.debugDir)) @@ -2402,6 +2559,24 @@ private: } else if(shellOpen) ShellOpen(target); +#ifdef __APPLE__ + else if(true) + { + char * prefixedTarget = new char[2080 + strlen(target) + 8]; + char dyldPath[2048]; + + SetPath(false, compiler, config, bitDepth); //true + + GetEnvironment("DYLD_LIBRARY_PATH", dyldPath, sizeof(dyldPath)); + strcpy(prefixedTarget, "DYLD_LIBRARY_PATH=\""); + strcat(prefixedTarget, dyldPath); + strcat(prefixedTarget, "\" "); + strcat(prefixedTarget, target); + PrintLn("Executing: ", prefixedTarget); + Execute(prefixedTarget); + delete prefixedTarget; + } +#endif else Execute(target); @@ -2583,11 +2758,27 @@ private: f.Printf("CPP := $(CCACHE_COMPILE)$(DISTCC_COMPILE)$(GCC_PREFIX)%s$(_SYSROOT)\n", compiler.cppCommand); f.Printf("CC := $(CCACHE_COMPILE)$(DISTCC_COMPILE)$(GCC_PREFIX)%s$(_SYSROOT)$(if $(GCC_CC_FLAGS),$(space)$(GCC_CC_FLAGS),)\n", compiler.ccCommand); f.Printf("CXX := $(CCACHE_COMPILE)$(DISTCC_COMPILE)$(GCC_PREFIX)%s$(_SYSROOT)$(if $(GCC_CXX_FLAGS),$(space)$(GCC_CXX_FLAGS),)\n", compiler.cxxCommand); + // TODO: Improve on all this... +#ifdef __APPLE__ + if(eC) + { + f.Printf("ECP := DYLD_LIBRARY_PATH=$(DYLD_LIBRARY_PATH) $(if $(ECP_DEBUG),ecere-ide \"$(ECERE_SDK_SRC)/compiler/ecp/ecp.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(GCC_CC_FLAGS),$(space)$(GCC_CC_FLAGS),)\n", compiler.ecpCommand); + f.Printf("ECC := DYLD_LIBRARY_PATH=$(DYLD_LIBRARY_PATH) $(if $(ECC_DEBUG),ecere-ide \"$(ECERE_SDK_SRC)/compiler/ecc/ecc.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)$(if $(GCC_CC_FLAGS),$(space)$(GCC_CC_FLAGS),)\n", compiler.eccCommand); + f.Printf("ECS := DYLD_LIBRARY_PATH=$(DYLD_LIBRARY_PATH) $(if $(ECS_DEBUG),ecere-ide \"$(ECERE_SDK_SRC)/compiler/ecs/ecs.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)$(if $(OUTPUT_POT), -outputpot,)$(if $(DISABLED_POOLING), -disabled-pooling,)\n", compiler.ecsCommand); + } + else + { + f.Printf("ECP := DYLD_LIBRARY_PATH=$(DYLD_LIBRARY_PATH) %s\n", compiler.ecpCommand); + f.Printf("ECC := DYLD_LIBRARY_PATH=$(DYLD_LIBRARY_PATH) %s$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)\n", compiler.eccCommand); + f.Printf("ECS := DYLD_LIBRARY_PATH=$(DYLD_LIBRARY_PATH) %s$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)$(if $(OUTPUT_POT), -outputpot,)$(if $(DISABLED_POOLING), -disabled-pooling,)\n", compiler.ecsCommand); + } + f.Printf("EAR := DYLD_LIBRARY_PATH=$(DYLD_LIBRARY_PATH) %s\n", compiler.earCommand); +#else if(eC) { - f.Printf("ECP := $(if $(ECP_DEBUG),ecere-ide -debug-start \"$(ECERE_SDK_SRC)/compiler/ecp/ecp.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(GCC_CC_FLAGS),$(space)$(GCC_CC_FLAGS),)\n", compiler.ecpCommand); - f.Printf("ECC := $(if $(ECC_DEBUG),ecere-ide -debug-start \"$(ECERE_SDK_SRC)/compiler/ecc/ecc.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)$(if $(GCC_CC_FLAGS),$(space)$(GCC_CC_FLAGS),)\n", compiler.eccCommand); - f.Printf("ECS := $(if $(ECS_DEBUG),ecere-ide -debug-start \"$(ECERE_SDK_SRC)/compiler/ecs/ecs.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)$(if $(OUTPUT_POT), -outputpot,)$(if $(DISABLED_POOLING), -disabled-pooling,)\n", compiler.ecsCommand); + f.Printf("ECP := $(if $(ECP_DEBUG),ecere-ide \"$(ECERE_SDK_SRC)/compiler/ecp/ecp.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(GCC_CC_FLAGS),$(space)$(GCC_CC_FLAGS),)\n", compiler.ecpCommand); + f.Printf("ECC := $(if $(ECC_DEBUG),ecere-ide \"$(ECERE_SDK_SRC)/compiler/ecc/ecc.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)$(if $(GCC_CC_FLAGS),$(space)$(GCC_CC_FLAGS),)\n", compiler.eccCommand); + f.Printf("ECS := $(if $(ECS_DEBUG),ecere-ide \"$(ECERE_SDK_SRC)/compiler/ecs/ecs.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)$(if $(OUTPUT_POT), -outputpot,)$(if $(DISABLED_POOLING), -disabled-pooling,)\n", compiler.ecsCommand); } else { @@ -2596,7 +2787,7 @@ private: f.Printf("ECS := %s$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)$(if $(OUTPUT_POT), -outputpot,)$(if $(DISABLED_POOLING), -disabled-pooling,)\n", compiler.ecsCommand); } f.Printf("EAR := %s\n", compiler.earCommand); - +#endif f.Puts("AS := $(GCC_PREFIX)as\n"); f.Printf("LD := "); if(compiler.ldCommand && compiler.ldCommand[0]) @@ -2643,7 +2834,7 @@ private: f.Puts("endif\n\n"); f.Puts("# HARD CODED TARGET_PLATFORM-SPECIFIC OPTIONS\n"); - f.Printf("LDFLAGS +=$(if $(%s), -Wl$(comma)--no-undefined,)\n", PlatformToMakefileTargetVariable(tux)); + f.Printf("LDFLAGS +=$(if $(%s), $(if $(__EMSCRIPTEN__),-Wl$(comma)--no-undefined,),)\n", PlatformToMakefileTargetVariable(tux)); f.Puts("\n"); // JF's @@ -2701,6 +2892,12 @@ private: OutputFlags(f, any, compiler.cxxFlags, inPlace); f.Puts("\n"); } + if(compiler.prepDirectives && compiler.prepDirectives.count) + { + f.Puts("\nCXXFLAGS +="); + OutputFlags(f, _D, compiler.prepDirectives, inPlace); + f.Puts("\n"); + } if(compiler.linkerFlags && compiler.linkerFlags.count) { f.Puts("\nLDFLAGS +="); @@ -2750,7 +2947,7 @@ private: if(f) { bool test; - int ifCount; + int ifCount = 0; Platform platform; char targetDir[MAX_LOCATION]; char objDirExpNoSpaces[MAX_LOCATION]; @@ -2777,7 +2974,7 @@ private: int rcSourcesParts = 0; Array listItems { }; Map varStringLenDiffs { }; - Map namesInfo { }; + Map namesInfo { }; Map cflagsVariations { }; Map nodeCFlagsMapping { }; @@ -3625,7 +3822,7 @@ private: if(numCObjects) GenMakefilePrintMainObjectRule(f, config); - f.Printf("cleantarget: objdir%s\n", sameOrRelObjTargetDirs ? "" : " targetdir"); + f.Printf("cleantarget:%s\n", sameOrRelObjTargetDirs ? "" : " targetdir"); if(numCObjects) { f.Printf("\t$(call rm,%s)\n", "$(OBJ)$(MODULE).main$(O) $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)"); @@ -3787,6 +3984,7 @@ private: } if(!result) break; } + dep.Wait(); delete dep; // If we failed to generate dependencies... @@ -3856,7 +4054,7 @@ private: static inline void ProjectLoadLastBuildNamesInfo(Project prj, ProjectConfig cfg) { const char * cfgName = cfg ? cfg.name : ""; - Map cfgNameCollisions = prj.configsNameCollisions[cfgName]; + Map cfgNameCollisions = prj.configsNameCollisions[cfgName]; if(cfgNameCollisions) { cfgNameCollisions.Free(); @@ -4709,7 +4907,7 @@ Project LoadProject(const char * filePath, const char * activeConfigName) project.resNode = project.topNode.Add(project, "Resources", project.topNode.files.last, resources, archiveFile, false); delete project.resNode.path; - project.resNode.path = project.resourcesPath; + project.resNode.path = project.resourcesPath ? project.resourcesPath : CopyString(""); project.resourcesPath = null; project.resNode.nodeType = (ProjectNodeType)-1; delete project.resNode.files; @@ -4719,6 +4917,8 @@ Project LoadProject(const char * filePath, const char * activeConfigName) if(!project.configurations) project.configurations = { }; project.resNode.FixupNode(insidePath); + + project.resolvePaths(); } delete parser; } @@ -4796,6 +4996,8 @@ static GccVersionInfo GetGccVersionInfo(CompilerConfig compiler, const String co bool inPar = false; for(i = 0; i < count; i++) { + if(!strcmpi(tokens[i], "clang")) + break; if(tokens[i][0] == '(') { if(tokens[i][strlen(tokens[i])-1] != ')') @@ -4812,6 +5014,7 @@ static GccVersionInfo GetGccVersionInfo(CompilerConfig compiler, const String co } } } + f.Wait(); delete f; } } diff --git a/ide/src/project/ProjectConfig.ec b/ide/src/project/ProjectConfig.ec index 45a1633949..7bdac99c98 100644 --- a/ide/src/project/ProjectConfig.ec +++ b/ide/src/project/ProjectConfig.ec @@ -2,6 +2,21 @@ import "Project" enum DirExpressionType { unknown, targetDir, intermediateObjectsDir }; // "type" is not right +static inline void evalAppendEnvVarValToBuffer(char * buffer, int * d, int * c, int * i, char * val, bool * matched) +{ + buffer[*d] = '\0'; +#if defined(__WIN32__) + ChangeCh(val, '\\', '/'); +#endif + strcat(buffer + *d, val); +#if defined(__WIN32__) + ChangeCh(val, '/', '\\'); +#endif + *d += strlen(val); + *c = *i; + *matched = true; +} + class DirExpression : struct { char * dir; @@ -135,23 +150,28 @@ class DirExpression : struct } else { +#if !defined(ECERE_EPJ2MAKE) + if(ide.workspace && ide.workspace.environmentVars && ide.workspace.environmentVars.count) + { + for(ev : ide.workspace.environmentVars; + ev.name && ev.string && ev.name[0] && ev.string[0] && + !strnicmp(&expr[c + 2], ev.name, n) && strlen(ev.name) == n) + { + evalAppendEnvVarValToBuffer(buffer, &d, &c, &i, ev.string, &matched); + break; + } + } +#endif if(compiler && compiler.environmentVars && compiler.environmentVars.count) + { for(ev : compiler.environmentVars; - ev.name && ev.string && ev.name[0] && ev.string[0] && !strnicmp(&expr[c + 2], ev.name, n) && strlen(ev.name) == n) + ev.name && ev.string && ev.name[0] && ev.string[0] && + !strnicmp(&expr[c + 2], ev.name, n) && strlen(ev.name) == n) { - buffer[d] = '\0'; -#if defined(__WIN32__) - ChangeCh(ev.string, '\\', '/'); -#endif - strcat(buffer, ev.string); -#if defined(__WIN32__) - ChangeCh(ev.string, '/', '\\'); -#endif - d += strlen(ev.string); - c = i; - matched = true; + evalAppendEnvVarValToBuffer(buffer, &d, &c, &i, ev.string, &matched); break; } + } if(!matched) { char name[512]; diff --git a/ide/src/project/ProjectNode.ec b/ide/src/project/ProjectNode.ec index 58603e8c7b..02134fb4b3 100644 --- a/ide/src/project/ProjectNode.ec +++ b/ide/src/project/ProjectNode.ec @@ -9,6 +9,10 @@ import "ecere" import "Project" +#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE) && !defined(TEST_SUITE) +import "FileSystemIterator" +#endif + static define app = ((GuiApplication)__thisModule); #endif @@ -447,6 +451,10 @@ private: // This is only set for Top Nodes Project project; + char * absolutePath; // absolute path for any type of node pointing to a file or folder + char * realPath; // real (symlinks resolved) absolute path if different from absolutePath + AVLTree paths { }; + void OpenRulesPlatformExclusionIfs(File f, int * ifCount, Array platforms) { if(!platforms.Find(unknown)) // unknown is "Common" @@ -550,7 +558,7 @@ private: return buffer; } - char * GetObjectFileName(char * buffer, Map namesInfo, IntermediateFileType type, bool dotMain, const char * objectFileExt) + char * GetObjectFileName(char * buffer, Map namesInfo, IntermediateFileType type, bool dotMain, const char * objectFileExt) { if(buffer && (this.type == file || (this.type == project && dotMain == true))) { @@ -795,6 +803,9 @@ private: } } + if(!path) + path = CopyString(""); + indent = parent ? parent.indent + 1 : 0; if(type == file) @@ -818,11 +829,63 @@ private: } } - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + void resolvePaths() + { + char path[MAX_LOCATION]; + GetFullFilePath(path, true, true); + if(path[0] && !paths.Find(path)) + { + delete absolutePath; + absolutePath = CopyString(path); + paths.Add(absolutePath); + } +#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE) && !defined(TEST_SUITE) + if(absolutePath) + { + getRealPath(absolutePath, path); + if(path[0] && !paths.Find(path)) + { + delete realPath; + realPath = CopyString(path); + paths.Add(realPath); + } + } +#endif + + if(files) + { + for(f : files) + { + f.resolvePaths(); + } + } + } + + const char * getRightPath(const char * filePath) { - if(!needClass) + const char * rightPath = null; + if(paths.Find(filePath)) + rightPath = absolutePath ? absolutePath : filePath; + + if(!rightPath && files) { - // TOCHECK: Called from JSON writer + for(f : files) + { + rightPath = f.getRightPath(filePath); + if(rightPath) break; + } + } + + if(rightPath && (rightPath == filePath || !fstrcmp(filePath, rightPath))) + rightPath = null; + return rightPath; + } + + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) + { + if(onType && *onType) + { + // TOCHECK: Called from ECON/JSON writer if(nodeType == file && !property::options && !property::configurations && !property::platforms && name) { strcpy(tempString, "\""); @@ -859,6 +922,10 @@ private: delete configurations; } + delete realPath; + delete absolutePath; + paths.Free(); + ///////////////////////////// delete path; delete name; @@ -1154,7 +1221,7 @@ private: return result; } - ProjectNode FindByObjectFileName(const char * fileName, IntermediateFileType type, bool dotMain, Map namesInfo, const char * objectFileExt) + ProjectNode FindByObjectFileName(const char * fileName, IntermediateFileType type, bool dotMain, Map namesInfo, const char * objectFileExt) { char p[MAX_LOCATION]; ProjectNode result = null; @@ -1462,7 +1529,7 @@ private: return false; } - void GenMakefileGetNameCollisionInfo(Map namesInfo, ProjectConfig prjConfig) + void GenMakefileGetNameCollisionInfo(Map namesInfo, ProjectConfig prjConfig) { if(type == file) { @@ -1512,7 +1579,7 @@ private: } int GenMakefilePrintNode(File f, Project project, GenMakefilePrintTypes printType, - Map namesInfo, Array items, + Map namesInfo, Array items, ProjectConfig prjConfig, bool * containsCXX) { int count = 0; @@ -1709,6 +1776,7 @@ private: } if(!result) break; } + dep.Wait(); delete dep; // If we failed to generate dependencies... @@ -1904,6 +1972,7 @@ private: } if(!result) break; } + dep.Wait(); delete dep; // If we failed to generate dependencies... @@ -1955,7 +2024,7 @@ private: } void GenMakefilePrintObjectRules(File f, Project project, - Map namesInfo, + Map namesInfo, ProjectConfig prjConfig, //Map parentExcludedPlatforms, Map nodeCFlagsMapping, Map nodeECFlagsMapping) @@ -2066,6 +2135,7 @@ private: } if(!result) break; } + dep.Wait(); delete dep; // If we failed to generate dependencies... @@ -2432,7 +2502,7 @@ private: return platforms; } - void GetTargets(ProjectConfig prjConfig, Map namesInfo, char * objDir, const char * objectFileExt, DynamicString output) + void GetTargets(ProjectConfig prjConfig, Map namesInfo, char * objDir, const char * objectFileExt, DynamicString output) { char moduleName[MAX_FILENAME]; if(type == file) @@ -2529,7 +2599,7 @@ private: } } - void DeleteIntermediateFiles(CompilerConfig compiler, ProjectConfig prjConfig, int bitDepth, Map namesInfo, bool onlyCObject) + void DeleteIntermediateFiles(CompilerConfig compiler, ProjectConfig prjConfig, int bitDepth, Map namesInfo, bool onlyCObject) { if(type == file) { @@ -2671,7 +2741,7 @@ static ProjectOptions BlendFileConfigPlatformProjectOptions(ProjectNode node, Pr for(s : strings) { bool found = false; - char priorityMark[10]; + char priorityMark[16]; order++; if(priority) sprintf(priorityMark, "%06d\n", priority * 1000 + order); diff --git a/ide/src/project/ProjectView.ec b/ide/src/project/ProjectView.ec index d09b64127b..5b840c8a03 100644 --- a/ide/src/project/ProjectView.ec +++ b/ide/src/project/ProjectView.ec @@ -329,7 +329,7 @@ class ProjectView : Window } MenuItem { pop, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings }; MenuDivider { pop }; - MenuItem { pop, $"Browse Folder", w, NotifySelect = MenuBrowseFolder }; + MenuItem { pop, $"Browse Folder(s)", w, NotifySelect = MenuBrowseFolder }; MenuDivider { pop }; MenuItem { pop, $"Save", v, Key { s, ctrl = true }, NotifySelect = ProjectSave }.disabled = !node.modified; MenuDivider { pop }; @@ -341,7 +341,7 @@ class ProjectView : Window MenuItem { pop, $"New Folder...", n, Key { f, ctrl = true }, NotifySelect = ProjectNewFolder }; MenuItem { pop, $"Import Folder...", i, NotifySelect = ProjectImportFolder }; MenuItem { pop, $"Add Resources to Project...", f, NotifySelect = ResourcesAddFiles }; - MenuItem { pop, $"Browse Folder", w, NotifySelect = MenuBrowseFolder }; + MenuItem { pop, $"Browse Folder(s)", w, NotifySelect = MenuBrowseFolder }; MenuDivider { pop }; MenuItem { pop, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings }; MenuItem { pop, $"Properties...", p, Key { enter, alt = true }, NotifySelect = FileProperties }; @@ -366,7 +366,7 @@ class ProjectView : Window MenuDivider { pop }; MenuItem { pop, $"Remove", r, NotifySelect = FileRemoveFile }; MenuDivider { pop }; - MenuItem { pop, $"Browse Folder", w, NotifySelect = MenuBrowseFolder }; + MenuItem { pop, $"Browse Folder(s)", w, NotifySelect = MenuBrowseFolder }; MenuDivider { pop }; MenuItem { pop, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings }; MenuItem { pop, $"Properties..", p, Key { enter, alt = true }, NotifySelect = FileProperties }; @@ -398,7 +398,7 @@ class ProjectView : Window MenuDivider { pop }; MenuItem { pop, $"Remove", r, NotifySelect = FileRemoveFile }; MenuDivider { pop }; - MenuItem { pop, $"Browse Folder", w, NotifySelect = MenuBrowseFolder }; + MenuItem { pop, $"Browse Folder(s)", w, NotifySelect = MenuBrowseFolder }; MenuDivider { pop }; MenuItem { pop, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings }; MenuItem { pop, $"Properties...", p, Key { enter, alt = true }, NotifySelect = FileProperties }; @@ -483,6 +483,15 @@ class ProjectView : Window } break; } + case ctrlL: + { + if(node.type == project || node.type == folder || node.type == resources) + { + ProjectNewFile(null, 0); + return false; + } + break; + } case ctrlF7: { if(node.type == file) @@ -610,7 +619,7 @@ class ProjectView : Window //if(ide.findInFilesDialog && ide.findInFilesDialog.created && ide.findInFilesDialog.mode != directory) // ide.findInFilesDialog.SearchStop(); - ide.outputView.buildBox.Clear(); + ide.outputView.buildClear(); ide.outputView.debugBox.Clear(); //ide.outputView.findBox.Clear(); ide.callStackView.Clear(); @@ -1267,7 +1276,7 @@ class ProjectView : Window result = false; if(ProjectPrepareForToolchain(project, normal, true, true, compiler, config)) { - Map namesInfo { }; + Map namesInfo { }; project.topNode.GenMakefileGetNameCollisionInfo(namesInfo, config); for(node : nodes) { @@ -1607,17 +1616,38 @@ class ProjectView : Window bool MenuBrowseFolder(MenuItem selection, Modifiers mods) { - char folder[MAX_LOCATION]; - Project prj; - ProjectNode node = GetSelectedNode(true); - if(!node) - node = project.topNode; - prj = node.project; + char filePath[MAX_LOCATION]; + AVLTree folders { }; + OldList selected; + OldLink item; - strcpy(folder, prj.topNode.path); - if(node != prj.topNode) - PathCatSlash(folder, node.path); - ShellOpen(folder); + fileList.GetMultiSelection(selected); + for(item = selected.first; item; item = item.next) + { + DataRow row = item.data; + ProjectNode node = (ProjectNode)(intptr)row.tag; + node.GetFullFilePath(filePath, true, true); + while(!FileExists(filePath).isDirectory) + { + StripLastDirectory(filePath, filePath); + if(filePath[0] == '\0') + break; + } + if(filePath[0] != '\0') + folders.Add(CopyString(filePath)); + } + selected.Free(null); + if(folders.count) + { + for(e : folders) + ShellOpen(e); + folders.Free(); + } + else + { + MessageBox { master = ide, type = ok, text = $"Browse Folder(s)", contents = $"Couldn't open any folder." }.Modal(); + } + delete folders; return true; } @@ -1693,7 +1723,7 @@ class ProjectView : Window return result; } - void GoToError(const char * line, const bool noParsing, const char * objectFileExt) + void GoToError(const char * line, bool openAsText, bool noParsing, const char * objectFileExt) { char * colon; @@ -1883,7 +1913,8 @@ class ProjectView : Window if(ide.GoToCodeSelectFile(moduleName, null, project, null, filePath, objectFileExt)) { - codeEditor = (CodeEditor)ide.OpenFile(filePath, false, true, null, no, normal, noParsing); + codeEditor = (CodeEditor)ide.OpenFile(filePath, false, true, openAsText ? "txt" : null, no, normal, noParsing); + ide.RepositionWindows(false); } if(!codeEditor) @@ -1894,12 +1925,12 @@ class ProjectView : Window PathCatSlash(filePath, moduleName); } - codeEditor = (CodeEditor)ide.OpenFile(filePath, false, true, null, no, normal, noParsing); + codeEditor = (CodeEditor)ide.OpenFile(filePath, false, true, openAsText ? "txt" : null, no, normal, noParsing); if(!codeEditor && !strcmp(ext, "c")) { char ecName[MAX_LOCATION]; ChangeExtension(filePath, "ec", ecName); - codeEditor = (CodeEditor)ide.OpenFile(ecName, false, true, null, no, normal, noParsing); + codeEditor = (CodeEditor)ide.OpenFile(ecName, false, true, openAsText ? "txt" : null, no, normal, noParsing); } if(!codeEditor) { @@ -1914,9 +1945,7 @@ class ProjectView : Window if((node = prj.topNode.FindWithPath(path, false))) { - strcpy(filePath, prj.topNode.path); - PathCatSlash(filePath, node.path); - PathCatSlash(filePath, node.name); + node.GetFullFilePath(filePath, true, true); codeEditor = (CodeEditor)ide.OpenFile(filePath, false, true, null, no, normal, noParsing); if(codeEditor) break; @@ -1929,9 +1958,7 @@ class ProjectView : Window ProjectNode node; if((node = prj.topNode.FindWithPath(moduleName, false))) { - strcpy(filePath, prj.topNode.path); - PathCatSlash(filePath, node.path); - PathCatSlash(filePath, node.name); + node.GetFullFilePath(filePath, true, true); codeEditor = (CodeEditor)ide.OpenFile(filePath, false, true, null, no, normal, noParsing); if(codeEditor) break; @@ -1946,9 +1973,7 @@ class ProjectView : Window ProjectNode node; if((node = prj.topNode.Find(moduleName, false))) { - strcpy(filePath, prj.topNode.path); - PathCatSlash(filePath, node.path); - PathCatSlash(filePath, node.name); + node.GetFullFilePath(filePath, true, true); codeEditor = (CodeEditor)ide.OpenFile(filePath, false, true, null, no, normal, noParsing); if(codeEditor) break; @@ -2024,6 +2049,21 @@ class ProjectView : Window projectNode.Delete(); } + void updateModified() + { + Project modPrj = null; + for(p : ide.workspace.projects) + { + if(p.topNode.modified) + { + modPrj = p; + break; + } + } + if(!modPrj) + modifiedDocument = false; + } + bool ProjectSave(MenuItem selection, Modifiers mods) { DataRow row = fileList.currentRow; @@ -2034,18 +2074,8 @@ class ProjectView : Window prj.StopMonitoring(); if(prj.Save(prj.filePath)) { - Project modPrj = null; prj.topNode.modified = false; - for(p : ide.workspace.projects) - { - if(p.topNode.modified) - { - modPrj = p; - break; - } - } - if(!modPrj) - modifiedDocument = false; + updateModified(); Update(null); } prj.StartMonitoring(); @@ -2196,6 +2226,9 @@ class ProjectView : Window Project prj = parentNode.project; int c; ProjectNode after = null; + + if(!parentNode.files) parentNode.files = { }; + for(node : parentNode.files) { if(node.type != folder) @@ -2209,7 +2242,7 @@ class ProjectView : Window { for(c = 0; c < 100; c++) { - char string[16]; + char string[32]; sprintf(string, c ? "New Folder (%d)" : "New Folder", c); if((folderNode = parentNode.Add(prj, string, after, folder, folder, true))) break; @@ -2336,6 +2369,9 @@ class ProjectView : Window { ProjectNode result = null; ProjectNode after = null; + + if(!parentNode.files) parentNode.files = { }; + for(node : parentNode.files) { if(node.type != folder && node.type != file && node.type) @@ -2367,12 +2403,14 @@ class ProjectView : Window ProjectNode projectNode; ProjectNode after = null; DataRow row = fileList.currentRow; + Project project; ProjectNode parentNode; int c; - if(!row) row = project.topNode.row; + if(!row) row = this.project.topNode.row; parentNode = (ProjectNode)(intptr)row.tag; + project = parentNode.project; for(node : parentNode.files) { diff --git a/ide/src/project/Workspace.ec b/ide/src/project/Workspace.ec index b6964903b6..591c93b892 100644 --- a/ide/src/project/Workspace.ec +++ b/ide/src/project/Workspace.ec @@ -10,7 +10,7 @@ enum ValgrindLeakCheck get { return OnGetString(null, null, null); } } - const char * OnGetString(char * tempString, void * fieldData, bool * needClass) + const char * OnGetString(char * tempString, void * fieldData, ObjectNotationType * onType) { if(this >= no && this <= full) { @@ -352,6 +352,19 @@ public: } } + const char * getRightPath(const char * filePath) + { + const char * rightPath = null; + for(p : projects) + { + rightPath = p.topNode.getRightPath(filePath); + if(rightPath) break; + rightPath = p.resNode.getRightPath(filePath); + if(rightPath) break; + } + return rightPath; + } + char * CopyAbsolutePathFromRelative(const char * relative) { char name[MAX_LOCATION]; @@ -933,6 +946,42 @@ public: } } + char * getEnvVarsString() + { + int len = 0; + char * vars; + char * d; + for(e : environmentVars) + { + int l = strlen(e.string); + if(l) + { + bool quoted = e.string[0] == '\"' && e.string[l - 1] == '\"'; + len += strlen(e.name) + l + (quoted ? 2 : 4); + } + } + vars = new char[len + 1]; + d = vars; + for(e : environmentVars) + { + int l = strlen(e.string); + if(l) + { + bool quoted = e.string[0] == '\"' && e.string[l - 1] == '\"'; + if(quoted) + sprintf(d, " %s=%s", e.name, e.string); + else + sprintf(d, " %s=\"%s\"", e.name, e.string); + d += strlen(e.name) + l + (quoted ? 2 : 4); + } + } + *d = '\0'; +#if defined(__WIN32__) + ChangeCh(vars, '\\', '/'); +#endif + return vars; + } + void Init() { if(!addedProjects) addedProjects = { }; @@ -968,7 +1017,7 @@ public: Workspace() { - ide.outputView.buildBox.Clear(); + ide.outputView.buildClear(); ide.outputView.debugBox.Clear(); ide.callStackView.Clear(); ide.watchesView.Clear(); diff --git a/ide/src/project/vsSupport.ec b/ide/src/project/vsSupport.ec index d1ccb2cf28..f074d563b1 100644 --- a/ide/src/project/vsSupport.ec +++ b/ide/src/project/vsSupport.ec @@ -125,7 +125,7 @@ void GenerateVCProjectFile(Project project, CompilerConfig compiler, int bitDept const char * targetFrameworkVersion = "196613"; const char * prjGUID = "3A1E5467-4EE2-4299-8F0C-7D26CC8C24BA"; char * rootNamespace = projectName; - Map namesInfo { }; + Map namesInfo { }; // TOFIX: Collision and Config-specific! project.topNode.GenMakefileGetNameCollisionInfo(namesInfo, project.config); @@ -575,7 +575,7 @@ void CollectPlatformSpecificDirs(Project project, ProjectConfig config, Array namesInfo, FilesFilter filter, bool justHasChild, bool usePrecompiledHeaders) +bool PrintNodes(File f, Project prj, ProjectNode node, Map namesInfo, FilesFilter filter, bool justHasChild, bool usePrecompiledHeaders) { if(node.type == file) { @@ -694,7 +694,7 @@ bool PrintNodes(File f, Project prj, ProjectNode node, Map namesInfo, bool usePrecompiledHeaders) +void PrintFile(File f, Project prj, ProjectNode node, Map namesInfo, bool usePrecompiledHeaders) { char modulePath[MAX_LOCATION]; char moduleName[MAX_FILENAME]; @@ -733,7 +733,7 @@ void PrintFile(File f, Project prj, ProjectNode node, Map namesInfo, ProjectConfig config, + Map namesInfo, ProjectConfig config, Array perFilePreprocessorDefs, Array perFileIncludeDirs, bool usePrecompiledHeaders) { diff --git a/installer/Makefile b/installer/Makefile index da799e3722..a0cbe9ee1e 100644 --- a/installer/Makefile +++ b/installer/Makefile @@ -1,4 +1,8 @@ -.PHONY: all objdir cleantarget clean realclean distclean pots +ifneq ($(V),1) +.SILENT: +endif + +.PHONY: all objdir cleantarget clean realclean wipeclean distclean pots # CORE VARIABLES @@ -5759,7 +5763,7 @@ LIBS += \ endif PRJ_CFLAGS += \ - $(if $(DEBUG), -g, -Os -ffast-math) $(FPIC) -w -DREPOSITORY_VERSION="\"$(REPOSITORY_VER)\"" \ + $(if $(DEBUG), -g, -Os -ffast-math) $(FPIC) -Wall -DREPOSITORY_VERSION="\"$(REPOSITORY_VER)\"" \ -DECERE_STATIC ECFLAGS += -module $(MODULE) @@ -5777,7 +5781,7 @@ endif all: objdir $(TARGET) objdir: - $(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ))) + $(call mkdir,$(OBJ)) $(if $(ECERE_SDK_SRC),$(if $(wildcard $(call escspace,$(ECERE_SDK_SRC)/crossplatform.mk)),,@$(call echo,Ecere SDK Source Warning: The value of ECERE_SDK_SRC is pointing to an incorrect ($(ECERE_SDK_SRC)) location.)),) $(if $(ECERE_SDK_SRC),,$(if $(ECP_DEBUG)$(ECC_DEBUG)$(ECS_DEBUG),@$(call echo,ECC Debug Warning: Please define ECERE_SDK_SRC before using ECP_DEBUG, ECC_DEBUG or ECS_DEBUG),)) @@ -6642,7 +6646,7 @@ endif $(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.c -o $(call quote_path,$@) -cleantarget: objdir +cleantarget: $(call rm,$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)) $(call rm,$(OBJ)symbols.lst) $(call rm,$(OBJ)objects.lst) @@ -6670,9 +6674,12 @@ endif realclean: cleantarget $(call rmr,$(OBJ)) +wipeclean: + $(call rmr,obj/) + distclean: $(_MAKE) -f $(_CF_DIR)Cleanfile distclean distclean_all_subdirs -Makefile: ; -$(_CF_DIR)crossplatform.mk: ; -$(_CF_DIR)default.cf: ; +$(MAKEFILE_LIST): ; +$(SOURCES): ; +$(RESOURCES): ; diff --git a/samples/3D/HoloLensModelViewer/Assets/LockScreenLogo.scale-200.png b/samples/3D/HoloLensModelViewer/Assets/LockScreenLogo.scale-200.png new file mode 100644 index 0000000000..735f57adb5 Binary files /dev/null and b/samples/3D/HoloLensModelViewer/Assets/LockScreenLogo.scale-200.png differ diff --git a/samples/3D/HoloLensModelViewer/Assets/SplashScreen.scale-200.png b/samples/3D/HoloLensModelViewer/Assets/SplashScreen.scale-200.png new file mode 100644 index 0000000000..023e7f1fed Binary files /dev/null and b/samples/3D/HoloLensModelViewer/Assets/SplashScreen.scale-200.png differ diff --git a/samples/3D/HoloLensModelViewer/Assets/Square150x150Logo.scale-200.png b/samples/3D/HoloLensModelViewer/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 0000000000..af49fec1a5 Binary files /dev/null and b/samples/3D/HoloLensModelViewer/Assets/Square150x150Logo.scale-200.png differ diff --git a/samples/3D/HoloLensModelViewer/Assets/Square44x44Logo.scale-200.png b/samples/3D/HoloLensModelViewer/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 0000000000..ce342a2ec8 Binary files /dev/null and b/samples/3D/HoloLensModelViewer/Assets/Square44x44Logo.scale-200.png differ diff --git a/samples/3D/HoloLensModelViewer/Assets/StoreLogo.png b/samples/3D/HoloLensModelViewer/Assets/StoreLogo.png new file mode 100644 index 0000000000..7385b56c0e Binary files /dev/null and b/samples/3D/HoloLensModelViewer/Assets/StoreLogo.png differ diff --git a/samples/3D/HoloLensModelViewer/Assets/Wide310x150Logo.scale-200.png b/samples/3D/HoloLensModelViewer/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000000..288995b397 Binary files /dev/null and b/samples/3D/HoloLensModelViewer/Assets/Wide310x150Logo.scale-200.png differ diff --git a/samples/3D/HoloLensModelViewer/HoloLensModelViewer.sln b/samples/3D/HoloLensModelViewer/HoloLensModelViewer.sln new file mode 100644 index 0000000000..1fc8ebbc18 --- /dev/null +++ b/samples/3D/HoloLensModelViewer/HoloLensModelViewer.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29911.84 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HoloLensModelViewer", "HoloLensModelViewer.vcxproj", "{AFF38973-8374-415C-86F2-558C248C730F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AFF38973-8374-415C-86F2-558C248C730F}.Debug|ARM.ActiveCfg = Debug|ARM + {AFF38973-8374-415C-86F2-558C248C730F}.Debug|ARM.Build.0 = Debug|ARM + {AFF38973-8374-415C-86F2-558C248C730F}.Debug|ARM.Deploy.0 = Debug|ARM + {AFF38973-8374-415C-86F2-558C248C730F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {AFF38973-8374-415C-86F2-558C248C730F}.Debug|ARM64.Build.0 = Debug|ARM64 + {AFF38973-8374-415C-86F2-558C248C730F}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {AFF38973-8374-415C-86F2-558C248C730F}.Debug|x64.ActiveCfg = Debug|x64 + {AFF38973-8374-415C-86F2-558C248C730F}.Debug|x64.Build.0 = Debug|x64 + {AFF38973-8374-415C-86F2-558C248C730F}.Debug|x64.Deploy.0 = Debug|x64 + {AFF38973-8374-415C-86F2-558C248C730F}.Debug|x86.ActiveCfg = Debug|Win32 + {AFF38973-8374-415C-86F2-558C248C730F}.Debug|x86.Build.0 = Debug|Win32 + {AFF38973-8374-415C-86F2-558C248C730F}.Debug|x86.Deploy.0 = Debug|Win32 + {AFF38973-8374-415C-86F2-558C248C730F}.Release|ARM.ActiveCfg = Release|ARM + {AFF38973-8374-415C-86F2-558C248C730F}.Release|ARM.Build.0 = Release|ARM + {AFF38973-8374-415C-86F2-558C248C730F}.Release|ARM.Deploy.0 = Release|ARM + {AFF38973-8374-415C-86F2-558C248C730F}.Release|ARM64.ActiveCfg = Release|ARM64 + {AFF38973-8374-415C-86F2-558C248C730F}.Release|ARM64.Build.0 = Release|ARM64 + {AFF38973-8374-415C-86F2-558C248C730F}.Release|ARM64.Deploy.0 = Release|ARM64 + {AFF38973-8374-415C-86F2-558C248C730F}.Release|x64.ActiveCfg = Release|x64 + {AFF38973-8374-415C-86F2-558C248C730F}.Release|x64.Build.0 = Release|x64 + {AFF38973-8374-415C-86F2-558C248C730F}.Release|x64.Deploy.0 = Release|x64 + {AFF38973-8374-415C-86F2-558C248C730F}.Release|x86.ActiveCfg = Release|Win32 + {AFF38973-8374-415C-86F2-558C248C730F}.Release|x86.Build.0 = Release|Win32 + {AFF38973-8374-415C-86F2-558C248C730F}.Release|x86.Deploy.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D1585234-F662-4650-A177-AC8FBC7E6149} + EndGlobalSection +EndGlobal diff --git a/samples/3D/HoloLensModelViewer/HoloLensModelViewer.vcxproj b/samples/3D/HoloLensModelViewer/HoloLensModelViewer.vcxproj new file mode 100644 index 0000000000..012918d99b --- /dev/null +++ b/samples/3D/HoloLensModelViewer/HoloLensModelViewer.vcxproj @@ -0,0 +1,416 @@ + + + + + true + true + true + {aff38973-8374-415c-86f2-558c248c730f} + HoloLensModelViewer + HoloLensModelViewer + en-US + 15.0 + true + Windows Store + 10.0.18362.0 + 10.0.17763.0 + 10.0 + true + + + $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ + $(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ + $(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ + $(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ + $(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ + $(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ + $(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ + $(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ + $(Platform)\$(Configuration)\ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM + + + Release + ARM + + + Debug + ARM64 + + + Release + ARM64 + + + + Application + true + $(DefaultPlatformToolset) + + + Application + false + true + $(DefaultPlatformToolset) + false + + + Application + true + $(DefaultPlatformToolset) + + + Application + false + true + $(DefaultPlatformToolset) + false + + + Application + true + $(DefaultPlatformToolset) + + + Application + false + true + $(DefaultPlatformToolset) + false + + + Application + true + $(DefaultPlatformToolset) + + + Application + false + true + $(DefaultPlatformToolset) + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + HoloLensModelViewer_TemporaryKey.pfx + False + + + + d2d1.lib; d3d11.lib; dxgi.lib; dwrite.lib; windowscodecs.lib;libGLESv2.lib;libEGL.lib;ecere.lib;modelViewer.lib;%(AdditionalDependencies); + %(AdditionalLibraryDirectories); $(VCInstallDir)\lib\store; $(VCInstallDir)\lib + kernel32.lib;ole32.lib;%(IgnoreSpecificDefaultLibraries) + false + + + pch.h + $(IntDir)pch.pch + $(ProjectDir);$(IntermediateOutputPath);\angle-hl2\include;%(AdditionalIncludeDirectories) + /bigobj /await /Zc:twoPhase- %(AdditionalOptions) + _DEBUG;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + true + false + stdcpp17 + true + NotUsing + false + + + + + d2d1.lib; d3d11.lib; dxgi.lib; dwrite.lib; windowscodecs.lib;libGLESv2.lib;libEGL.lib;ecere.lib;modelViewer.lib;%(AdditionalDependencies); + %(AdditionalLibraryDirectories); $(VCInstallDir)\lib\store; $(VCInstallDir)\lib; $(VC_LibraryPath_VC_x86_Desktop_spectre); $(VC_LibraryPath_VC_x86_Store_spectre) + kernel32.lib;ole32.lib;%(IgnoreSpecificDefaultLibraries) + false + + + pch.h + $(IntDir)pch.pch + $(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) + /bigobj /await %(AdditionalOptions) + NDEBUG;%(PreprocessorDefinitions);;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS + false + false + Guard + stdcpp17 + true + NotUsing + + + + + d2d1.lib; d3d11.lib; dxgi.lib; dwrite.lib; windowscodecs.lib;libGLESv2.lib;libEGL.lib;ecere.lib;modelViewer.lib;%(AdditionalDependencies); + %(AdditionalLibraryDirectories); $(VCInstallDir)\lib\store\amd64; $(VCInstallDir)\lib\amd64 + kernel32.lib;ole32.lib;%(IgnoreSpecificDefaultLibraries) + false + + + pch.h + $(IntDir)pch.pch + $(ProjectDir);$(IntermediateOutputPath);\angle-hl2\include;%(AdditionalIncludeDirectories) + /bigobj /await /Zc:twoPhase- %(AdditionalOptions) + _DEBUG;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + true + false + stdcpp17 + true + NotUsing + false + + + + + d2d1.lib; d3d11.lib; dxgi.lib; dwrite.lib; windowscodecs.lib;libGLESv2.lib;libEGL.lib;ecere.lib;modelViewer.lib;%(AdditionalDependencies); + %(AdditionalLibraryDirectories); $(VCInstallDir)\lib\store\amd64; $(VCInstallDir)\lib\amd64; $(VC_LibraryPath_VC_x64_Desktop_spectre); $(VC_LibraryPath_VC_x64_Store_spectre) + kernel32.lib;ole32.lib;%(IgnoreSpecificDefaultLibraries) + false + + + pch.h + $(IntDir)pch.pch + $(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) + /bigobj /await %(AdditionalOptions) + NDEBUG;%(PreprocessorDefinitions);;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS + false + false + Guard + stdcpp17 + true + NotUsing + + + + + d2d1.lib; d3d11.lib; dxgi.lib; dwrite.lib; windowscodecs.lib;libGLESv2.lib;libEGL.lib;ecere.lib;modelViewer.lib;%(AdditionalDependencies); + %(AdditionalLibraryDirectories); $(VCInstallDir)\lib\store\arm; $(VCInstallDir)\lib\arm + kernel32.lib;ole32.lib;%(IgnoreSpecificDefaultLibraries) + false + + + pch.h + $(IntDir)pch.pch + $(ProjectDir);$(IntermediateOutputPath);\angle-hl2\include;%(AdditionalIncludeDirectories) + /bigobj /await /Zc:twoPhase- %(AdditionalOptions) + _DEBUG;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + true + false + Guard + stdcpp17 + true + NotUsing + false + + + + + d2d1.lib; d3d11.lib; dxgi.lib; dwrite.lib; windowscodecs.lib;libGLESv2.lib;libEGL.lib;ecere.lib;modelViewer.lib;%(AdditionalDependencies); + %(AdditionalLibraryDirectories); $(VCInstallDir)\lib\store\arm; $(VCInstallDir)\lib\arm; $(VC_LibraryPath_VC_ARM_Desktop_spectre); $(VC_LibraryPath_VC_ARM_Store_spectre) + kernel32.lib;ole32.lib;%(IgnoreSpecificDefaultLibraries) + false + + + pch.h + $(IntDir)pch.pch + $(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories) + /bigobj /await %(AdditionalOptions) + NDEBUG;%(PreprocessorDefinitions);;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS + false + false + Guard + stdcpp17 + true + NotUsing + + + + + d2d1.lib; d3d11.lib; dxgi.lib; dwrite.lib; windowscodecs.lib;libGLESv2.lib;libEGL.lib;ecere.lib;modelViewer.lib;%(AdditionalDependencies); + %(AdditionalLibraryDirectories);$(VCInstallDir)\lib\store\arm64;$(VCInstallDir)\lib\arm64;\angle-hl2\winrt\10\src\Debug_ARM64\lib;\ecere-sdk\ecere\obj\uwp.win32.clangARM64;.\modelViewer\obj\debug.win32.clangARM64 + kernel32.lib;ole32.lib;%(IgnoreSpecificDefaultLibraries) + false + + + pch.h + $(IntDir)pch.pch + $(ProjectDir);$(IntermediateOutputPath);\angle-hl2\include;%(AdditionalIncludeDirectories) + /bigobj /await /Zc:twoPhase- %(AdditionalOptions) + _DEBUG;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + true + false + Guard + stdcpp17 + true + NotUsing + false + + + + + d2d1.lib; d3d11.lib; dxgi.lib; dwrite.lib; windowscodecs.lib;libGLESv2.lib;libEGL.lib;ecere.lib;modelViewer.lib;%(AdditionalDependencies); + %(AdditionalLibraryDirectories);$(VCInstallDir)\lib\store\arm64;$(VCInstallDir)\lib\arm64;$(VC_LibraryPath_VC_ARM64_Desktop_spectre);$(VC_LibraryPath_VC_ARM64_Store_spectre);\angle-hl2\winrt\10\src\Release_ARM64\lib;\ecere-sdk\ecere\obj\uwp.win32.clangARM64;. + kernel32.lib;ole32.lib;%(IgnoreSpecificDefaultLibraries) + false + + + pch.h + $(IntDir)pch.pch + $(ProjectDir);$(IntermediateOutputPath);%(AdditionalIncludeDirectories);\angle-hl2\include + /bigobj /await /Zc:twoPhase- %(AdditionalOptions) + NDEBUG;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + true + false + Guard + stdcpp17 + true + NotUsing + + + + + + + + + + + + + Designer + + + true + true + + + true + true + + + true + true + + + true + true + + + + true + true + + + true + true + + + + + + + + + + + true + + + + + + + + + + $(VC_IncludePath);$(WindowsSDK_IncludePath) + False + SHA256 + C:\HoloLensModelViewer\Bundles\Test1\ + False + True + Always + arm64 + 0 + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet package restore to download them. By default, NuGet package restore is activated when the solution is built. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/samples/3D/HoloLensModelViewer/Package.appxmanifest b/samples/3D/HoloLensModelViewer/Package.appxmanifest new file mode 100644 index 0000000000..a8048b74d2 --- /dev/null +++ b/samples/3D/HoloLensModelViewer/Package.appxmanifest @@ -0,0 +1,44 @@ + + + + + + + + + + HoloLensModelViewer + Jerome + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/3D/HoloLensModelViewer/PropertySheet.props b/samples/3D/HoloLensModelViewer/PropertySheet.props new file mode 100644 index 0000000000..5942ba395b --- /dev/null +++ b/samples/3D/HoloLensModelViewer/PropertySheet.props @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/samples/3D/HoloLensModelViewer/app.cpp b/samples/3D/HoloLensModelViewer/app.cpp new file mode 100644 index 0000000000..7ff77b9257 --- /dev/null +++ b/samples/3D/HoloLensModelViewer/app.cpp @@ -0,0 +1,414 @@ +// +// This file demonstrates how to initialize EGL in a Windows Store app, using ICoreWindow. +// + +#include "app.h" + +extern "C" +{ +#define DLLIMPORT __declspec(dllimport) + +DLLIMPORT int E3DViewer_Init(int argc, const char ** argv, const wchar_t* widePath); +DLLIMPORT void E3DViewer_Cycle(int w, int h); +DLLIMPORT void E3DViewer_Render(); +DLLIMPORT void E3DViewer_Terminate(); +}; + +//std::wstring installedPath = Windows::ApplicationModel::Package::Current->InstalledLocation->Path->Data(); + +using namespace Windows::ApplicationModel::Core; +using namespace Windows::ApplicationModel::Activation; +using namespace Windows::UI::Core; +using namespace Windows::UI::Input; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace Windows::Graphics::Display; +using namespace Windows::Graphics::Holographic; +using namespace Windows::Perception::Spatial; +using namespace Microsoft::WRL; +using namespace Platform; + +using namespace HoloLensModelViewer; + +// Helper to convert a length in device-independent pixels (DIPs) to a length in physical pixels. +inline float ConvertDipsToPixels(float dips, float dpi) +{ + static const float dipsPerInch = 96.0f; + return floorf(dips * dpi / dipsPerInch + 0.5f); // Round to nearest integer. +} + +// Implementation of the IFrameworkViewSource interface, necessary to run our app. +ref class SimpleApplicationSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource +{ +public: + virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView() + { + return ref new App(); + } +}; + +// The main function creates an IFrameworkViewSource for our app, and runs the app. +[Platform::MTAThread] +int main(Platform::Array^) +{ + auto simpleApplicationSource = ref new SimpleApplicationSource(); + CoreApplication::Run(simpleApplicationSource); + return 0; +} + +App::App() : + mWindowClosed(false), + mWindowVisible(true), + mEglDisplay(EGL_NO_DISPLAY), + mEglContext(EGL_NO_CONTEXT), + mEglSurface(EGL_NO_SURFACE) +{ +} + +// The first method called when the IFrameworkView is being created. +void App::Initialize(CoreApplicationView^ applicationView) +{ + // Register event handlers for app lifecycle. This example includes Activated, so that we + // can make the CoreWindow active and start rendering on the window. + applicationView->Activated += + ref new TypedEventHandler(this, &App::OnActivated); + + // Logic for other event handlers could go here. + // Information about the Suspending and Resuming event handlers can be found here: + // http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh994930.aspx +} + +// Called when the CoreWindow object is created (or re-created). +void App::SetWindow(CoreWindow^ window) +{ + window->VisibilityChanged += + ref new TypedEventHandler(this, &App::OnVisibilityChanged); + + window->Closed += + ref new TypedEventHandler(this, &App::OnWindowClosed); + + try + { + // Create a holographic space for the core window for the current view. + mHolographicSpace = HolographicSpace::CreateForCoreWindow(window); + + // Get the default SpatialLocator. + SpatialLocator^ locator = SpatialLocator::GetDefault(); + if (locator != nullptr) + { + // Create a stationary frame of reference. + mStationaryReferenceFrame = locator->CreateStationaryFrameOfReferenceAtCurrentLocation(); + + // The HolographicSpace has been created, so EGL can be initialized in holographic mode. + InitializeEGL(mHolographicSpace); + } + else + throw Exception::CreateException(E_FAIL, L"No Holographic"); + } + catch (Platform::Exception^ ex) + { + //if (ex->HResult == HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)) + { + // Device does not support holographic spaces. Initialize EGL to use the CoreWindow instead. + InitializeEGL(window); + } + } +} + +// Initializes scene resources +void App::Load(Platform::String^ entryPoint) +{ + RecreateRenderer(); +} + +void App::RecreateRenderer() +{ + +} + +// This method is called after the window becomes active. +void App::Run() +{ + while (!mWindowClosed) + { + if (mWindowVisible) + { + CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent); + + EGLint panelWidth = 0; + EGLint panelHeight = 0; + eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &panelWidth); + eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &panelHeight); + + E3DViewer_Cycle(panelWidth, panelHeight); + + // Logic to update the scene could go here + + E3DViewer_Render(); + //mCubeRenderer->UpdateWindowSize(panelWidth, panelHeight); + //mCubeRenderer->Draw(); + + // The call to eglSwapBuffers might not be successful (e.g. due to Device Lost) + // If the call fails, then we must reinitialize EGL and the GL resources. + if (eglSwapBuffers(mEglDisplay, mEglSurface) != EGL_TRUE) + { + //mCubeRenderer.reset(nullptr); + CleanupEGL(); + + if (mHolographicSpace != nullptr) + { + InitializeEGL(mHolographicSpace); + } + else + { + InitializeEGL(CoreWindow::GetForCurrentThread()); + } + + RecreateRenderer(); + } + } + else + { + CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending); + } + } + + CleanupEGL(); +} + +// Terminate events do not cause Uninitialize to be called. It will be called if your IFrameworkView +// class is torn down while the app is in the foreground. +void App::Uninitialize() +{ +} + +// Application lifecycle event handler. +void App::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args) +{ + // Run() won't start until the CoreWindow is activated. + CoreWindow::GetForCurrentThread()->Activate(); +} + +// Window event handlers. +void App::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args) +{ + mWindowVisible = args->Visible; +} + +void App::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args) +{ + mWindowClosed = true; +} + +void App::InitializeEGL(Windows::UI::Core::CoreWindow^ window) +{ + App::InitializeEGLInner(window); +} + +void App::InitializeEGL(Windows::Graphics::Holographic::HolographicSpace^ holographicSpace) +{ + App::InitializeEGLInner(holographicSpace); +} + +void App::InitializeEGLInner(Platform::Object^ windowBasis) +{ + const EGLint configAttributes[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 8, + EGL_STENCIL_SIZE, 8, + EGL_NONE + }; + + const EGLint contextAttributes[] = + { + EGL_CONTEXT_CLIENT_VERSION, 3, + EGL_NONE + }; + + const EGLint surfaceAttributes[] = + { + // EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER is part of the same optimization as EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER (see above). + // If you have compilation issues with it then please update your Visual Studio templates. + EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER, EGL_TRUE, + //EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE, EGL_TRUE, + EGL_NONE + }; + + const EGLint defaultDisplayAttributes[] = + { + // These are the default display attributes, used to request ANGLE's D3D11 renderer. + // eglInitialize will only succeed with these attributes if the hardware supports D3D11 Feature Level 10_0+. + EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + + // EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization that can have large performance benefits on mobile devices. + // Its syntax is subject to change, though. Please update your Visual Studio templates if you experience compilation issues with it. + //EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE, + EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE, EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE, + + // EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that enables ANGLE to automatically call + // the IDXGIDevice3::Trim method on behalf of the application when it gets suspended. + // Calling IDXGIDevice3::Trim when an application is suspended is a Windows Store application certification requirement. + EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, + EGL_NONE, + }; + + const EGLint fl9_3DisplayAttributes[] = + { + // These can be used to request ANGLE's D3D11 renderer, with D3D11 Feature Level 9_3. + // These attributes are used if the call to eglInitialize fails with the default display attributes. + EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, 9, + EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, 3, + //EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE, + EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE, EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE, + EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, + EGL_NONE, + }; + + const EGLint warpDisplayAttributes[] = + { + // These attributes can be used to request D3D11 WARP. + // They are used if eglInitialize fails with both the default display attributes and the 9_3 display attributes. + EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE, + //EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE, + EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE, EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE, + EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, + EGL_NONE, + }; + + EGLConfig config = NULL; + + // eglGetPlatformDisplayEXT is an alternative to eglGetDisplay. It allows us to pass in display attributes, used to configure D3D11. + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = reinterpret_cast(eglGetProcAddress("eglGetPlatformDisplayEXT")); + if (!eglGetPlatformDisplayEXT) + { + throw Exception::CreateException(E_FAIL, L"Failed to get function eglGetPlatformDisplayEXT"); + } + + // + // To initialize the display, we make three sets of calls to eglGetPlatformDisplayEXT and eglInitialize, with varying + // parameters passed to eglGetPlatformDisplayEXT: + // 1) The first calls uses "defaultDisplayAttributes" as a parameter. This corresponds to D3D11 Feature Level 10_0+. + // 2) If eglInitialize fails for step 1 (e.g. because 10_0+ isn't supported by the default GPU), then we try again + // using "fl9_3DisplayAttributes". This corresponds to D3D11 Feature Level 9_3. + // 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again + // using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer. + // + + // This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details. + mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes); + if (mEglDisplay == EGL_NO_DISPLAY) + { + throw Exception::CreateException(E_FAIL, L"Failed to get EGL display"); + } + + if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) + { + // This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on some mobile devices). + mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, fl9_3DisplayAttributes); + if (mEglDisplay == EGL_NO_DISPLAY) + { + throw Exception::CreateException(E_FAIL, L"Failed to get EGL display"); + } + + if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) + { + // This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU. + mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes); + if (mEglDisplay == EGL_NO_DISPLAY) + { + throw Exception::CreateException(E_FAIL, L"Failed to get EGL display"); + } + + if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) + { + // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. + throw Exception::CreateException(E_FAIL, L"Failed to initialize EGL"); + } + } + } + + EGLint numConfigs = 0; + if ((eglChooseConfig(mEglDisplay, configAttributes, &config, 1, &numConfigs) == EGL_FALSE) || (numConfigs == 0)) + { + throw Exception::CreateException(E_FAIL, L"Failed to choose first EGLConfig"); + } + + // Create a PropertySet and initialize with the EGLNativeWindowType. + PropertySet^ surfaceCreationProperties = ref new PropertySet(); + surfaceCreationProperties->Insert(ref new String(EGLNativeWindowTypeProperty), windowBasis); + if (mStationaryReferenceFrame != nullptr) + { + surfaceCreationProperties->Insert(ref new String(EGLBaseCoordinateSystemProperty), mStationaryReferenceFrame); + surfaceCreationProperties->Insert(ref new String(EGLAutomaticStereoRenderingProperty), PropertyValue::CreateBoolean(true)); + surfaceCreationProperties->Insert(ref new String(EGLAutomaticDepthBasedImageStabilizationProperty), PropertyValue::CreateBoolean(true)); + } + + // You can configure the surface to render at a lower resolution and be scaled up to + // the full window size. This scaling is often free on mobile hardware. + // + // One way to configure the SwapChainPanel is to specify precisely which resolution it should render at. + // Size customRenderSurfaceSize = Size(800, 600); + // surfaceCreationProperties->Insert(ref new String(EGLRenderSurfaceSizeProperty), PropertyValue::CreateSize(customRenderSurfaceSize)); + // + // Another way is to tell the SwapChainPanel to render at a certain scale factor compared to its size. + // e.g. if the SwapChainPanel is 1920x1280 then setting a factor of 0.5f will make the app render at 960x640 + // float customResolutionScale = 0.5f; + // surfaceCreationProperties->Insert(ref new String(EGLRenderResolutionScaleProperty), PropertyValue::CreateSingle(customResolutionScale)); + + mEglSurface = eglCreateWindowSurface(mEglDisplay, config, reinterpret_cast(surfaceCreationProperties), surfaceAttributes); + if (mEglSurface == EGL_NO_SURFACE) + { + throw Exception::CreateException(E_FAIL, L"Failed to create EGL fullscreen surface"); + } + + mEglContext = eglCreateContext(mEglDisplay, config, EGL_NO_CONTEXT, contextAttributes); + if (mEglContext == EGL_NO_CONTEXT) + { + throw Exception::CreateException(E_FAIL, L"Failed to create EGL context"); + } + + if (eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext) == EGL_FALSE) + { + throw Exception::CreateException(E_FAIL, L"Failed to make fullscreen EGLSurface current"); + } + + // By default, allow HolographicFrame::PresentUsingCurrentPrediction() to wait for the current frame to + // finish before it returns. + eglSurfaceAttrib(mEglDisplay, mEglSurface, EGLEXT_WAIT_FOR_VBLANK_ANGLE, true); + + { + const char * argv[] = { "modelViewer", }; + int argc = sizeof(argv) / sizeof(argv[0]); + std::wstring installedPath = Windows::ApplicationModel::Package::Current->InstalledLocation->Path->Data(); + E3DViewer_Init(argc, argv, installedPath.c_str()); + } +} + +void App::CleanupEGL() +{ + E3DViewer_Terminate(); + + if (mEglDisplay != EGL_NO_DISPLAY && mEglSurface != EGL_NO_SURFACE) + { + eglDestroySurface(mEglDisplay, mEglSurface); + mEglSurface = EGL_NO_SURFACE; + } + + if (mEglDisplay != EGL_NO_DISPLAY && mEglContext != EGL_NO_CONTEXT) + { + eglDestroyContext(mEglDisplay, mEglContext); + mEglContext = EGL_NO_CONTEXT; + } + + if (mEglDisplay != EGL_NO_DISPLAY) + { + eglTerminate(mEglDisplay); + mEglDisplay = EGL_NO_DISPLAY; + } +} diff --git a/samples/3D/HoloLensModelViewer/app.h b/samples/3D/HoloLensModelViewer/app.h new file mode 100644 index 0000000000..c037f72140 --- /dev/null +++ b/samples/3D/HoloLensModelViewer/app.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +#include +#include + +// EGL includes +#include +#include +#include + +// ANGLE include for Windows Store +#include + +namespace HoloLensModelViewer +{ + ref class App sealed : public Windows::ApplicationModel::Core::IFrameworkView + { + public: + App(); + + // IFrameworkView Methods. + virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView); + virtual void SetWindow(Windows::UI::Core::CoreWindow^ window); + virtual void Load(Platform::String^ entryPoint); + virtual void Run(); + virtual void Uninitialize(); + + private: + void RecreateRenderer(); + + // Application lifecycle event handlers. + void OnActivated(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args); + + // Window event handlers. + void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args); + void OnWindowClosed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CoreWindowEventArgs^ args); + + void InitializeEGL(Windows::Graphics::Holographic::HolographicSpace^ holographicSpace); + void InitializeEGL(Windows::UI::Core::CoreWindow^ window); + void InitializeEGLInner(Platform::Object^ windowBasis); + void CleanupEGL(); + + bool mWindowClosed; + bool mWindowVisible; + + EGLDisplay mEglDisplay; + EGLContext mEglContext; + EGLSurface mEglSurface; + + // The holographic space the app will use for rendering. + Windows::Graphics::Holographic::HolographicSpace^ mHolographicSpace = nullptr; + + // The world coordinate system. In this example, a reference frame placed in the environment. + Windows::Perception::Spatial::SpatialStationaryFrameOfReference^ mStationaryReferenceFrame = nullptr; + }; + +} \ No newline at end of file diff --git a/samples/3D/HoloLensModelViewer/modelViewer.ec b/samples/3D/HoloLensModelViewer/modelViewer.ec new file mode 100644 index 0000000000..a9748d054a --- /dev/null +++ b/samples/3D/HoloLensModelViewer/modelViewer.ec @@ -0,0 +1,276 @@ +import "ecere" + +#define _GLES3 +#include "gl123es.h" + +ModelWindow modelWindow; + +class ModelWindow : Window +{ + displayDriver = "OpenGL"; + caption = $"Ecere Model Viewer"; + opacity = 0; + drawBehind = false; + + anchor = { left = 0, top = 0, right = 0, bottom = 0 }; + + Camera camera + { + fixedQuaternion, + fovDirection = vertical, + fov = 53, + eulerOrientation = Euler { 0, 0, 0 }, + zMin = 1; + zMax = 10000; + }; + Object model {}; + + Light light + { + diffuse = white; + specular = white; + orientation = Euler { pitch = 50, yaw = 45 }; + }; + + const char * modelFile; + + void OnUnloadGraphics() + { + model.Free(displaySystem); + displaySystem.ClearMaterials(); + displaySystem.ClearTextures(); + displaySystem.ClearMeshes(); + } + + void OnDestroy() + { + delete model; + model = { }; + } + + void OnResize(int w, int h) + { + camera.Setup(w, h, null); + Update(null); + } + + bool OnKeyDown(Key key, unichar ch) + { + switch(key) + { + case escape: Destroy(0); return false; + } + return true; + } + + bool OnLoadGraphics() + { + if(model.Load(modelFile, null, displaySystem)) + { + float r = model.radius; + //float s = 0.08 / r; // Avocado + float s = 20.0f / r; // Sponza / Buildings + + model.transform.scaling = { s, s, s }; + model.UpdateTransform(); + + Log("OnLoadGraphics(): Successfully loaded 3D model!\n"); + + camera.zMin = 0.01; //1; + camera.zMax = 100; + + // Sponza + camera.position = { 0, -1.6, 0 }, + + // Buildings + //camera.position = { 0, -0.3, 0 }, + + // Avocado + //camera.position = { 0, 0, -0.25 }; //r * 2 }; + // camera.eulerOrientation = Euler { 30, 0, 0 }; + + light.orientation = Euler { pitch = 50, yaw = 45 }; + + return true; + } + return false; + } + + void OnRedraw(Surface surface) + { + Vector3D p, o; + + display.fogDensity = 0; + + surface.SetBackground(0); + surface.Clear(colorAndDepth); + + app.updateCamera(); + + p = camera.position; + o.Add(p, hmdPosition); + camera.orientation = hmdOrientation; + camera.position = o; + + camera.Update(); + + display.SetLight(0, &light); + + display.SetCamera(surface, camera); + + display.ambient = { 30, 30, 30 }; //black; + + display.DrawObject(model); + display.SetCamera(surface, null); + + camera.position = p; + + renderedSinceLastCycle = true; + } +} + +static bool renderedSinceLastCycle = true; + +class HoloLensApp : GuiApplication +{ + driver = "OpenGL"; + + bool Init() + { + static char modelPath[MAX_LOCATION]; + UseSingleGLContext(true); + modelWindow = { }; + incref modelWindow; + + strcpy(modelPath, installedPath); + //PathCat(modelPath, "Avocado.e3d"); //Assets/E3D/Avocado.e3d"); + PathCat(modelPath, "sponza.e3d"); + //PathCat(modelPath, "buildings-tateyo.e3d"); + + modelWindow.modelFile = modelPath; + + return modelWindow.Create(); + } + + void updateCamera() + { + static bool firstTime = true; + if(!firstTime) + { + Matrix matrix, inverse; + Euler hmdEuler; +#if defined(__UWP__) + float mat[16]; + + glGetFloatv(GLEXT_HOLOGRAPHIC_MONO_VIEW_MATRIX_ANGLE, mat); + matrix = + { { + mat[ 0], mat[ 1], mat[ 2], mat[ 3], + mat[ 4], mat[ 5], mat[ 6], mat[ 7], + mat[ 8], mat[ 9], mat[10], mat[11], + mat[12], mat[13], mat[14], mat[15] + } }; +#else + matrix.Identity(); +#endif + inverse.Inverse(matrix); + + inverse.Scale(1,1,-1); + + hmdEuler.FromMatrix(inverse, yxz); + hmdEuler.yaw *= -1; + hmdEuler.roll *= -1; + hmdEuler.yaw += 180; + + hmdOrientation = hmdEuler; + + hmdPosition = { -inverse.array[12], -inverse.array[13], inverse.array[14] }; + } + firstTime = false; + } + + bool Cycle(bool idle) + { + if(!modelWindow || modelWindow.destroyed || !renderedSinceLastCycle) + return true; + + modelWindow.Update(null); + return true; + } + + void Terminate() + { + delete modelWindow; + } +} + +Euler hmdOrientation; +Vector3D hmdPosition; + +HoloLensApp app; +char installedPath[MAX_LOCATION]; + +default: + +dllexport bool __stdcall __ecereDll_Load(Module module); +dllexport bool __stdcall __ecereDll_Unload(Module module); + +dllexport int E3DViewer_Init(int argc, const char ** argv, const uint16 * widePath) +{ + int result = 0; + + app = (HoloLensApp)__ecere_COM_Initialize(true, argc, (char **)argv); + if(app) + { + Module module = eModule_LoadStatic(app, "modelViewer", publicAccess, __ecereDll_Load, __ecereDll_Unload); + if(module) + { + eInstance_Evolve(&app, class(HoloLensApp)); + + SetLoggingMode(debug, null); + Log("Successfully loaded modelViewer module!\n"); + + LocateModule(null, installedPath); + StripLastDirectory(installedPath, installedPath); + + UTF16toUTF8Buffer(widePath, (char *)installedPath, sizeof(installedPath)); + + Logf("Installed path: %s\n", installedPath); + + result = app.Init(); + if(result) + { + app.desktop.opacity = 0; + app.desktop.drawBehind = false; + + Log("HoloLensApp initialization successful!\n"); + } + } + } + return result; +} + +static int desktopW, desktopH; + +dllexport void E3DViewer_Cycle(int w, int h) +{ + if(w == -1 && h == -1) w = 2048, h = 1080; + if(desktopW != w || desktopH != h) + { + app.SetDesktopPosition(0, 0, w, h, true); + desktopW = w; + desktopH = h; + } + app.Cycle(false); + app.ProcessInput(true); +} + +dllexport void E3DViewer_Render() +{ + app.UpdateDisplay(); +} + +dllexport void E3DViewer_Terminate() +{ + delete app; +} diff --git a/samples/3D/HoloLensModelViewer/modelViewer.epj b/samples/3D/HoloLensModelViewer/modelViewer.epj new file mode 100644 index 0000000000..01715f6043 --- /dev/null +++ b/samples/3D/HoloLensModelViewer/modelViewer.epj @@ -0,0 +1,187 @@ +{ + "Version" : 0.2, + "ModuleName" : "modelViewer", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "C:/gnosis2/deps/lzma-1701", + "C:/ecere-sdk/ecere/src/gfx/drivers/gl3" + ], + "TargetType" : "SharedLibrary", + "TargetFileName" : "modelViewer", + "Libraries" : [ + "ecere", + "libGLESv2", + "legacy_stdio_definitions" + ] + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "TargetType" : "SharedLibrary", + "Libraries" : [ + "ecere", + "libGLESv2" + ], + "Console" : true, + "FastMath" : false + } + }, + { + "Name" : "Release", + "Options" : { + "Debug" : false, + "Optimization" : "Speed", + "TargetDirectory" : ".", + "FastMath" : true + } + }, + { + "Name" : "Test", + "Options" : { + "Debug" : true, + "TargetType" : "Executable", + "Libraries" : [ + "ecere" + ] + } + } + ], + "Files" : [ + { + "Folder" : "e3d", + "Files" : [ + "$(ECERE_SDK_SRC)/ecere/src/gfx/3D/models/e3d/e3dDefs.ec", + "$(ECERE_SDK_SRC)/ecere/src/gfx/3D/models/e3d/e3dRead.ec", + "$(ECERE_SDK_SRC)/ecere/src/gfx/3D/models/e3d/e3dWrite.ec", + "$(ECERE_SDK_SRC)/ecere/src/gfx/3D/models/e3d.ec" + ] + }, + { + "Folder" : "lzma", + "Files" : [ + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7z.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zAlloc.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zAlloc.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zArcIn.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zBuf.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zBuf.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zBuf2.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zCrc.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zCrc.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zCrcOpt.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zDec.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zFile.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zFile.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zStream.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zTypes.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/7zVersion.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Aes.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Aes.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/AesOpt.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Alloc.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Alloc.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Bcj2.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Bcj2.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Bcj2Enc.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Bra.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Bra.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Bra86.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/BraIA64.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Compiler.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/CpuArch.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/CpuArch.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Delta.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Delta.h", + { + "FileName" : "$(GNOSIS_SDK_SRC)/deps/lzma-1701/DllSecur.c", + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "ExcludeFromBuild" : true + } + }, + { + "Name" : "Release", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + }, + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/DllSecur.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/LzFind.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/LzFind.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/LzFindMt.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/LzFindMt.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/LzHash.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Lzma2Dec.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Lzma2Dec.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Lzma2Enc.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Lzma2Enc.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Lzma86.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Lzma86Dec.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Lzma86Enc.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/LzmaDec.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/LzmaDec.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/LzmaEnc.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/LzmaEnc.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/LzmaLib.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/LzmaLib.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/MtCoder.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/MtCoder.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Ppmd.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Ppmd7.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Ppmd7.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Ppmd7Dec.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Ppmd7Enc.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Precomp.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/RotateDefs.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Sha256.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Sha256.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Sort.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Sort.h", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Threads.c", + "$(GNOSIS_SDK_SRC)/deps/lzma-1701/Threads.h" + ] + }, + { + "Folder" : "src", + "Files" : [ + { + "FileName" : "../app.cpp", + "Options" : { + "ExcludeFromBuild" : true + } + }, + "../app.h", + "./modelViewer.ec" + ], + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "ExcludeFromBuild" : true + } + }, + { + "Name" : "Test", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + } + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/3D/hello3D/hello3D.ec b/samples/3D/hello3D/hello3D.ec index e50144df35..7d224fa72d 100644 --- a/samples/3D/hello3D/hello3D.ec +++ b/samples/3D/hello3D/hello3D.ec @@ -23,23 +23,38 @@ class Hello3D : Window { caption = "Hello, 3D"; background = black; +#if 0 borderStyle = sizable; hasMaximize = true; hasMinimize = true; hasClose = true; +#else + moveable = true; + borderStyle = none; + opacity = 0; + alphaBlend = true; + stayOnTop = true; +#endif size = { 640, 480 }; Cube cube { }; + Material material { diffuse = white/*, ambient = blue*//*, specular = red, power = 8*/, opacity = 0.5f/*, flags = { translucent = true, doubleSided = true }*/ }; bool OnLoadGraphics() { cube.Create(displaySystem); + cube.mesh.ApplyMaterial(material); cube.transform.scaling = { 100, 100, 100 }; cube.transform.orientation = Euler { 50, 30, 50 }; cube.UpdateTransform(); return true; } + void OnUnloadGraphics() + { + cube.Free(displaySystem); + } + void OnResize(int w, int h) { camera.Setup(w, h, null); @@ -54,6 +69,12 @@ class Hello3D : Window display.DrawObject(cube); display.SetCamera(surface, null); } + + bool OnKeyDown(Key key, unichar ch) + { + if(key == escape) Destroy(0); + return true; + } } Hello3D hello3D {}; diff --git a/samples/3D/materials/materials.ec b/samples/3D/materials/materials.ec index 8871f227b6..8337a56591 100644 --- a/samples/3D/materials/materials.ec +++ b/samples/3D/materials/materials.ec @@ -161,6 +161,8 @@ class MaterialsTest : Window doubleSided = true; tile = true; separateSpecular = true; + setupTextures = true; + update = true; }; uScale = 4, vScale = 4; reflectivity = 0.3; @@ -182,6 +184,7 @@ class MaterialsTest : Window double r = model.radius * model.transform.scaling.z; //wradius; material.uScale = uvScale; material.vScale = uvScale; + material.flags |= { update = true, setupTextures = true }; camera.position = { 0, 0, -r * distance }; cameraTarget.transform.position = { @@ -196,10 +199,20 @@ class MaterialsTest : Window void selectEnv(SkyBox sky, CubeMap cubeMap) { material.envMap = cubeMap; + material.flags |= { update = true, setupTextures = true }; this.sky = sky; Update(null); } + void OnUnloadGraphics() + { + teapot.Free(displaySystem); + sphere.Free(displaySystem); + roundedCylinder.Free(displaySystem); + cube.Free(displaySystem); + waterBox.Free(displaySystem); + } + bool OnLoadGraphics() { const String faceNames1[] = { ":watersky/rt", ":watersky/lf", ":watersky/up", ":watersky/dn", ":watersky/bk", ":watersky/fr" }; @@ -450,6 +463,7 @@ class MaterialsTest : Window } case b: material.bumpMap = material.bumpMap ? null : bumpMap.bitmap; + material.flags |= { update = true, setupTextures = true }; Update(null); break; case r: @@ -476,6 +490,7 @@ class MaterialsTest : Window material.refractiveIndex = rGlass; material.opacity = 0.3; } + material.flags |= { update = true, setupTextures = true }; Update(null); break; } diff --git a/samples/3D/terrainCameraDemo/cameraDemo.epj b/samples/3D/terrainCameraDemo/cameraDemo.epj index 9ad8f0a318..c26cba22c2 100644 --- a/samples/3D/terrainCameraDemo/cameraDemo.epj +++ b/samples/3D/terrainCameraDemo/cameraDemo.epj @@ -6,6 +6,9 @@ "Debug" : true, "Profile" : false, "Optimization" : "None", + "PreprocessorDefinitions" : [ + "IMPORT_STATIC=\"\"" + ], "IncludeDirs" : [ "..\\eModel" ], @@ -44,7 +47,8 @@ "Files" : [ "demo.ec", "terrain.ec", - "dna.ec" + "dna.ec", + "../../../extras/BinaryTriangle.ec" ], "ResourcesPath" : "res", "Resources" : [ diff --git a/samples/3D/terrainCameraDemo/demo.ec b/samples/3D/terrainCameraDemo/demo.ec index 7df94496f3..dd2106db75 100644 --- a/samples/3D/terrainCameraDemo/demo.ec +++ b/samples/3D/terrainCameraDemo/demo.ec @@ -436,6 +436,9 @@ class Scene : Window for(c = 0; c<16; c++) delete textures[c]; terrainMesh.FreeMesh(); + player.Free(displaySystem); + sky.Free(displaySystem); + dna.Free(displaySystem); } void OnRedraw(Surface surface) diff --git a/samples/3D/terrainCameraDemo/dna.ec b/samples/3D/terrainCameraDemo/dna.ec index eaa5ae9a1f..cab5eedd07 100644 --- a/samples/3D/terrainCameraDemo/dna.ec +++ b/samples/3D/terrainCameraDemo/dna.ec @@ -305,9 +305,7 @@ public: bool result = false; if(this) { - InitializeMesh(displaySystem); - - if(mesh) + Initialize(); { int c; Bitmap map { }; @@ -356,9 +354,9 @@ public: helix.ApplyMaterial(displaySystem.GetMaterial("Phosphate")); //******************* PHOSPHATE ******************* - phosphate = { mesh = helix }; AddName(phosphate, "Phosphate01"); - phosphate = { mesh = helix }; AddName(phosphate, "Phosphate02"); + phosphate = { mesh = helix, flags.mesh = true }; AddName(phosphate, "Phosphate01"); phosphate.transform.orientation = Euler { yaw = 180 }; + phosphate = { mesh = helix, flags.mesh = true }; AddName(phosphate, "Phosphate02"); //******************* HYDROGENE ******************* hydrogen = { }; AddName(hydrogen, "Hydrogene"); @@ -416,7 +414,7 @@ public: //******************* DESOXYRIBOSE ******************* sprintf(name, "Desoxyribose%02d", c*2); - desoxyribose = { }; AddName(desoxyribose, name); + desoxyribose = { flags.mesh = true }; AddName(desoxyribose, name); desoxyribose.mesh = box; desoxyribose.transform.orientation = angle; desoxyribose.transform.scaling = { (float)desoxyriboseWidth / baseWidth, 1,1 }; @@ -425,7 +423,7 @@ public: desoxyribose.material = displaySystem.GetMaterial("Desoxyribose"); sprintf(name, "Desoxyribose%02d", c*2+1); - desoxyribose = { }; AddName(desoxyribose, name); + desoxyribose = { flags.mesh = true }; AddName(desoxyribose, name); desoxyribose.mesh = box; desoxyribose.transform.orientation = angle; desoxyribose.transform.scaling = { (float)desoxyriboseWidth / baseWidth, 1,1 }; @@ -433,14 +431,11 @@ public: pos.MultMatrix(position, matrix); desoxyribose.transform.position = pos; desoxyribose.material = displaySystem.GetMaterial("Desoxyribose"); } - transform.scaling = { 1,1,1 }; - transform.orientation = { 1, 0,0,0 }; - UpdateTransform(); - if(Merge(displaySystem)) - result = true; UpdateTransform(); SetMinMaxRadius(true); + if(Merge(displaySystem)) + result = true; delete box; delete helix; diff --git a/samples/3D/terrainCameraDemo/terrain.ec b/samples/3D/terrainCameraDemo/terrain.ec index 93322fc91f..e9a33e7d68 100644 --- a/samples/3D/terrainCameraDemo/terrain.ec +++ b/samples/3D/terrainCameraDemo/terrain.ec @@ -1,6 +1,17 @@ -import "ecere" +/* + Terrain tesselation based on binary triangle trees, partly inspired by tutorial and guidance + from Seumas McNally of Longbow Digital Arts: -class Elevation : int; + https://www.longbowgames.com/seumas/progbintri.html + + and "ROAMing Terrain: Real-time Optimally Adapting Meshes" white paper by Mark Duchaineau et al. + from Los Alamos National Laboratory & Lawrence Livermore National Laboratory, proceedings from VIS '97: + + http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.22.1811&rep=rep1&type=pdf + https://dl.acm.org/citation.cfm?id=266989.267028 +*/ +import "ecere" +import "BinaryTriangle" struct FPS { @@ -32,253 +43,36 @@ struct FPS } }; -static float fsqrt(float x, float y, float z) -{ - float ix = Abs(x),iy = Abs(y), iz = Abs(z); - float tmp; - - if(ix < iy) { tmp = ix; ix = iy; iy = tmp; } - if(ix < iz) { tmp = ix; ix = iz; iz = tmp; } - if(iz > iy) { iz = iy; } - return ix + (iz/2); -} - define SQR2 = 1.4142135623730950488016887242097f; define MAXTRIS = 9265536; - -class BinTri : struct -{ - BinTri left, right, bottom; - BinTri leftChild, rightChild; - Point v0,va,v1,vc; - uint tv0,tva,tv1,tvc; - int index; - - void Clear() - { - leftChild = null; - rightChild = null; - left = null; - right = null; - bottom = null; - } - - void SplitBinTri2(struct BinTri * stack, int * stackIndex) - { - BinTri leftChild, rightChild; - BinTri right, left; - - leftChild = this.leftChild = (BinTri) (stack + ((*stackIndex)++)); - rightChild = this.rightChild = (BinTri) (stack + ((*stackIndex)++)); - - leftChild.left = rightChild; - rightChild.right = leftChild; - - leftChild.bottom = this.left; - rightChild.bottom = this.right; - - left = this.left; - if(left) - { - if(left.bottom == this) - left.bottom = leftChild; - else - { - if(left.left == this) - left.left = leftChild; - else - left.right = leftChild; - } - } - - right = this.right; - if(right) - { - if(right.bottom == this) - right.bottom = rightChild; - else - { - if(right.right == this) - right.right = rightChild; - else - right.left = rightChild; - } - } - - leftChild.index = index<<1; - rightChild.index = leftChild.index+1; - leftChild.leftChild = null; - rightChild.leftChild = null; - leftChild.rightChild = null; - rightChild.rightChild = null; - - leftChild.tv0 = tva; - rightChild.tv1 = tva; - leftChild.tva = tvc; - rightChild.tva = tvc; - leftChild.tv1 = tv0; - rightChild.tv0 = tv1; - - leftChild.tvc = (tva + tv0) >> 1; - rightChild.tvc = (tv1 + tva) >> 1; - - leftChild.v0 = va; - rightChild.v1 = va; - leftChild.va = vc; - rightChild.va = vc; - leftChild.v1 = v0; - rightChild.v0 = v1; - - leftChild.vc.x = (va.x + v0.x) >> 1; - leftChild.vc.y = (va.y + v0.y) >> 1; - rightChild.vc.x = (v1.x + va.x) >> 1; - rightChild.vc.y = (v1.y + va.y) >> 1; - } - - bool SplitBinTri(struct BinTri * stack, int * stackIndex) - { - if(bottom) - { - if((bottom.bottom == this || bottom.SplitBinTri(stack, stackIndex)) && *stackIndex < MAXTRIS - 4) - { - SplitBinTri2(stack, stackIndex); - bottom.SplitBinTri2(stack, stackIndex); - leftChild.right = bottom.rightChild; - rightChild.left = bottom.leftChild; - bottom.leftChild.right = rightChild; - bottom.rightChild.left = leftChild; - return true; - } - } - else if(*stackIndex < MAXTRIS - 2) - { - SplitBinTri2(stack, stackIndex); - leftChild.right = null; - rightChild.left = null; - return true; - } - return false; - } - - void Split(struct BinTri * stack, int * triIndex, - byte level, float resolutionX, float resolutionY, int positionX, int positionY, int positionZ, - Elevation * heightMap, byte maxVarLevel, int detailBias, Elevation * variance, byte maxLevel) - { - int distance; - int varIndex; - int ix, iy, iz, tmp; - int x1,y1,x2,y2,x3,y3; - - // *** Compute Distance between this Triangle and the Viewer *** - x1 = (int)(v0.x * resolutionX) - positionX; - y1 = (int)(v0.y * resolutionY) - positionZ; - x2 = (int)(va.x * resolutionX) - positionX; - y2 = (int)(va.y * resolutionY) - positionZ; - x3 = (int)(v1.x * resolutionX) - positionX; - y3 = (int)(v1.y * resolutionY) - positionZ; - if((x1 <= 0) && (x2 <= 0) && (x3 <= 0)) - { - x1 = -x1; - x2 = -x2; - x3 = -x3; - ix = Min(x1,x2); - if(x3 < ix) ix = x3; - } - else if((x1 >= 0) && (x2 >= 0) && (x3 >= 0)) - { - ix = Min(x1,x2); - if(x3 < ix) ix = x3; - } - else ix=0; - - if((y1 <= 0) && (y2 <= 0) && (y3 <= 0)) - { - y1 = -y1; - y2 = -y2; - y3 = -y3; - iz = Min(y1,y2); - if(y3 < iz) iz = y3; - } - else if((y1 >= 0) && (y2 >= 0) && (y3 >= 0)) - { - iz = Min(y1,y2); - if(y3 < iz) iz = y3; - } - else iz=0; - - iy = -heightMap[tv0]; - tmp = -heightMap[tva]; - if(tmp < iy) iy = tmp; - tmp = -heightMap[tv1]; - if(tmp < iy) iy = tmp; - iy -= positionY; - if(iy < 0) iy = -iy; - - if(ix < iy) { tmp = ix; ix = iy; iy = tmp; } - if(ix < iz) { tmp = ix; ix = iz; iz = tmp; } - if(iz > iy) { iz = iy; } - distance = ix + (iz>>1); - - // *** Find the variance table index *** - varIndex = index; - if(level > maxVarLevel) - varIndex >>= (level - maxVarLevel); - varIndex--; - - if(distance < detailBias * variance[varIndex]) - { - // *** Split Further *** - if(!leftChild) - SplitBinTri(stack, triIndex); - level++; - if(leftChild && rightChild && level < maxLevel) - { - leftChild.Split(stack, triIndex, level, resolutionX, resolutionY, positionX, positionY, positionZ, heightMap, maxVarLevel, detailBias, variance, maxLevel); - rightChild.Split(stack, triIndex, level, resolutionX, resolutionY, positionX, positionY, positionZ, heightMap, maxVarLevel, detailBias, variance, maxLevel); - } - } - } -}; +class Elevation : int; class Patch : struct { int offsetX, offsetY; - BinTri leftTri { }; - BinTri rightTri { }; + BinaryTriangle leftTri { }; + BinaryTriangle rightTri { }; int x,y; Elevation *heightMap; Elevation *leftVariance, *rightVariance; Elevation maxHeight, minHeight; Elevation CalcVariance(Elevation * variance, int patchSize, int numBinTris, int numVarTris, - int i, /*struct */Point v0, /*struct */Point va, /*struct */Point v1, /*struct */Point vc) + int i, int tv0, int tva, int tv1, int tvc) { - Elevation real, avg; - int leftchild = i * 2; - Elevation temp1,temp2; - int v; - - int tv0 = (v0.y + offsetY) * patchSize + v0.x + offsetX; - int tv1 = (v1.y + offsetY) * patchSize + v1.x + offsetX; - int tvc = (vc.y + offsetY) * patchSize + vc.x + offsetX; - - - real = heightMap[tvc]; - avg = ((int)heightMap[tv1]+(int)heightMap[tv0]) /2; - - v = Abs(real - avg); + int leftchild = i << 1; + Elevation real = heightMap[tvc]; + Elevation avg = ((int)heightMap[tv1]+(int)heightMap[tv0]) >> 1; + int v = Abs(real - avg); if(leftchild + 1 <= numBinTris) { - temp1 = CalcVariance(variance, patchSize, numBinTris, numVarTris, leftchild, va, vc, v0, { (va.x + v0.x)/2, (va.y + v0.y)/2 }); - temp2 = CalcVariance(variance, patchSize, numBinTris, numVarTris, leftchild+1, v1, vc, va, { (v1.x + va.x)/2, (v1.y + va.y)/2 }); - v = Max(v,temp1); - v = Max(v,temp2); + v = Max(v, CalcVariance(variance, patchSize, numBinTris, numVarTris, leftchild, tva, tvc, tv0, (tva + tv0) >> 1)); + v = Max(v, CalcVariance(variance, patchSize, numBinTris, numVarTris, leftchild + 1, tv1, tvc, tva, (tv1 + tva) >> 1)); } if(i <= numVarTris) - variance[i-1] = v; - + variance[i-1] = v; return v; } }; @@ -287,8 +81,8 @@ class TerrainPatch : struct { Patch patch; int tx,ty; - BinTri leftTri; - BinTri rightTri; + BinaryTriangle * leftTri; + BinaryTriangle * rightTri; Material material; Mesh mesh; PrimitiveGroup group; @@ -416,10 +210,12 @@ class PatchNode : struct minHeight = patch.minHeight; // *** Center Point *** + block = { - ((patch.leftTri.v0.x + patch.leftTri.v1.x) * terrain.resolutionX) / 2, + ((float)-patch.offsetX + (((patch.leftTri.tv0 + patch.leftTri.tv1) >> 1) % terrain.patchSize)) * terrain.resolutionX, -(patch.minHeight + patch.maxHeight) / 2, - ((patch.leftTri.v0.y + patch.leftTri.v1.y) * terrain.resolutionY) / 2 }; + ((float)-patch.offsetY + (((patch.leftTri.tv0 + patch.leftTri.tv1) >> 1) / terrain.patchSize)) * terrain.resolutionY }; + // *** Radius *** dx = (terrain.patchSize - 1) * terrain.resolutionX; dz = (terrain.patchSize - 1) * terrain.resolutionY; @@ -434,14 +230,14 @@ class PatchNode : struct void QuadCheck(int positionX, int positionY, int positionZ, float maxDistance, Camera camera) { - float x1,y1,z1; - // *** Check if this patch is worth our attention *** - x1 = (float)(positionX - block.x); - y1 = (float)(positionY - block.y); - z1 = (float)(positionZ - block.z); - - if(fsqrt(x1, y1, z1) - radius > maxDistance) + Vector3Df v + { + (float)(positionX - block.x), + (float)(positionY - block.y), + (float)(positionZ - block.z) + }; + if(v.lengthApprox - radius > maxDistance) shown = false; else { @@ -479,34 +275,34 @@ class PatchNode : struct y = patch.y; // *** Clear the patch Tris *** - terrainPatch.leftTri.Clear(); - terrainPatch.rightTri.Clear(); + terrainPatch.leftTri->clear(); + terrainPatch.rightTri->clear(); // *** Reestablish Connections *** - terrainPatch.leftTri.bottom = terrainPatch.rightTri; - terrainPatch.rightTri.bottom = terrainPatch.leftTri; + terrainPatch.leftTri->bottom = terrainPatch.rightTri; + terrainPatch.rightTri->bottom = terrainPatch.leftTri; if((x + y) % 2) { if(x > 0 && (terrainPatchPtr - 1)->shown) - terrainPatch.leftTri .left = (terrainPatchPtr - 1)->rightTri; + terrainPatch.leftTri->left = (terrainPatchPtr - 1)->rightTri; if(x < patchesWide - 1 && (terrainPatchPtr + 1)->shown) - terrainPatch.rightTri.left = (terrainPatchPtr + 1)->leftTri; + terrainPatch.rightTri->left = (terrainPatchPtr + 1)->leftTri; if(y > 0 && (terrainPatchPtr - patchesWide)->shown) - terrainPatch.leftTri.right = (terrainPatchPtr - patchesWide)->leftTri; + terrainPatch.leftTri->right = (terrainPatchPtr - patchesWide)->leftTri; if(y < patchesWide - 1 && (terrainPatchPtr + patchesWide)->shown) - terrainPatch.rightTri.right = (terrainPatchPtr + patchesWide)->rightTri; + terrainPatch.rightTri->right = (terrainPatchPtr + patchesWide)->rightTri; } else { if(x > 0 && (terrainPatchPtr - 1)->shown) - terrainPatch.leftTri .right = (terrainPatchPtr - 1)->rightTri; + terrainPatch.leftTri->right = (terrainPatchPtr - 1)->rightTri; if(x < patchesWide - 1 && (terrainPatchPtr + 1)->shown) - terrainPatch.rightTri.right = (terrainPatchPtr + 1)->leftTri; + terrainPatch.rightTri->right = (terrainPatchPtr + 1)->leftTri; if(y > 0 && (terrainPatchPtr - patchesWide)->shown) - terrainPatch.rightTri.left = (terrainPatchPtr - patchesWide)->rightTri; + terrainPatch.rightTri->left = (terrainPatchPtr - patchesWide)->rightTri; if(y < patchesWide - 1 && (terrainPatchPtr + patchesWide)->shown) - terrainPatch.leftTri .left = (terrainPatchPtr + patchesWide)->leftTri; + terrainPatch.leftTri->left = (terrainPatchPtr + patchesWide)->leftTri; } } else @@ -519,17 +315,19 @@ class PatchNode : struct } void QuadSplit(TerrainMesh terrainMesh, - byte maxVarLevel, byte maxLevel, float resolutionX, float resolutionY, - int positionX, int positionY, int positionZ, int detailBias) + int maxVarLevel, int maxLevel, float resolutionX, float resolutionY, + float positionX, float positionY, float positionZ, int detailBias) { TerrainPatch patch = this.patch; if(!shown) return; if(patch) { - patch.leftTri.Split(terrainMesh.binTriStack, &terrainMesh.index, 0, resolutionX, resolutionY, positionX, positionY, positionZ, + // SplitTriangle() will work off patch-relative vertices + float px = positionX + patch.patch.offsetX * resolutionX, py = positionY, pz = positionZ + patch.patch.offsetY * resolutionY; + terrainMesh.SplitTriangle(patch.leftTri, 0, resolutionX, resolutionY, px, py, pz, patch.patch.heightMap, maxVarLevel, detailBias, patch.patch.leftVariance, maxLevel); - patch.rightTri.Split(terrainMesh.binTriStack, &terrainMesh.index, 0, resolutionX, resolutionY, positionX, positionY, positionZ, + terrainMesh.SplitTriangle(patch.rightTri, 0, resolutionX, resolutionY, px, py, pz, patch.patch.heightMap, maxVarLevel, detailBias, patch.patch.rightVariance, maxLevel); } else if(child[0]) @@ -581,7 +379,7 @@ class TerrainMesh : struct // *** Runtime Stuff *** int nTriangles; int maxTris; - struct BinTri * binTriStack; + BinaryTriangle * binTriStack; int index; // *** Stats *** @@ -590,7 +388,87 @@ class TerrainMesh : struct TerrainMesh() { - binTriStack = new struct BinTri[MAXTRIS]; + binTriStack = new BinaryTriangle[MAXTRIS]; + } + + void SplitTriangle(BinaryTriangle tri, int level, float resolutionX, float resolutionY, float positionX, float positionY, float positionZ, + Elevation * heightMap, int maxVarLevel, int detailBias, Elevation * variance, int maxLevel) + { + int distance; + int varIndex; + int ix, iy, iz, tmp; + int size = terrain.patchSize; + int y1 = tri.tv0 / size, x1 = tri.tv0 - (size * y1); + int y2 = tri.tva / size, x2 = tri.tva - (size * y2); + int y3 = tri.tv1 / size, x3 = tri.tv1 - (size * y3); + + // *** Compute Distance between this Triangle and the Viewer *** + x1 = (int)(x1 * resolutionX - positionX); + y1 = (int)(y1 * resolutionY - positionZ); + x2 = (int)(x2 * resolutionX - positionX); + y2 = (int)(y2 * resolutionY - positionZ); + x3 = (int)(x3 * resolutionX - positionX); + y3 = (int)(y3 * resolutionY - positionZ); + if((x1 <= 0) && (x2 <= 0) && (x3 <= 0)) + { + x1 = -x1; + x2 = -x2; + x3 = -x3; + ix = Min(x1,x2); + if(x3 < ix) ix = x3; + } + else if((x1 >= 0) && (x2 >= 0) && (x3 >= 0)) + { + ix = Min(x1,x2); + if(x3 < ix) ix = x3; + } + else ix=0; + + if((y1 <= 0) && (y2 <= 0) && (y3 <= 0)) + { + y1 = -y1; + y2 = -y2; + y3 = -y3; + iz = Min(y1,y2); + if(y3 < iz) iz = y3; + } + else if((y1 >= 0) && (y2 >= 0) && (y3 >= 0)) + { + iz = Min(y1,y2); + if(y3 < iz) iz = y3; + } + else iz=0; + + iy = -heightMap[tri.tv0]; + tmp = -heightMap[tri.tva]; + if(tmp < iy) iy = tmp; + tmp = -heightMap[tri.tv1]; + if(tmp < iy) iy = tmp; + iy -= positionY; + if(iy < 0) iy = -iy; + + if(ix < iy) { tmp = ix; ix = iy; iy = tmp; } + if(ix < iz) { tmp = ix; ix = iz; iz = tmp; } + if(iz > iy) { iz = iy; } + distance = ix + (iz>>1); + + // *** Find the variance table index *** + varIndex = tri.index; + if(level > maxVarLevel) + varIndex >>= (level - maxVarLevel); + varIndex--; + + if(distance < detailBias * variance[varIndex]) + { + // *** Split Further *** + if(!tri.leftTriangle) + tri.split(binTriStack, &index); + if(++level < maxLevel) + { + SplitTriangle(tri.leftTriangle, level, resolutionX, resolutionY, positionX, positionY, positionZ, heightMap, maxVarLevel, detailBias, variance, maxLevel); + SplitTriangle(tri.rightTriangle, level, resolutionX, resolutionY, positionX, positionY, positionZ, heightMap, maxVarLevel, detailBias, variance, maxLevel); + } + } } #define DO_ALTITUDE(h, a, b) \ @@ -634,7 +512,7 @@ class TerrainMesh : struct nx = dy2*dz1-dz2*dy1; \ ny = dz2*dx1-dx2*dz1; \ nz = dx2*dy1-dy2*dx1; \ - m = fsqrt(nx,ny,nz); \ + m = Vector3Df{nx,ny,nz}.lengthApprox; \ if(m) \ { \ cx += nx / m; \ @@ -644,7 +522,7 @@ class TerrainMesh : struct nx = dy3*dz2-dz3*dy2; \ ny = dz3*dx2-dx3*dz2; \ nz = dx3*dy2-dy3*dx2; \ - m = fsqrt(nx,ny,nz); \ + m = Vector3Df{nx,ny,nz}.lengthApprox; \ if(m) \ { \ cx += nx / m; \ @@ -709,14 +587,14 @@ class TerrainMesh : struct return true; } - uint16 * CreateTriangles(BinTri tri, uint16 * indices) + uint16 * CreateTriangles(BinaryTriangle tri, uint16 * indices) { - if(tri.leftChild) + if(tri.leftTriangle) { if(nTriangles < maxTris) - indices = CreateTriangles(tri.leftChild, indices); + indices = CreateTriangles(tri.leftTriangle, indices); if(nTriangles < maxTris) - indices = CreateTriangles(tri.rightChild, indices); + indices = CreateTriangles(tri.rightTriangle, indices); } else { @@ -747,9 +625,9 @@ class TerrainMesh : struct nodes.QuadCheck((int)camera.cPosition.x, (int)camera.cPosition.y, (int)camera.cPosition.z, distance, camera); nodes.QuadClear(terrain.wide); - nodes.QuadSplit(this, (byte)terrain.maxVarLevel, (byte)terrain.maxLevel, + nodes.QuadSplit(this, terrain.maxVarLevel, terrain.maxLevel, terrain.resolutionX, terrain.resolutionY, - (int)camera.cPosition.x, (int)camera.cPosition.y, (int)camera.cPosition.z, (int)bias); + (float)camera.cPosition.x, (float)camera.cPosition.y, (float)camera.cPosition.z, (int)bias); nodes.QuadTriangles(this); } @@ -829,8 +707,8 @@ class TerrainMesh : struct patch.patch = (Patch)&terrain.patches[c]; // *** Set up Root Triangles *** - patch.leftTri = patch.patch.leftTri; - patch.rightTri = patch.patch.rightTri; + patch.leftTri = &patch.patch.leftTri; + patch.rightTri = &patch.patch.rightTri; patch.mesh = Mesh { }; patch.material = Material { opacity = 1, flags = { doubleSided = true } }; @@ -924,7 +802,7 @@ class Terrain this.resolutionX = resolutionX; this.resolutionY = resolutionY; numSamples = size * size; - maxLevel = (uint16)(2 * log2i(size - 1)); + maxLevel = (uint16)((2 * log2i(size - 1)) - 1); numBinTris = (1 << (maxLevel + 1)) - 1; if(skipVarLevel && skipVarLevel < maxLevel) maxVarLevel = (uint16)(maxLevel - skipVarLevel); @@ -939,7 +817,7 @@ class Terrain { for(x = 0; x < patchesCount; x++, c++) { - patch = (Patch)&patches[c]; // = Patch { }; + patch = (Patch)&patches[c]; patch.leftTri = { }; patch.rightTri = { }; @@ -966,39 +844,23 @@ class Terrain if((x + y) % 2) { - patch.leftTri.v0 = { -patch.offsetX,-patch.offsetY + (size-1) }; - patch.leftTri.va = { -patch.offsetX,-patch.offsetY }; - patch.leftTri.v1 = { -patch.offsetX + (size-1), -patch.offsetY }; patch.leftTri.tv0 = numSamples-size; patch.leftTri.tva = 0; patch.leftTri.tv1 = size-1; - patch.rightTri.va = { -patch.offsetX + (size-1), -patch.offsetY + (size-1) }; patch.rightTri.tva = numSamples-1; } else { - patch.leftTri.v0 = { -patch.offsetX + (size-1), -patch.offsetY + (size-1) }; - patch.leftTri.va = { -patch.offsetX,-patch.offsetY + (size-1) }; - patch.leftTri.v1 = { -patch.offsetX,-patch.offsetY }; patch.leftTri.tv0 = numSamples-1; patch.leftTri.tva = numSamples-size; patch.leftTri.tv1 = 0; - patch.rightTri.va = { -patch.offsetX + (size-1), -patch.offsetY }; patch.rightTri.tva = size-1; } - patch.leftTri.vc = - { - (patch.leftTri.v0.x + patch.leftTri.v1.x) / 2, - (patch.leftTri.v0.y + patch.leftTri.v1.y) / 2 - }; patch.leftTri.tvc = (patch.leftTri.tv0 + patch.leftTri.tv1)/2; - patch.rightTri.v0 = patch.leftTri.v1; - patch.rightTri.v1 = patch.leftTri.v0; patch.rightTri.tv0 = patch.leftTri.tv1; patch.rightTri.tv1 = patch.leftTri.tv0; - patch.rightTri.vc = patch.leftTri.vc; patch.rightTri.tvc = patch.leftTri.tvc; patch.leftTri.index = 1; @@ -1013,12 +875,12 @@ class Terrain // *** Compute Variance *** patch.CalcVariance(patch.leftVariance, patchSize, numBinTris, numVarTris, - 1, patch.leftTri.v0,patch.leftTri.va, - patch.leftTri.v1, patch.leftTri.vc); + 1, patch.leftTri.tv0,patch.leftTri.tva, + patch.leftTri.tv1, patch.leftTri.tvc); patch.CalcVariance(patch.rightVariance, patchSize, numBinTris, numVarTris, - 1, patch.rightTri.v0, patch.rightTri.va, - patch.rightTri.v1, patch.rightTri.vc); + 1, patch.rightTri.tv0, patch.rightTri.tva, + patch.rightTri.tv1, patch.rightTri.tvc); } } // *** Set Up Min/Max *** @@ -1172,8 +1034,6 @@ class Terrain delete patch.heightMap; delete patch.leftVariance; delete patch.rightVariance; - delete patch.leftTri; - delete patch.rightTri; } delete patches; } diff --git a/samples/android/android/AndroidManifest.xml b/samples/android/android/AndroidManifest.xml index 6ad27455a2..6e8daf6cfb 100644 --- a/samples/android/android/AndroidManifest.xml +++ b/samples/android/android/AndroidManifest.xml @@ -18,7 +18,7 @@ android:hardwareAccelerated="true"> - @@ -31,5 +31,5 @@ - + diff --git a/samples/android/helloAndroid.epj b/samples/android/helloAndroid.epj index c2d71235ac..0c40284b39 100644 --- a/samples/android/helloAndroid.epj +++ b/samples/android/helloAndroid.epj @@ -14,16 +14,16 @@ ], "PostbuildCommands" : [ "$(call mkdir,$(OBJ)classes)", - "$(call mkdir,$(OBJ)apk/lib/arm64-v8a)", - "javac -verbose -d $(OBJ)/classes -classpath C:/android-sdk/platforms/android-22/android.jar;$(OBJ) -sourcepath . $(MODULE).java", + "$(call mkdir,$(OBJ)apk/lib/$(APP_ABI))", + "javac -verbose -d $(OBJ)/classes -classpath $(ANDROID_PLATFORM)/android.jar$(JAVA_SEP)$(OBJ) -sourcepath . $(MODULE).java", "dx --dex --verbose --output=$(OBJ)apk/classes.dex $(OBJ)classes", - "$(call cp,../../ecere/obj/android.linux.$(COMPILER)/libecere.so,$(OBJ)apk/lib/arm64-v8a)", - "$(call cp,$(TARGET),$(OBJ)apk/lib/arm64-v8a)", - "aapt package -v -f -m -M android/AndroidManifest.xml -F $(OBJ)$(MODULE)-unsigned.apk -I C:/android-sdk/platforms/android-22/android.jar -S android/res $(OBJ)apk", - "jarsigner -sigalg MD5withRSA -digestalg SHA1 -keystore C:/android-sdk/debug.keystore -storepass android $(OBJ)$(MODULE)-unsigned.apk androiddebugkey -signedjar $(OBJ)$(MODULE).apk", + "$(call cp,$(ECERE_SDK_SRC)/ecere/obj/android.$(PLATFORM)$(COMPILER_SUFFIX)$(ARCH_SUFFIX)/libecere.so.0.44,$(OBJ)apk/lib/$(APP_ABI)/libecere.so)", + "$(call cp,obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)/lib$(MODULE).so,$(OBJ)apk/lib/$(APP_ABI))", + "aapt package -v -f -m -M android/AndroidManifest.xml -F $(OBJ)$(MODULE)-unsigned.apk -I $(ANDROID_PLATFORM)/android.jar -S android/res $(OBJ)apk", + "jarsigner -sigalg MD5withRSA -digestalg SHA1 -keystore /android/debug.keystore -storepass android $(OBJ)$(MODULE)-unsigned.apk androiddebugkey -signedjar $(OBJ)$(MODULE).apk", "adb uninstall com.ecere.$(MODULE)", "adb install $(OBJ)$(MODULE).apk", - "adb shell am start -a android.intent.action.MAIN -n com.ecere.$(MODULE)/.hello" + "adb shell am start -a android.intent.action.MAIN -n com.ecere.$(MODULE)/.$(MODULE)" ] }, "Configurations" : [ diff --git a/samples/android/helloAndroid.epj-win.old b/samples/android/helloAndroid.epj-win.old new file mode 100644 index 0000000000..c2d71235ac --- /dev/null +++ b/samples/android/helloAndroid.epj-win.old @@ -0,0 +1,75 @@ +{ + "Version" : 0.2, + "ModuleName" : "hello", + "Options" : { + "Warnings" : "All", + "TargetType" : "SharedLibrary", + "TargetFileName" : "hello", + "Libraries" : [ + "ecere", + "log", + "android", + "EGL", + "GLESv1_CM" + ], + "PostbuildCommands" : [ + "$(call mkdir,$(OBJ)classes)", + "$(call mkdir,$(OBJ)apk/lib/arm64-v8a)", + "javac -verbose -d $(OBJ)/classes -classpath C:/android-sdk/platforms/android-22/android.jar;$(OBJ) -sourcepath . $(MODULE).java", + "dx --dex --verbose --output=$(OBJ)apk/classes.dex $(OBJ)classes", + "$(call cp,../../ecere/obj/android.linux.$(COMPILER)/libecere.so,$(OBJ)apk/lib/arm64-v8a)", + "$(call cp,$(TARGET),$(OBJ)apk/lib/arm64-v8a)", + "aapt package -v -f -m -M android/AndroidManifest.xml -F $(OBJ)$(MODULE)-unsigned.apk -I C:/android-sdk/platforms/android-22/android.jar -S android/res $(OBJ)apk", + "jarsigner -sigalg MD5withRSA -digestalg SHA1 -keystore C:/android-sdk/debug.keystore -storepass android $(OBJ)$(MODULE)-unsigned.apk androiddebugkey -signedjar $(OBJ)$(MODULE).apk", + "adb uninstall com.ecere.$(MODULE)", + "adb install $(OBJ)$(MODULE).apk", + "adb shell am start -a android.intent.action.MAIN -n com.ecere.$(MODULE)/.hello" + ] + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "Console" : true + } + }, + { + "Name" : "Release", + "Options" : { + "Debug" : false, + "Optimization" : "Speed" + } + } + ], + "Files" : [ + { + "Folder" : "android", + "Files" : [ + { + "Folder" : "res", + "Files" : [ + { + "Folder" : "values", + "Files" : [ + "strings.xml" + ] + } + ] + }, + "AndroidManifest.xml" + ] + }, + "helloAndroid.ec", + "note.txt", + "hello.java" + ], + "ResourcesPath" : "", + "Resources" : [ + "res/ecere sdk.png" + ] +} diff --git a/samples/audio/Piano/Piano.ec b/samples/audio/Piano/Piano.ec index dd76b0c29b..8eccf2d268 100644 --- a/samples/audio/Piano/Piano.ec +++ b/samples/audio/Piano/Piano.ec @@ -1,3 +1,4 @@ +import "ecere" // TODO: Check why this is newly needed? import "EcereAudio" // There are 12 half-tones in an octave, and the frequency doubles in an octave. diff --git a/samples/bindings/c/01_console/console.c b/samples/bindings/c/01_console/console.c new file mode 100644 index 0000000000..b1d2797f8e --- /dev/null +++ b/samples/bindings/c/01_console/console.c @@ -0,0 +1,9 @@ +#define __CONSOLE_APP__ +#include "eC.h" + +APP_INTRO(0) +{ +// eC_printLn(class_String, __STDC_VERSION__, null); + eC_printLn(class_String, "C: Hello, eC!", null); +} +APP_OUTRO diff --git a/samples/bindings/c/01_console/console.epj b/samples/bindings/c/01_console/console.epj new file mode 100644 index 0000000000..f1916af118 --- /dev/null +++ b/samples/bindings/c/01_console/console.epj @@ -0,0 +1,69 @@ +{ + "Version" : 0.2, + "ModuleName" : "console", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_SRC)/bindings/c" + ], + "TargetType" : "Executable", + "TargetFileName" : "console", + "Libraries" : [ + "ecereCOM" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Dynamic", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "Libraries" : [ + "ecereCOM", + "eC_c" + ], + "FastMath" : false + } + }, + { + "Name" : "Embedded", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + "$(ECERE_SDK_SRC)/bindings/c/eC.c", + "$(ECERE_SDK_SRC)/bindings/c/eC.h" + ], + "Options" : { + "ExcludeFromBuild" : true + }, + "Configurations" : [ + { + "Name" : "Embedded", + "Options" : { + "ExcludeFromBuild" : false + } + } + ] + }, + "console.c" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/c/01_console/console_dllbind.epj b/samples/bindings/c/01_console/console_dllbind.epj new file mode 100644 index 0000000000..b1151a6a09 --- /dev/null +++ b/samples/bindings/c/01_console/console_dllbind.epj @@ -0,0 +1,51 @@ +{ + "Version" : 0.2, + "ModuleName" : "console", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS)/c" + ], + "TargetType" : "Executable", + "TargetFileName" : "console", + "Libraries" : [ + "ecereCOM", + "eC_c" + ], + "LinkerOptions" : [ + "--no-undefined" + ], + "LibraryDirs" : [ + "$(ECERE_SDK_BINDINGS)/c/ecereCOM/obj/ecereCOM.release.win32", + "$(ECERE_SDK_SRC)/obj/$(PLATFORM)/bin", + "$(ECERE_SDK_SRC)/obj/$(PLATFORM)/lib" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + "$(ECERE_SDK_BINDINGS)/c/eC.h" + ] + }, + "console.c" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/c/02_gui/gui.c b/samples/bindings/c/02_gui/gui.c new file mode 100644 index 0000000000..39379c6d63 --- /dev/null +++ b/samples/bindings/c/02_gui/gui.c @@ -0,0 +1,50 @@ +#define MODULE_NAME "gui" + +#include "ecere.h" + +static eC_Class * class_HelloWindow; + +struct class_members_HelloWindow { eC_Label label; }; + +typedef eC_Instance eC_HelloWindow; + +static eC_bool HelloWindow_constructor(eC_HelloWindow this) +{ + struct class_members_HelloWindow * self = IPTR(this, HelloWindow); + Window_set_caption(this, $("My First Ecere/C Bindings App")); + Window_set_borderStyle(this, BorderStyle_sizable); + Window_set_size(this, &(eC_Size){ 380, 190 }); + Window_set_hasClose(this, true); + + eC_FontResource font = newi(FontResource); + FontResource_set_faceName(font, "Arial"); + FontResource_set_size(font, 30); + + /*char tmp[256]; + String s = Instance_onGetString(0, font, tmp, null, null); + PrintLn(class_String, s, null); // Need to terminate with a null!*/ + + self->label = newi(Label); + Window_set_parent(self->label, this); + Window_set_position(self->label, &(eC_Point){ 10, 10 }); + Window_set_font(self->label, font); + Window_set_caption(self->label, $("Hello, Bindings!!")); + return true; +} + +static void HelloWindow_destructor(eC_HelloWindow this) +{ + struct class_members_HelloWindow * self = IPTR(this, HelloWindow); + deletei(self->label); +} + +GUIAPP_INTRO +{ + class_HelloWindow = registerClass(app, HelloWindow, Window); + eC_HelloWindow hello = newi(HelloWindow); + + Application_main(app); + + deletei(hello); +} +ECERE_APP_OUTRO diff --git a/samples/bindings/c/02_gui/gui.epj b/samples/bindings/c/02_gui/gui.epj new file mode 100644 index 0000000000..94251103fb --- /dev/null +++ b/samples/bindings/c/02_gui/gui.epj @@ -0,0 +1,71 @@ +{ + "Version" : 0.2, + "ModuleName" : "gui", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_SRC)/bindings/c" + ], + "TargetType" : "Executable", + "TargetFileName" : "gui", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Dynamic", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "Libraries" : [ + "ecere", + "ecere_c" + ], + "FastMath" : false + } + }, + { + "Name" : "Embedded", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + "$(ECERE_SDK_SRC)/bindings/c/eC.c", + "$(ECERE_SDK_SRC)/bindings/c/eC.h", + "$(ECERE_SDK_SRC)/bindings/c/ecere.c", + "$(ECERE_SDK_SRC)/bindings/c/ecere.h" + ], + "Options" : { + "ExcludeFromBuild" : true + }, + "Configurations" : [ + { + "Name" : "Embedded", + "Options" : { + "ExcludeFromBuild" : false + } + } + ] + }, + "gui.c" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/c/02_gui/gui_dllbind.epj b/samples/bindings/c/02_gui/gui_dllbind.epj new file mode 100644 index 0000000000..781eb18220 --- /dev/null +++ b/samples/bindings/c/02_gui/gui_dllbind.epj @@ -0,0 +1,47 @@ +{ + "Version" : 0.2, + "ModuleName" : "gui", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS)/c" + ], + "TargetType" : "Executable", + "TargetFileName" : "gui", + "Libraries" : [ + "ecere", + "ecere_c" + ], + "LibraryDirs" : [ + "$(ECERE_SDK_BINDINGS)/c/ecere/obj/release.win32" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + "$(ECERE_SDK_BINDINGS)/c/eC.h", + "$(ECERE_SDK_BINDINGS)/c/ecere.h" + ] + }, + "gui.c" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/c/03_button/button.c b/samples/bindings/c/03_button/button.c new file mode 100644 index 0000000000..43bd6041e5 --- /dev/null +++ b/samples/bindings/c/03_button/button.c @@ -0,0 +1,66 @@ +#define MODULE_NAME "button" + +#include "ecere.h" + +static eC_Class * class_HelloWindow; + +struct class_members_HelloWindow { eC_Button button; }; + +typedef eC_Instance eC_HelloWindow; + +static void HelloWindow_onRedraw(eC_HelloWindow this, eC_Surface surface) +{ + // Surface_writeTextf(surface, 100, 100, I18N("Testing stuff!")); + Surface_writeTextf(surface, 100, 100, "%d + %d = %d", 2, 3, 2+3); +} + +static eC_bool HelloWindow_button_notifyClicked(eC_HelloWindow this, eC_Button button, int x, int y, eC_Modifiers mods) +{ + eC_MessageBox msgBox = newi(MessageBox); + double i = 3.14159265; + char tmp[256]; + const char * s = _onGetString(class_double, &i, tmp, null, null); + eC_printLn(class_String, "Hello! -- ", class_String, s, null); // Need to terminate with a null! + Window_set_caption(msgBox, $("Hello!")); + MessageBox_set_contents(msgBox, $("C Bindings!")); + Window_modal(msgBox); + return true; +} + +static eC_bool HelloWindow_constructor(eC_HelloWindow this) +{ + struct class_members_HelloWindow * self = IPTR(this, HelloWindow); + Window_set_caption(this, $("My Second Ecere/C Bindings App")); + Window_set_borderStyle(this, BorderStyle_sizable); + Window_set_clientSize(this, &(eC_Size){ 640, 480 }); + Window_set_hasClose(this, true); + Window_set_hasMaximize(this, true); + Window_set_hasMinimize(this, true); + Window_set_background(this, SystemColor_formColor); + + self->button = newi(Button); + Window_set_parent(self->button, this); + Window_set_position(self->button, &(eC_Point){ 200, 200 }); + Window_set_caption(self->button, $("Yay!!")); + Instance_setMethod(self->button, "NotifyClicked", HelloWindow_button_notifyClicked); + return true; +} + +static void HelloWindow_destructor(eC_HelloWindow this) +{ + struct class_members_HelloWindow * self = IPTR(this, HelloWindow); + deletei(self->button); +} + +GUIAPP_INTRO +{ + class_HelloWindow = registerClass(app, HelloWindow, Window); + addMethod(class_HelloWindow, "OnRedraw", HelloWindow_onRedraw); + + eC_HelloWindow hello = newi(HelloWindow); + + Application_main(app); + + deletei(hello); +} +ECERE_APP_OUTRO diff --git a/samples/bindings/c/03_button/button.epj b/samples/bindings/c/03_button/button.epj new file mode 100644 index 0000000000..498fd5a5be --- /dev/null +++ b/samples/bindings/c/03_button/button.epj @@ -0,0 +1,71 @@ +{ + "Version" : 0.2, + "ModuleName" : "button", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_SRC)/bindings/c" + ], + "TargetType" : "Executable", + "TargetFileName" : "button", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Dynamic", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "Libraries" : [ + "ecere", + "ecere_c" + ], + "FastMath" : false + } + }, + { + "Name" : "Embedded", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + "$(ECERE_SDK_SRC)/bindings/c/eC.c", + "$(ECERE_SDK_SRC)/bindings/c/eC.h", + "$(ECERE_SDK_SRC)/bindings/c/ecere.c", + "$(ECERE_SDK_SRC)/bindings/c/ecere.h" + ], + "Options" : { + "ExcludeFromBuild" : true + }, + "Configurations" : [ + { + "Name" : "Embedded", + "Options" : { + "ExcludeFromBuild" : false + } + } + ] + }, + "button.c" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/c/03_button/button_dllbind.epj b/samples/bindings/c/03_button/button_dllbind.epj new file mode 100644 index 0000000000..8eaccb55fb --- /dev/null +++ b/samples/bindings/c/03_button/button_dllbind.epj @@ -0,0 +1,47 @@ +{ + "Version" : 0.2, + "ModuleName" : "button", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS)/c" + ], + "TargetType" : "Executable", + "TargetFileName" : "button", + "Libraries" : [ + "ecere", + "ecere_c" + ], + "LibraryDirs" : [ + "$(ECERE_SDK_BINDINGS)/c/ecere/obj/release.win32" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + "$(ECERE_SDK_BINDINGS)/c/eC.h", + "$(ECERE_SDK_BINDINGS)/c/ecere.h" + ] + }, + "button.c" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/c/04_module/module.c b/samples/bindings/c/04_module/module.c new file mode 100644 index 0000000000..2da2c20662 --- /dev/null +++ b/samples/bindings/c/04_module/module.c @@ -0,0 +1,79 @@ +#define MODULE_NAME "module" + +#include "ecere.h" + +static eC_Class * class_HelloWindow; + +struct class_members_HelloWindow { eC_Label label; eC_Button button; }; + +typedef eC_Instance eC_HelloWindow; + +static eC_bool HelloWindow_button_notifyClicked(eC_HelloWindow this, eC_Button button, int x, int y, eC_Modifiers mods) +{ + eC_MessageBox msgBox = newi(MessageBox); + double i = 3.14159265; + char tmp[256]; + //constString s = _onGetString(class_double, &i, tmp, null, null); + const char * s = _onGetString(class_double, &i, tmp, null, null); + + eC_printLn(class_String, "Hello! -- ", class_String, s, null); // Need to terminate with a null! + Window_set_caption(msgBox, $("Hello!")); + MessageBox_set_contents(msgBox, $("C Bindings!")); + Window_modal(msgBox); + + // Recursive call to test Button_notifyClicked(): + Button_notifyClicked(button, this, button, x, y, mods); + return true; +} + +static eC_bool HelloWindow_constructor(eC_HelloWindow this) +{ + struct class_members_HelloWindow * self = IPTR(this, HelloWindow); + Window_set_caption(this, $("My First Ecere/C Bindings App")); + Window_set_borderStyle(this, BorderStyle_sizable); + Window_set_size(this, &(eC_Size){ 380, 190 }); + Window_set_hasClose(this, true); + + eC_FontResource font = newi(FontResource); + FontResource_set_faceName(font, "Arial"); + FontResource_set_size(font, 30); + + /*char tmp[256]; + eC_String s = Instance_onGetString(0, font, tmp, null, null); + PrintLn(class_String, s, null); // Need to terminate with a null!*/ + + self->label = newi(Label); + Window_set_parent(self->label, this); + Window_set_position(self->label, &(eC_Point){ 10, 10 }); + Window_set_font(self->label, font); + Window_set_caption(self->label, $("Hello, Bindings!!")); + + self->button = newi(Button); + Window_set_parent(self->button, this); + Window_set_position(self->button, &(eC_Point){ 10, 70 }); + Window_set_font(self->button, font); + Window_set_caption(self->button, $("Push!! (recursive)")); + Instance_setMethod(self->button, "NotifyClicked", HelloWindow_button_notifyClicked); + return true; +} + +static void HelloWindow_destructor(eC_HelloWindow this) +{ + struct class_members_HelloWindow * self = IPTR(this, HelloWindow); + deletei(self->label); +} + +MODULE_LOAD +{ + eC_init(module, true, false, 0, null); + ecere_init(module); + loadTranslatedStrings(null, MODULE_NAME); + class_HelloWindow = registerClass(module, HelloWindow, Window); + return true; +} + +MODULE_UNLOAD +{ + unloadTranslatedStrings(MODULE_NAME); + return true; +} diff --git a/samples/bindings/c/04_module/module.epj b/samples/bindings/c/04_module/module.epj new file mode 100644 index 0000000000..38ab25a867 --- /dev/null +++ b/samples/bindings/c/04_module/module.epj @@ -0,0 +1,70 @@ +{ + "Version" : 0.2, + "ModuleName" : "module", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_SRC)/bindings/c" + ], + "TargetType" : "SharedLibrary", + "TargetFileName" : "module", + "Libraries" : [ + "ecere" + ] + }, + "Configurations" : [ + { + "Name" : "Dynamic", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "Libraries" : [ + "ecere", + "ecere_c" + ], + "FastMath" : false + } + }, + { + "Name" : "Embedded", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + "$(ECERE_SDK_SRC)/bindings/c/eC.c", + "$(ECERE_SDK_SRC)/bindings/c/eC.h", + "$(ECERE_SDK_SRC)/bindings/c/ecere.c", + "$(ECERE_SDK_SRC)/bindings/c/ecere.h" + ], + "Options" : { + "ExcludeFromBuild" : true + }, + "Configurations" : [ + { + "Name" : "Embedded", + "Options" : { + "ExcludeFromBuild" : false + } + } + ] + }, + "module.c" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/c/04_module/module_dllbind.epj b/samples/bindings/c/04_module/module_dllbind.epj new file mode 100644 index 0000000000..9a061713a0 --- /dev/null +++ b/samples/bindings/c/04_module/module_dllbind.epj @@ -0,0 +1,46 @@ +{ + "Version" : 0.2, + "ModuleName" : "module", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS)/c" + ], + "TargetType" : "SharedLibrary", + "TargetFileName" : "module", + "Libraries" : [ + "ecere", + "ecere_c" + ], + "LibraryDirs" : [ + "$(ECERE_SDK_BINDINGS)/c/ecere/obj/release.win32" + ] + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + "$(ECERE_SDK_BINDINGS)/c/eC.h", + "$(ECERE_SDK_BINDINGS)/c/ecere.h" + ] + }, + "module.c" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/c/04_module/tester.ec b/samples/bindings/c/04_module/tester.ec new file mode 100644 index 0000000000..34f7ce5624 --- /dev/null +++ b/samples/bindings/c/04_module/tester.ec @@ -0,0 +1,36 @@ +import "ecere" + +#define TEST_MODULE_NAME "module" + +class ModuleTester : Window +{ + caption = $"Form1"; + background = formColor; + borderStyle = sizable; + hasMaximize = true; + hasMinimize = true; + hasClose = true; + clientSize = { 632, 438 }; + + Button button1 + { + this, caption = $"button1", position = { 128, 112 }; + + bool NotifyClicked(Button button, int x, int y, Modifiers mods) + { + // build module.epj first + Module m = eModule_Load(__thisModule, TEST_MODULE_NAME, publicAccess); + if(m) + { + Class c = eSystem_FindClass(m, "HelloWindow"); + Window w = (Window)eInstance_New(c); + w.Create(); + } + else + PrintLn("Failed to load library '", TEST_MODULE_NAME, "'"); + return true; + } + }; +} + +ModuleTester moduleTester {}; diff --git a/samples/bindings/c/04_module/tester.epj b/samples/bindings/c/04_module/tester.epj new file mode 100644 index 0000000000..4ecfb246be --- /dev/null +++ b/samples/bindings/c/04_module/tester.epj @@ -0,0 +1,33 @@ +{ + "Version" : 0.2, + "ModuleName" : "tester", + "Options" : { + "Warnings" : "All", + "TargetType" : "Executable", + "TargetFileName" : "tester", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + "tester.ec" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/c/05_templates/templates.c b/samples/bindings/c/05_templates/templates.c new file mode 100644 index 0000000000..133ff134db --- /dev/null +++ b/samples/bindings/c/05_templates/templates.c @@ -0,0 +1,119 @@ +#include "ecere.h" + +eC_Class * class_Array_ColorAlpha; +eC_Class * class_Array_double; +eC_Class * class_Foo; +eC_Class * class_Foo_double; +eC_Class * class_Foo_String; +typedef eC_Array T(Array, ColorAlpha); +typedef eC_Array T(Array, double); +typedef eC_Map T(Map, String, ColorAlpha); +typedef eC_Instance eC_Foo; +typedef uint64_t TP(Foo, A); + +struct class_members_Foo { int foo; }; +eC_bool Foo_constructor(eC_Foo this) +{ + struct class_members_Foo * self = IPTR(this, Foo); + self->foo = 5; + return true; +} +void Foo_destructor(eC_Foo this) { } + +#define FOO_TP_A_INDEX 0 + +void Foo_add(eC_Foo this, TP(Foo, A) value) +{ + eC_ClassTemplateArgument * tArgs = this->_class->templateArgs; + int tCount = this->_class->templateParams.count; + int baseParam = this->_class->numParams - tCount; + eC_ClassTemplateArgument * tArg = tArgs ? &tArgs[baseParam + FOO_TP_A_INDEX] : null; + eC_Class * c = null; + void * p; + eC_DataValue dv; + if(tArg) + { + if(!tArg->dataTypeClass) + tArg->dataTypeClass = eC_findClass(this->_class->module, tArg->dataTypeString); + c = tArg->dataTypeClass; + } + if(!c) c = class_int; + switch(c->type) + { + case ClassType_noHeadClass: case ClassType_normalClass: + case ClassType_structClass: case ClassType_unionClass: + p = pTA(void, value); + break; + case ClassType_bitClass: case ClassType_unitClass: + case ClassType_enumClass: case ClassType_systemClass: + { + eC_Class * dc = c; + if(c->type != ClassType_systemClass) + { + // NOTE: Storing Class in dataType here because we don't have a 'dataTypeClass', since Type is only used by eC compiler + if(!c->dataType) + c->dataType = (eC_Type *) eC_findClass(c->module, c->dataTypeString); + if(c->dataType) + dc = (eC_Class *)c->dataType; + } + if(dc == class_double) dv.d = dTA(value), p = &dv.d; + else if(dc == class_float) dv.f = fTA(value), p = &dv.f; + else if(dc == class_char) dv.c = cTA(value), p = &dv.c; + else if(dc == class_byte) dv.uc = bTA(value), p = &dv.uc; + else if(dc == class_short) dv.s = sTA(value), p = &dv.s; + else if(dc == class_uint16) dv.us = usTA(value), p = &dv.us; + else if(dc == class_int) dv.i = iTA(value), p = &dv.i; + else if(dc == class_uint) dv.ui = uiTA(value), p = &dv.ui; + else /* if(c == class_int64) */ dv.ui64 = ui64TA(value), p = &dv.ui64; + break; + } + } + eC_printLn(c, p, null); +} + +int main(int argc, char *argv[]) +{ + eC_Application module = eC_init(null, true, false, argc, argv); + //C(Window) win = newi(Window); + double d; + eC_Foo foo; + + ecere_init(module); + + T(Array, ColorAlpha) a; + T(Array, double) ad; + //T(Map, String, ColorAlpha) m; + + class_Array_ColorAlpha = eC_findClass(module, "Array"); + a = newi(Array, ColorAlpha); + Container_add(a, ColorAlpha_from_Color(DefinedColor_red)); + Container_add(a, ColorAlpha_from_Color(DefinedColor_blue)); + //Container_add(a, COLORALPHA(255, red)); + eC_printLn(a->_class, a, null); + + class_Array_double = eC_findClass(module, "Array"); + ad = newi(Array, double); + Container_add(ad, TAd(3.14159)); + eC_printLn(ad->_class, ad, null); + + d = ((double *)IPTR(ad, Array)->array)[0]; + eC_printLn(class_double, &d, null); + + // class_Foo = registerClass(module, Foo, Instance); + class_Foo = registerClass(module, Foo, Map); // why is this not in double quote? + Class_addTemplateParameter(class_Foo, "A", TemplateParameterType_type, null, null); + Class_doneAddingTemplateParameters(class_Foo); + addMethod(class_Foo, "add", Foo_add); + + class_Foo_double = eC_findClass(module, "Foo"); + foo = newi(Foo, double); + Foo_add(foo, TAd(3.14159)); + + class_Foo_String = eC_findClass(module, "Foo"); + foo = newi(Foo, String); + Foo_add(foo, TAp("Hello, Templates in C!")); + Foo_add(foo, TAp("Hello Again!")); + + eC_printLn(foo->_class, foo, null); // todo, need a OnGetString for this to work? + return 0; +} diff --git a/samples/bindings/c/05_templates/templates.epj b/samples/bindings/c/05_templates/templates.epj new file mode 100644 index 0000000000..7629947bc0 --- /dev/null +++ b/samples/bindings/c/05_templates/templates.epj @@ -0,0 +1,71 @@ +{ + "Version" : 0.2, + "ModuleName" : "templates", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_SRC)/bindings/c" + ], + "TargetType" : "Executable", + "TargetFileName" : "templates", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Dynamic", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "Libraries" : [ + "ecere", + "ecere_c" + ], + "FastMath" : false + } + }, + { + "Name" : "Embedded", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + "$(ECERE_SDK_SRC)/bindings/c/eC.c", + "$(ECERE_SDK_SRC)/bindings/c/eC.h", + "$(ECERE_SDK_SRC)/bindings/c/ecere.c", + "$(ECERE_SDK_SRC)/bindings/c/ecere.h" + ], + "Options" : { + "ExcludeFromBuild" : true + }, + "Configurations" : [ + { + "Name" : "Embedded", + "Options" : { + "ExcludeFromBuild" : false + } + } + ] + }, + "templates.c" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/c/05_templates/templates_dllbind.epj b/samples/bindings/c/05_templates/templates_dllbind.epj new file mode 100644 index 0000000000..64eebafd39 --- /dev/null +++ b/samples/bindings/c/05_templates/templates_dllbind.epj @@ -0,0 +1,47 @@ +{ + "Version" : 0.2, + "ModuleName" : "templates", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS)/c" + ], + "TargetType" : "Executable", + "TargetFileName" : "templates", + "Libraries" : [ + "ecere", + "ecere_c" + ], + "LibraryDirs" : [ + "$(ECERE_SDK_BINDINGS)/c/ecere/obj/release.win32" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + "$(ECERE_SDK_BINDINGS)/c/eC.h", + "$(ECERE_SDK_BINDINGS)/c/ecere.h" + ] + }, + "templates.c" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/c/06_cube/cube.c b/samples/bindings/c/06_cube/cube.c new file mode 100644 index 0000000000..cd699010d7 --- /dev/null +++ b/samples/bindings/c/06_cube/cube.c @@ -0,0 +1,116 @@ +#define MODULE_NAME "gui" + +#include "ecere.h" + +static eC_Class * class_CubeWindow; + +struct class_members_CubeWindow { eC_Camera camera; eC_Light * light; eC_Cube * cube; }; + +typedef eC_Instance eC_CubeWindow; + +static eC_bool CubeWindow_onLoadGraphics(eC_CubeWindow this) +{ + struct class_members_CubeWindow * self = IPTR(this, CubeWindow); + eC_DisplaySystem displaySystem = Window_get_displaySystem(this); + Cube_create(self->cube, displaySystem); + { + eC_Transform transform; + Object_get_transform((eC_Object*)self->cube, &transform); + transform.scaling = (eC_Vector3Df){ 100, 100, 100 }; + { + eC_Quaternion orientation; + Euler_to_Quaternion(&(eC_Euler){ Degrees(50), Degrees(30), Degrees(50) }, &orientation); + transform.orientation = orientation; + } + Object_set_transform((eC_Object*)self->cube, &transform); + } + Object_updateTransform((eC_Object*)self->cube); + return true; +} + +static eC_bool CubeWindow_onUnloadGraphics(eC_CubeWindow this) +{ + struct class_members_CubeWindow * self = IPTR(this, CubeWindow); + eC_DisplaySystem displaySystem = Window_get_displaySystem(this); + Object_free((eC_Object *)self->cube, displaySystem); // Shouldn't we have a Cube_free() macro casting to base class? + return true; +} + +static void CubeWindow_onResize(eC_CubeWindow this, int w, int h) +{ + struct class_members_CubeWindow * self = IPTR(this, CubeWindow); + Camera_setup(self->camera, w, h, null); + Camera_update(self->camera); +} + +static void CubeWindow_onRedraw(eC_CubeWindow this, eC_Surface surface) +{ + struct class_members_CubeWindow * self = IPTR(this, CubeWindow); + eC_Display display = Window_get_display(this); + Surface_clear(surface, ClearType_depthBuffer); + Display_setLight(display, 0, self->light); + Display_setCamera(display, surface, self->camera); + Display_drawObject(display, (eC_Object*)self->cube); + Display_setCamera(display, surface, null); +} + +static eC_bool CubeWindow_constructor(eC_CubeWindow this) +{ + struct class_members_CubeWindow * self = IPTR(this, CubeWindow); + Window_set_caption(this, $("My First Ecere/C Bindings App")); + Window_set_borderStyle(this, BorderStyle_sizable); + Window_set_background(this, DefinedColor_black); + Window_set_size(this, &(eC_Size){ 640, 480 }); + Window_set_hasClose(this, true); + + eC_FontResource font = newi(FontResource); + FontResource_set_faceName(font, "Arial"); + FontResource_set_size(font, 30); + + self->camera = newi(Camera); + Camera_set_type(self->camera, CameraType_fixed); + Camera_set_position(self->camera, &(eC_Vector3D){ 0, 0, -300 }); + Camera_set_eulerOrientation(self->camera, &(eC_Euler){ 0, 0, 0 }); + Camera_set_fov(self->camera, Degrees(53)); + + self->cube = newcs(Cube); + self->light = newt0(eC_Light, 1); + { + eC_ColorRGB diffuse; + Color_to_ColorRGB(DefinedColor_lightCoral, &diffuse); + self->light->diffuse = diffuse; + } + { + eC_Quaternion orientation; + Euler_to_Quaternion(&(eC_Euler){ Degrees(30), Degrees(10) }, &orientation); // { pitch = 10, yaw = 30 } + self->light->orientation = orientation; + } + + Instance_setMethod(this, "OnLoadGraphics", CubeWindow_onLoadGraphics); + Instance_setMethod(this, "OnUnloadGraphics", CubeWindow_onUnloadGraphics); + Instance_setMethod(this, "OnResize", CubeWindow_onResize); + Instance_setMethod(this, "OnRedraw", CubeWindow_onRedraw); + return true; +} + +static void CubeWindow_destructor(eC_CubeWindow this) +{ + struct class_members_CubeWindow * self = IPTR(this, CubeWindow); + deletei(self->camera); + delete(self->cube); + delete(self->light); +} + +GUIAPP_INTRO +{ + // do all class registration first. + class_CubeWindow = registerClass(app, CubeWindow, Window); + // application code start here. + GuiApplication_set_driver(app, "OpenGL"); + eC_CubeWindow hello = newi(CubeWindow); + + Application_main(app); + + deletei(hello); +} +ECERE_APP_OUTRO diff --git a/samples/bindings/c/06_cube/cube.epj b/samples/bindings/c/06_cube/cube.epj new file mode 100644 index 0000000000..58f0f32c6d --- /dev/null +++ b/samples/bindings/c/06_cube/cube.epj @@ -0,0 +1,71 @@ +{ + "Version" : 0.2, + "ModuleName" : "gui", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_SRC)/bindings/c" + ], + "TargetType" : "Executable", + "TargetFileName" : "gui", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Dynamic", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "Libraries" : [ + "ecere", + "ecere_c" + ], + "FastMath" : false + } + }, + { + "Name" : "Embedded", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + "$(ECERE_SDK_SRC)/bindings/c/eC.c", + "$(ECERE_SDK_SRC)/bindings/c/eC.h", + "$(ECERE_SDK_SRC)/bindings/c/ecere.c", + "$(ECERE_SDK_SRC)/bindings/c/ecere.h" + ], + "Options" : { + "ExcludeFromBuild" : true + }, + "Configurations" : [ + { + "Name" : "Embedded", + "Options" : { + "ExcludeFromBuild" : false + } + } + ] + }, + "cube.c" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/c/06_cube/cube_dllbind.epj b/samples/bindings/c/06_cube/cube_dllbind.epj new file mode 100644 index 0000000000..c939a4dd25 --- /dev/null +++ b/samples/bindings/c/06_cube/cube_dllbind.epj @@ -0,0 +1,47 @@ +{ + "Version" : 0.2, + "ModuleName" : "gui", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS)/c" + ], + "TargetType" : "Executable", + "TargetFileName" : "gui", + "Libraries" : [ + "ecere", + "ecere_c" + ], + "LibraryDirs" : [ + "$(ECERE_SDK_BINDINGS)/c/ecere/obj/release.win32" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + "$(ECERE_SDK_BINDINGS)/c/eC.h", + "$(ECERE_SDK_BINDINGS)/c/ecere.h" + ] + }, + "cube.c" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/cpp/01_consoleAppRegisterEvolved/consoleAppRegisterEvolved.cpp b/samples/bindings/cpp/01_consoleAppRegisterEvolved/consoleAppRegisterEvolved.cpp new file mode 100644 index 0000000000..12c1df22c0 --- /dev/null +++ b/samples/bindings/cpp/01_consoleAppRegisterEvolved/consoleAppRegisterEvolved.cpp @@ -0,0 +1,208 @@ +#define MODULE_NAME "MyApp" +#define __CONSOLE_APP__ // affects MAIN_DECLARATION and eC_init_CALL definition -- anything else? +#define __LINK_ECERE__ 0 + +#include "eC.hpp" +// #include "ecere.hpp" + +// Demonstrates overriding Application class, evolving instance from REGISTER() + +#define USE_MACROS + +#if defined(USE_MACROS) + +class MyApp; +extern MyApp app; + +class MyApp : public Application +{ +public: + APP_CONSTRUCT(MyApp, Application) { } + + void main() + { + // breakpoint on first line - and when not much else is in main() - with eC_printLn or printLn won't work + // eC_printLn(class_String, "C++: Hello, eC", null); // just this line won't break + // printLn("C++: Hello, eC"); // just this line won't break + // printLn("C++: Hello, eC", 1); // this will break + printf("why\n"); // adding this before will allow the breakpoint on printLn or printLn2 to work + printLn("C++: Hello, eC"); + + // printf("why this is so messed up\n"); + // printf("\n"); + // eC_printLn(class_String, "C++: Hello, eC", null); + // eC_printLn(class_String, "C++: Hello, eC", null); + + // eC_printLn(class_String, "wth", null); + // printf("why\n"); + // printLn("C++: Hello, eC", 5, 4.3, '2'); + // printf("%d\n", 5); + } + + REGISTER() + { + REG_Application(MyApp); + /* + Application::class_registration(_class); + register_main(_class, + [](Application & app) + { + eC_printLn(class_String, "C++: Hello, eC", null); + } + ); + */ + EVOLVE_APP(MyApp, app); + } +}; + +MyApp app; + +REGISTER_CLASS_DEF(MyApp, Application, app); + +MAIN_DEFINITION; + +#else + +class MyApp; +extern MyApp app; + +class MyApp : public Application +{ +public: +// APP_CONSTRUCT(MyApp, Application) { } + inline MyApp() : MyApp(eC_init_CALL(__LINK_ECERE__)) { } + + struct Instance_onCompare_Functor + { + int _[0]; + typedef int (* FunctionType)(Instance & _ARG Instance & object); + inline FunctionType operator= (FunctionType func); + inline int operator()( Instance & object); + } onCompare; + inline void register_onCompare(CPPClass & cl, Instance::Instance_onCompare_Functor::FunctionType func); + + struct Instance_onCopy_Functor + { + int _[0]; + typedef void (* FunctionType)(Instance & _ARG Instance & newData); + inline FunctionType operator= (FunctionType func); + inline void operator()( Instance & newData); + } onCopy; + inline void register_onCopy(CPPClass & cl, Instance::Instance_onCopy_Functor::FunctionType func); + + struct Instance_onDisplay_Functor + { + int _[0]; + typedef void (* FunctionType)(Instance & _ARG Instance /*Surface*/ & surface _ARG int x _ARG int y _ARG int width _ARG void * fieldData _ARG C(Alignment) alignment _ARG C(DataDisplayFlags) displayFlags); + inline FunctionType operator= (FunctionType func); + inline void operator()( Instance /*Surface*/ & surface _ARG int x _ARG int y _ARG int width _ARG void * fieldData _ARG C(Alignment) alignment _ARG C(DataDisplayFlags) displayFlags); + } onDisplay; + inline void register_onDisplay(CPPClass & cl, Instance::Instance_onDisplay_Functor::FunctionType func); + + struct Instance_onEdit_Functor + { + int _[0]; + typedef Instance & (* FunctionType)(Instance & _ARG Instance /*DataBox*/ & dataBox _ARG Instance /*DataBox*/ & obsolete _ARG int x _ARG int y _ARG int w _ARG int h _ARG void * userData); + inline FunctionType operator= (FunctionType func); + inline Instance & operator()( Instance /*DataBox*/ & dataBox _ARG Instance /*DataBox*/ & obsolete _ARG int x _ARG int y _ARG int w _ARG int h _ARG void * userData); + } onEdit; + inline void register_onEdit(CPPClass & cl, Instance::Instance_onEdit_Functor::FunctionType func); + + struct Instance_onFree_Functor + { + int _[0]; + typedef void (* FunctionType)(Instance & ); + inline FunctionType operator= (FunctionType func); + inline void operator()( ); + } onFree; + inline void register_onFree(CPPClass & cl, Instance::Instance_onFree_Functor::FunctionType func); + + struct Instance_onGetDataFromString_Functor + { + int _[0]; + typedef bool (* FunctionType)(Instance & _ARG const char * string); + inline FunctionType operator= (FunctionType func); + inline bool operator()( const char * string); + } onGetDataFromString; + inline void register_onGetDataFromString(CPPClass & cl, Instance::Instance_onGetDataFromString_Functor::FunctionType func); + + struct Instance_onGetString_Functor + { + int _[0]; + typedef const char * (* FunctionType)(Instance & _ARG char * tempString _ARG void * reserved _ARG C(ObjectNotationType) * onType); + inline FunctionType operator= (FunctionType func); + inline const char * operator()( char * tempString _ARG void * reserved _ARG C(ObjectNotationType) * onType); + } onGetString; + inline void register_onGetString(CPPClass & cl, Instance::Instance_onGetString_Functor::FunctionType func); + + struct Instance_onSaveEdit_Functor + { + int _[0]; + typedef bool (* FunctionType)(Instance & _ARG Instance /*Window*/ & window _ARG void * object); + inline FunctionType operator= (FunctionType func); + inline bool operator()( Instance /*Window*/ & window _ARG void * object); + } onSaveEdit; + inline void register_onSaveEdit(CPPClass & cl, Instance::Instance_onSaveEdit_Functor::FunctionType func); + + struct Instance_onSerialize_Functor + { + int _[0]; + typedef void (* FunctionType)(Instance & _ARG Instance /*IOChannel*/ & channel); + inline FunctionType operator= (FunctionType func); + inline void operator()( Instance /*IOChannel*/ & channel); + } onSerialize; + inline void register_onSerialize(CPPClass & cl, Instance::Instance_onSerialize_Functor::FunctionType func); + + struct Instance_onUnserialize_Functor + { + int _[0]; + typedef void (* FunctionType)(Instance & _ARG Instance /*IOChannel*/ & channel); + inline FunctionType operator= (FunctionType func); + inline void operator()( Instance /*IOChannel*/ & channel); + } onUnserialize; + inline void register_onUnserialize(CPPClass & cl, Instance::Instance_onUnserialize_Functor::FunctionType func); + + static TCPPClass _class; + static C(bool) constructor(C(Instance) i, C(bool) alloc) { return (alloc && !_INSTANCE(i, _class.impl)) ? new MyApp(i, _class) != null : true; } \ + static void destructor(C(Instance) i) { MyApp * inst = (MyApp *)_INSTANCE(i, _class.impl); if(_class.destructor) ((void (*)(MyApp & self))_class.destructor)(*inst); delete inst; } \ + explicit inline MyApp(C(Instance) _impl, CPPClass & cl = _class) : Application(_impl, cl) + { + } + + static void class_registration(CPPClass & _class) + { + Application::class_registration(_class); + register_main(_class, + [](Application & app) + { + // eC_printLn(class_String, "C++: Hello, eC", null); + printLn("C++: Hello, eC"); + } + ); + Instance_evolve(&(app).impl, MyApp::_class.impl); + _INSTANCE((app).impl, (app).impl->_class) = &(app); + __thisModule = (app).impl; + (app).vTbl = _class.vTbl; + } +}; + +MyApp app; + +TCPPClass MyApp::_class( + (Class *)eC_registerClass(ClassType_normalClass, "MyApp", Application::_class.impl->name, sizeof(Instance *), 0, + (C(bool) (*)(void *)) MyApp::constructor, (void(*)(void *)) MyApp::destructor, (app).impl, + AccessMode_privateAccess, AccessMode_publicAccess) +); + +#if defined(__WIN32__) && !defined(__CONSOLE_APP__) +#else +extern "C" int main(int argc, char * argv[]) +{ + APP_SET_ARGS(app); + app.main(); + unloadTranslatedStrings(MODULE_NAME); + return app.exitCode; +} +#endif + +#endif // defined(USE_MACROS) diff --git a/samples/bindings/cpp/01_consoleAppRegisterEvolved/consoleAppRegisterEvolved.epj b/samples/bindings/cpp/01_consoleAppRegisterEvolved/consoleAppRegisterEvolved.epj new file mode 100644 index 0000000000..f93c355008 --- /dev/null +++ b/samples/bindings/cpp/01_consoleAppRegisterEvolved/consoleAppRegisterEvolved.epj @@ -0,0 +1,105 @@ +{ + "Version" : 0.2, + "ModuleName" : "consoleAppTest", + "Options" : { + "Warnings" : "All", + "PreprocessorDefinitions" : [ + "ECERECOM_ONLY" + ], + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS_C)", + "$(ECERE_SDK_BINDINGS_CPP)" + ], + "TargetType" : "Executable", + "TargetFileName" : "consoleAppTest", + "Libraries" : [ + "ecereCOM", + "stdc++" + ], + "Console" : true, + "PrebuildCommands" : [ + "@$(call echo,gcc $(shell @gcc -dumpversion $(nullerror)))" + ] + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS)/c", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/eC", + "Files" : [ + "eC.c", + "eC.h" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/ecere", + "Files" : [ + "ecere.c", + "ecere.h" + ], + "Options" : { + "ExcludeFromBuild" : true + } + } + ], + "Options" : { + "PreprocessorDefinitions" : [ + "ECPRFX=eC_" + ] + } + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS)/cpp", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/eC", + "Files" : [ + "eC.cpp", + "eC.hpp" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/ecere", + "Files" : [ + "ecere.cpp", + "ecere.hpp" + ], + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + } + ] + }, + { + "FileName" : "consoleAppRegisterEvolved.cpp", + "Options" : { + "PrebuildCommands" : [ + "make --version" + ] + } + } + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/cpp/02_consoleAppUnregistered/consoleAppUnregistered.cpp b/samples/bindings/cpp/02_consoleAppUnregistered/consoleAppUnregistered.cpp new file mode 100644 index 0000000000..44321945cb --- /dev/null +++ b/samples/bindings/cpp/02_consoleAppUnregistered/consoleAppUnregistered.cpp @@ -0,0 +1,20 @@ +#define MODULE_NAME "MyApp" +#define __LINK_ECERE__ 0 + +#include "eC.hpp" + +// Demonstrates 'main()' C++ style override (MyApp is not registered with eC COM) + +class MyApp : public Application +{ +public: + void main() + { + // eC_printLn(class_String, "C++: Hello, eC", null); + printLn("C++: Hello, eC"); + } +}; + +MyApp app; + +MAIN_DEFINITION diff --git a/samples/bindings/cpp/02_consoleAppUnregistered/consoleAppUnregistered.epj b/samples/bindings/cpp/02_consoleAppUnregistered/consoleAppUnregistered.epj new file mode 100644 index 0000000000..08d2c604f3 --- /dev/null +++ b/samples/bindings/cpp/02_consoleAppUnregistered/consoleAppUnregistered.epj @@ -0,0 +1,94 @@ +{ + "Version" : 0.2, + "ModuleName" : "consoleAppTest", + "Options" : { + "Warnings" : "All", + "PreprocessorDefinitions" : [ + "ECERECOM_ONLY" + ], + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS_C)", + "$(ECERE_SDK_BINDINGS_CPP)" + ], + "TargetType" : "Executable", + "TargetFileName" : "consoleAppTest", + "Libraries" : [ + "ecereCOM" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS)/c", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/eC", + "Files" : [ + "eC.c", + "eC.h" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/ecere", + "Files" : [ + "ecere.c", + "ecere.h" + ], + "Options" : { + "ExcludeFromBuild" : true + } + } + ], + "Options" : { + "PreprocessorDefinitions" : [ + "ECPRFX=eC_" + ] + } + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS)/cpp", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/eC", + "Files" : [ + "eC.cpp", + "eC.hpp" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/ecere", + "Files" : [ + "ecere.cpp", + "ecere.hpp" + ], + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + } + ] + }, + "consoleAppUnregistered.cpp" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/cpp/03_consoleAppConstructorEvolved/consoleAppConstructorEvolved.cpp b/samples/bindings/cpp/03_consoleAppConstructorEvolved/consoleAppConstructorEvolved.cpp new file mode 100644 index 0000000000..b348b3624c --- /dev/null +++ b/samples/bindings/cpp/03_consoleAppConstructorEvolved/consoleAppConstructorEvolved.cpp @@ -0,0 +1,63 @@ +#define MODULE_NAME "MyApp" +#define __LINK_ECERE__ 0 + +#include "eC.hpp" + +// Demonstrates overriding Application class, evolving instance from constructor + +#define USE_MACROS + +#if defined(USE_MACROS) + +class MyApp : public Application +{ +public: + +#if 1 // both work + void main() + { + eC_printLn(class_String, "C++: Hello, eC", null); + } +#endif + + APP_CONSTRUCT(MyApp, Application) + { + REGISTER_APP_CLASS(MyApp, Application, *this); + +#if 0 // both work + main = [](Application & app) + { + eC_printLn(class_String, "C++: Hello, eC", null); + }; +#endif + } +}; + +#else + +class MyApp : public Application +{ +public: + // expansion of + // APP_CONSTRUCT(MyApp, Application) + inline MyApp() : MyApp(eC_init_CALL(__LINK_ECERE__)) { } + INSTANCE_VIRTUAL_METHODS_PROTO(MyApp) + static TCPPClass _class; + static C(bool) constructor(C(Instance) i, C(bool) alloc) { return (alloc && !_INSTANCE(i, _class.impl)) ? new MyApp(i, _class) != null : true; } + static void destructor(C(Instance) i) { MyApp * inst = (MyApp *)_INSTANCE(i, _class.impl); if(_class.destructor) ((void (*)(MyApp & self))_class.destructor)(*inst); delete inst; } + explicit inline MyApp(C(Instance) _impl, CPPClass & cl = _class) : Application(_impl, cl) + { + REGISTER_APP_CLASS(MyApp, Application, *this); + + main = [](Application & app) + { + eC_printLn(class_String, "C++: Hello, eC", null); + }; + } +}; + +#endif // defined(USE_MACROS) + +MyApp app; +CLASS_DEF(MyApp); +MAIN_DEFINITION; diff --git a/samples/bindings/cpp/03_consoleAppConstructorEvolved/consoleAppConstructorEvolved.epj b/samples/bindings/cpp/03_consoleAppConstructorEvolved/consoleAppConstructorEvolved.epj new file mode 100644 index 0000000000..4001b7a102 --- /dev/null +++ b/samples/bindings/cpp/03_consoleAppConstructorEvolved/consoleAppConstructorEvolved.epj @@ -0,0 +1,94 @@ +{ + "Version" : 0.2, + "ModuleName" : "consoleAppTest", + "Options" : { + "Warnings" : "All", + "PreprocessorDefinitions" : [ + "ECERECOM_ONLY" + ], + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS_C)", + "$(ECERE_SDK_BINDINGS_CPP)" + ], + "TargetType" : "Executable", + "TargetFileName" : "consoleAppTest", + "Libraries" : [ + "ecereCOM" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS)/c", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/eC", + "Files" : [ + "eC.c", + "eC.h" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/ecere", + "Files" : [ + "ecere.c", + "ecere.h" + ], + "Options" : { + "ExcludeFromBuild" : true + } + } + ], + "Options" : { + "PreprocessorDefinitions" : [ + "ECPRFX=eC_" + ] + } + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS)/cpp", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/eC", + "Files" : [ + "eC.cpp", + "eC.hpp" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/ecere", + "Files" : [ + "ecere.cpp", + "ecere.hpp" + ], + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + } + ] + }, + "consoleAppConstructorEvolved.cpp" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/cpp/04_consoleGuiApp/consoleGuiApp.cpp b/samples/bindings/cpp/04_consoleGuiApp/consoleGuiApp.cpp new file mode 100644 index 0000000000..9ead9d6861 --- /dev/null +++ b/samples/bindings/cpp/04_consoleGuiApp/consoleGuiApp.cpp @@ -0,0 +1,63 @@ +#define MODULE_NAME "MyApp" + +#include "ecere.hpp" + +// Demonstrates overriding GuiApplication class, evolving instance from constructor +// Simpler alternative override of cycle() at instance level commented out +// Still using C++ style override of main() (Could also override just like cycle() in constructor or REGISTER()) + +class MyApp : public GuiApplication +{ +public: + APP_CONSTRUCT(MyApp, GuiApplication) + { + REGISTER_APP_CLASS(MyApp, GuiApplication, *this); + REG_GuiApplication(MyApp); + /*cycle = +[](GuiApplication & app, bool idle) + { + eC_printLn(class_String, " Cycling!", null); + return true; + };*/ + } + + REGISTER() + { + GuiApplication::class_registration(_cpp_class); + /* + register_cycle(_class, [](GuiApplication & app, bool idle) -> C(bool) + { + eC_printLn(class_String, " Cycling!", null); + return true; + }); + */ + } + + void main() + { + eC_printLn(class_String, "C++: Hello, eC", null); + GuiApplication::main(); + } + + bool init() + { + printLn("MyApp::init()"); + return true; + } + + bool cycle(bool idle) + { + printLn("MyApp::cycle()"); + return true; + } +}; + +MyApp app; +// Window w; +CLASS_DEF(MyApp); +MAIN_DEFINITION + + + + + + diff --git a/samples/bindings/cpp/04_consoleGuiApp/consoleGuiApp.epj b/samples/bindings/cpp/04_consoleGuiApp/consoleGuiApp.epj new file mode 100644 index 0000000000..7d4d1b8470 --- /dev/null +++ b/samples/bindings/cpp/04_consoleGuiApp/consoleGuiApp.epj @@ -0,0 +1,85 @@ +{ + "Version" : 0.2, + "ModuleName" : "consoleAppTest", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS_C)", + "$(ECERE_SDK_BINDINGS_CPP)" + ], + "TargetType" : "Executable", + "TargetFileName" : "consoleAppTest", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS)/c", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/eC", + "Files" : [ + "eC.c", + "eC.h" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/ecere", + "Files" : [ + "ecere.c", + "ecere.h" + ] + } + ], + "Options" : { + "PreprocessorDefinitions" : [ + "ECPRFX=eC_" + ] + } + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS)/cpp", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/eC", + "Files" : [ + "eC.cpp", + "eC.hpp" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/ecere", + "Files" : [ + "ecere.cpp", + "ecere.hpp" + ] + } + ] + } + ] + }, + "consoleGuiApp.cpp" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/cpp/05_guiWindowWithLabel/guiWindowWithLabel.cpp b/samples/bindings/cpp/05_guiWindowWithLabel/guiWindowWithLabel.cpp new file mode 100644 index 0000000000..8e5e392803 --- /dev/null +++ b/samples/bindings/cpp/05_guiWindowWithLabel/guiWindowWithLabel.cpp @@ -0,0 +1,112 @@ +#define MODULE_NAME "HelloForm" +// #define __CONSOLE_APP__ + +#include "ecere.hpp" + +#define USE_MACROS + +#if defined(USE_MACROS) + +class HelloForm : public Window +{ +public: + Label label; + + // REGISTER() { REG_Window(HelloForm); } + CONSTRUCT(HelloForm, Window) + { + caption = $("My First Ecere/C++ Bindings App"); + borderStyle = BorderStyle::sizable; + size = { 380, 190 }; + hasClose = true; + + label.parent = this; + label.caption = $("Hello, Bindings!!"); + label.position = { 10, 10 }; + label.font = { "Arial", 30 }; + + //char tmp[256]; + //String s = Instance_onGetString(0, font, tmp, null, null); + //PrintLn(class_String, s, null); // Need to terminate with a null! + } +}; + +GuiApplication app; + +REGISTER_CLASS_DEF(HelloForm, Window, app); + +HelloForm hello; + +MAIN_DEFINITION; + +#else + +class HelloForm : public Window +{ +public: + Label label; + + // expansion of + // CONSTRUCT(HelloForm, Window) + HelloForm() : HelloForm((C(Instance))Instance_newEx(_class.impl, false), _class) { } \ + INSTANCE_VIRTUAL_METHODS_PROTO(HelloForm) \ + static TCPPClass _class; \ + static C(bool) constructor(C(Instance) i, C(bool) alloc) { return (alloc && !_INSTANCE(i, _class.impl)) ? new HelloForm(i, _class) != null : true; } \ + static void destructor(C(Instance) i) { HelloForm * inst = (HelloForm *)_INSTANCE(i, _class.impl); if(_class.destructor) ((void (*)(HelloForm & self))_class.destructor)(*inst); delete inst; } \ + explicit inline HelloForm(C(Instance) _impl, CPPClass & cl = _class) : Window(_impl, cl) + { + caption = $("My First Ecere/C++ Bindings App"); + borderStyle = BorderStyle_sizable; + size = { 380, 190 }; + hasClose = true; + + label.parent = this; + label.caption = $("Hello, Bindings!!"); + label.position = { 10, 10 }; + label.font = { "Arial", 30 }; + +// thisfile.cpp:65:18: error: no match for 'operator=' in '((HelloForm*)this)->HelloForm::label.Label::.CommonControl::.Window::font = {"Arial", 30}' (operand types are 'Window::fontProp' and '') + + //char tmp[256]; + //String s = Instance_onGetString(0, font, tmp, null, null); + //PrintLn(class_String, s, null); // Need to terminate with a null! + } +}; + +GuiApplication app; // should this be calling eC_init_CALL()? + // we're missing the following provided by APP_CONSTRUCT(MyApp, Application) in previous samples + // inline MyApp() : MyApp(eC_init_CALL(__LINK_ECERE__)) { } + + +// expansion of +// REGISTER_CLASS_DEF(HelloForm, Window, app); +// issue here: REGISTER_CLASS_DEF uses _REGISTER_CLASS which makes use of Window::_class.impl->name before Window::_class.setup +// aka before ecere_cpp_init() is called +TCPPClass HelloForm::_class( + (Class *)eC_registerClass( + ClassType_normalClass, + "HelloForm", "Window", // issue work around Window::_class.impl->name, + sizeof(Instance *), 0, + (C(bool) (*)(void *)) HelloForm::constructor, + (void(*)(void *)) HelloForm::destructor, + (app).impl, // issue here app.impl is null, once again, ecere_cpp_init not called before? + AccessMode_privateAccess, AccessMode_publicAccess) +); + +HelloForm hello; + +// expansion of +// MAIN_DEFINITION; +extern "C" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, char * cmdLine, int show) +{ +#define APP_CONSTRUCT(c, b) \ + inline c() : c(eC_init_CALL(__LINK_ECERE__)) { } \ + _CONSTRUCT(c, b) + + APP_SET_ARGS(app); + app.main(); + unloadTranslatedStrings(MODULE_NAME); + return app.exitCode; +} + +#endif // defined(USE_MACROS) diff --git a/samples/bindings/cpp/05_guiWindowWithLabel/guiWindowWithLabel.epj b/samples/bindings/cpp/05_guiWindowWithLabel/guiWindowWithLabel.epj new file mode 100644 index 0000000000..286b12fccf --- /dev/null +++ b/samples/bindings/cpp/05_guiWindowWithLabel/guiWindowWithLabel.epj @@ -0,0 +1,85 @@ +{ + "Version" : 0.2, + "ModuleName" : "guiAppTest", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS_C)", + "$(ECERE_SDK_BINDINGS_CPP)" + ], + "TargetType" : "Executable", + "TargetFileName" : "guiAppTest", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS)/c", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/eC", + "Files" : [ + "eC.c", + "eC.h" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/ecere", + "Files" : [ + "ecere.c", + "ecere.h" + ] + } + ], + "Options" : { + "PreprocessorDefinitions" : [ + "ECPRFX=eC_" + ] + } + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS)/cpp", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/eC", + "Files" : [ + "eC.cpp", + "eC.hpp" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/ecere", + "Files" : [ + "ecere.cpp", + "ecere.hpp" + ] + } + ] + } + ] + }, + "guiWindowWithLabel.cpp" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/cpp/06_guiWindowRedrawWithButtonV1/guiWindowRedrawWithButton.cpp b/samples/bindings/cpp/06_guiWindowRedrawWithButtonV1/guiWindowRedrawWithButton.cpp new file mode 100644 index 0000000000..2bb42fa409 --- /dev/null +++ b/samples/bindings/cpp/06_guiWindowRedrawWithButtonV1/guiWindowRedrawWithButton.cpp @@ -0,0 +1,61 @@ +#define MODULE_NAME "HelloForm" + +#include "ecere.hpp" + +class HelloForm : public Window +{ +public: + Button button; + + REGISTER() + { + REG_Window(HelloForm); + DESTRUCT(HelloForm) = [](HelloForm & self) { printf("It's the end my friend!\n");/* system("pause");*/ }; + } + CONSTRUCT(HelloForm, Window) + { + caption = $("Sample App using Ecere Toolkit/C++ Bindings"); + borderStyle = BorderStyle::sizable; + clientSize = { 640, 480 }; + hasClose = true; + hasMaximize = true; + hasMinimize = true; + background = SystemColor::formColor; + font = { "Arial", 30 }; + + button.parent = this; + button.position = { 200, 200 }; + button.caption = $("Yay!!"); + button.notifyClicked = [](Window & owner, Button & btn, int x, int y, Modifiers mods) -> bool + { + HelloForm & self = (HelloForm &)owner; + MessageBox msgBox; + msgBox.caption = self.button.caption; + msgBox.contents = $("C++ Bindings!"); + msgBox.modal(); + return true; + }; + } + + void onRedraw(Surface & surface) + { + surface.writeText/*f*/(100, 100, "simple", 6); + } + + // REGISTER() + // { + // Window::class_registration(_class); + + // register_onRedraw(_class, [](Window & w, Surface & surface) { surface.writeText/*f*/(100, 100, "simple", 6); }); + + // DESTRUCT(HelloForm) = [](HelloForm & self) { printf("It's the end my friend!\n"); }; + // } +}; + +GuiApplication app; + +REGISTER_CLASS_DEF(HelloForm, Window, app); + +HelloForm hello; + +MAIN_DEFINITION; diff --git a/samples/bindings/cpp/06_guiWindowRedrawWithButtonV1/guiWindowRedrawWithButton.epj b/samples/bindings/cpp/06_guiWindowRedrawWithButtonV1/guiWindowRedrawWithButton.epj new file mode 100644 index 0000000000..6653809512 --- /dev/null +++ b/samples/bindings/cpp/06_guiWindowRedrawWithButtonV1/guiWindowRedrawWithButton.epj @@ -0,0 +1,85 @@ +{ + "Version" : 0.2, + "ModuleName" : "guiAppTest", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS_C)", + "$(ECERE_SDK_BINDINGS_CPP)" + ], + "TargetType" : "Executable", + "TargetFileName" : "guiAppTest", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS)/c", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/eC", + "Files" : [ + "eC.c", + "eC.h" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/ecere", + "Files" : [ + "ecere.c", + "ecere.h" + ] + } + ], + "Options" : { + "PreprocessorDefinitions" : [ + "ECPRFX=eC_" + ] + } + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS)/cpp", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/eC", + "Files" : [ + "eC.cpp", + "eC.hpp" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/ecere", + "Files" : [ + "ecere.cpp", + "ecere.hpp" + ] + } + ] + } + ] + }, + "guiWindowRedrawWithButton.cpp" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/cpp/07_guiWindowRedrawWithButtonV2/guiWindowRedrawWithButton.cpp b/samples/bindings/cpp/07_guiWindowRedrawWithButtonV2/guiWindowRedrawWithButton.cpp new file mode 100644 index 0000000000..c932236c9c --- /dev/null +++ b/samples/bindings/cpp/07_guiWindowRedrawWithButtonV2/guiWindowRedrawWithButton.cpp @@ -0,0 +1,61 @@ +#define MODULE_NAME "HelloForm" + +#include "ecere.hpp" + +class HelloForm : public Window +{ +public: + Button button; + + REGISTER() { REG_Window(HelloForm); } + CONSTRUCT(HelloForm, Window) + { + caption = $("Sample App using Ecere Toolkit/C++ Bindings"); + borderStyle = BorderStyle::sizable; + clientSize = { 640, 480 }; + hasClose = true; + hasMaximize = true; + hasMinimize = true; + background = SystemColor::formColor; + font = { "Arial", 30 }; + + button.parent = this; + button.position = { 200, 200 }; + button.caption = $("Yay!!"); + button.notifyClicked = [](Window & owner, Button & btn, int x, int y, Modifiers mods) -> bool + { + HelloForm & self = (HelloForm &)owner; + MessageBox msgBox; + msgBox.caption = self.button.caption; + msgBox.contents = $("C++ Bindings!"); + msgBox.modal(); + return true; + }; + } + + void onRedraw(Surface & surface) + { + surface.writeText/*f*/(100, 100, $("Class Method!!"), 14); + } + + /* + REGISTER() + { + register_onRedraw(_class, [](Window & w, Surface & surface) { ... }); + } + */ +}; + +GuiApplication app; + +CLASS_DEF(HelloForm); + +MAIN_DECLARATION +{ + REGISTER_CLASS(HelloForm, Window, app); + APP_SET_ARGS(app); + HelloForm hello; + app.main(); + unloadTranslatedStrings(MODULE_NAME); + return app.exitCode; +} diff --git a/samples/bindings/cpp/07_guiWindowRedrawWithButtonV2/guiWindowRedrawWithButton.epj b/samples/bindings/cpp/07_guiWindowRedrawWithButtonV2/guiWindowRedrawWithButton.epj new file mode 100644 index 0000000000..6653809512 --- /dev/null +++ b/samples/bindings/cpp/07_guiWindowRedrawWithButtonV2/guiWindowRedrawWithButton.epj @@ -0,0 +1,85 @@ +{ + "Version" : 0.2, + "ModuleName" : "guiAppTest", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS_C)", + "$(ECERE_SDK_BINDINGS_CPP)" + ], + "TargetType" : "Executable", + "TargetFileName" : "guiAppTest", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS)/c", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/eC", + "Files" : [ + "eC.c", + "eC.h" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/ecere", + "Files" : [ + "ecere.c", + "ecere.h" + ] + } + ], + "Options" : { + "PreprocessorDefinitions" : [ + "ECPRFX=eC_" + ] + } + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS)/cpp", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/eC", + "Files" : [ + "eC.cpp", + "eC.hpp" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/ecere", + "Files" : [ + "ecere.cpp", + "ecere.hpp" + ] + } + ] + } + ] + }, + "guiWindowRedrawWithButton.cpp" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/cpp/08_guiWindowRedrawWithButtonV3/guiWindowRedrawWithButton.cpp b/samples/bindings/cpp/08_guiWindowRedrawWithButtonV3/guiWindowRedrawWithButton.cpp new file mode 100644 index 0000000000..8fda91639e --- /dev/null +++ b/samples/bindings/cpp/08_guiWindowRedrawWithButtonV3/guiWindowRedrawWithButton.cpp @@ -0,0 +1,69 @@ +#define MODULE_NAME "HelloForm" + +#include "ecere.hpp" + +class HelloForm : public Window +{ +public: + Button button; + + REGISTER() { REG_Window(HelloForm); } + CONSTRUCT(HelloForm, Window) + { + caption = $("Sample App using Ecere Toolkit/C++ Bindings"); + borderStyle = BorderStyle::sizable; + clientSize = { 640, 480 }; + hasClose = true; + hasMaximize = true; + hasMinimize = true; + background = SystemColor::formColor; + font = { "Arial", 30 }; + + button.parent = this; + button.position = { 200, 200 }; + button.caption = $("Yay!!"); + button.notifyClicked = [](Window & owner, Button & btn, int x, int y, Modifiers mods) -> bool + { + HelloForm & self = (HelloForm &)owner; + MessageBox msgBox; + msgBox.caption = self.button.caption; + msgBox.contents = $("C++ Bindings!"); + msgBox.modal(); + return true; + }; + } + + void onRedraw(Surface & surface) + { + surface.writeText/*f*/(100, 100, $("Class Method!!"), 14); + } + + /* + REGISTER() + { + Window::class_registration(_class); + register_onRedraw(_class, [](Window & w, Surface & surface) { ... }); + } + */ +}; + +class MyApplication : public GuiApplication +{ +public: + APP_CONSTRUCT(MyApplication, GuiApplication) { } + + void main() + { + HelloForm hello; + + GuiApplication::main(); + } +}; + +MyApplication app; + +REGISTER_CLASS_DEF(MyApplication, GuiApplication, app); + +REGISTER_CLASS_DEF(HelloForm, Window, app); + +MAIN_DEFINITION; diff --git a/samples/bindings/cpp/08_guiWindowRedrawWithButtonV3/guiWindowRedrawWithButton.epj b/samples/bindings/cpp/08_guiWindowRedrawWithButtonV3/guiWindowRedrawWithButton.epj new file mode 100644 index 0000000000..6653809512 --- /dev/null +++ b/samples/bindings/cpp/08_guiWindowRedrawWithButtonV3/guiWindowRedrawWithButton.epj @@ -0,0 +1,85 @@ +{ + "Version" : 0.2, + "ModuleName" : "guiAppTest", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS_C)", + "$(ECERE_SDK_BINDINGS_CPP)" + ], + "TargetType" : "Executable", + "TargetFileName" : "guiAppTest", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS)/c", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/eC", + "Files" : [ + "eC.c", + "eC.h" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/ecere", + "Files" : [ + "ecere.c", + "ecere.h" + ] + } + ], + "Options" : { + "PreprocessorDefinitions" : [ + "ECPRFX=eC_" + ] + } + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS)/cpp", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/eC", + "Files" : [ + "eC.cpp", + "eC.hpp" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/ecere", + "Files" : [ + "ecere.cpp", + "ecere.hpp" + ] + } + ] + } + ] + }, + "guiWindowRedrawWithButton.cpp" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/cpp/09_guiWindowRedrawWithButtonV4/guiWindowRedrawWithButton.cpp b/samples/bindings/cpp/09_guiWindowRedrawWithButtonV4/guiWindowRedrawWithButton.cpp new file mode 100644 index 0000000000..a1a856cdd2 --- /dev/null +++ b/samples/bindings/cpp/09_guiWindowRedrawWithButtonV4/guiWindowRedrawWithButton.cpp @@ -0,0 +1,44 @@ +#define MODULE_NAME "HelloForm" + +#include "ecere.hpp" + +class HelloForm : public Window +{ +public: + Button button; + + CONSTRUCT(HelloForm, Window) + { + caption = $("Sample App using Ecere Toolkit/C++ Bindings"); + borderStyle = BorderStyle::sizable; + displayDriver = "OpenGL"; + clientSize = { 640, 480 }; + hasClose = true; + hasMaximize = true; + hasMinimize = true; + background = SystemColor::formColor; + font = { "Arial", 30 }; + + button.parent = this; + button.position = { 200, 200 }; + button.caption = $("Yay!!"); + button.notifyClicked = [](Window & owner, Button & btn, int x, int y, Modifiers mods) -> bool + { + HelloForm & self = (HelloForm &)owner; + MessageBox msgBox; + msgBox.caption = self.button.caption; + msgBox.contents = $("C++ Bindings!"); + msgBox.modal(); + return true; + }; + + onRedraw = [](Window & w, Surface & surface) { surface.writeText/*f*/(100, 100, $("Instance Method!"), 16); }; + } +}; +GuiApplication app; + +REGISTER_CLASS_DEF(HelloForm, Window, app); + +HelloForm hello; + +MAIN_DEFINITION; diff --git a/samples/bindings/cpp/09_guiWindowRedrawWithButtonV4/guiWindowRedrawWithButton.epj b/samples/bindings/cpp/09_guiWindowRedrawWithButtonV4/guiWindowRedrawWithButton.epj new file mode 100644 index 0000000000..6653809512 --- /dev/null +++ b/samples/bindings/cpp/09_guiWindowRedrawWithButtonV4/guiWindowRedrawWithButton.epj @@ -0,0 +1,85 @@ +{ + "Version" : 0.2, + "ModuleName" : "guiAppTest", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS_C)", + "$(ECERE_SDK_BINDINGS_CPP)" + ], + "TargetType" : "Executable", + "TargetFileName" : "guiAppTest", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS)/c", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/eC", + "Files" : [ + "eC.c", + "eC.h" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/ecere", + "Files" : [ + "ecere.c", + "ecere.h" + ] + } + ], + "Options" : { + "PreprocessorDefinitions" : [ + "ECPRFX=eC_" + ] + } + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS)/cpp", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/eC", + "Files" : [ + "eC.cpp", + "eC.hpp" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/ecere", + "Files" : [ + "ecere.cpp", + "ecere.hpp" + ] + } + ] + } + ] + }, + "guiWindowRedrawWithButton.cpp" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/cpp/10_guiWindowRedrawWithButtonV5/guiWindowRedrawWithButton.cpp b/samples/bindings/cpp/10_guiWindowRedrawWithButtonV5/guiWindowRedrawWithButton.cpp new file mode 100644 index 0000000000..824f269a60 --- /dev/null +++ b/samples/bindings/cpp/10_guiWindowRedrawWithButtonV5/guiWindowRedrawWithButton.cpp @@ -0,0 +1,54 @@ +#define MODULE_NAME "HelloForm" + +#include "ecere.hpp" + +class HelloForm : public Window +{ +public: + Button button; + + HelloForm() + { + caption = $("Sample App using Ecere Toolkit/C++ Bindings"); + borderStyle = BorderStyle::sizable; + clientSize = { 640, 480 }; + hasClose = true; + hasMaximize = true; + hasMinimize = true; + background = SystemColor::formColor; + font = { "Arial", 30 }; + + button.parent = this; + button.position = { 200, 200 }; + button.caption = $("Yay!!"); + button.notifyClicked = [](Window & owner, Button & btn, int x, int y, Modifiers mods) -> bool + { + HelloForm & self = (HelloForm &)owner; + MessageBox msgBox; + msgBox.caption = self.button.caption; + msgBox.contents = $("C++ Bindings!"); + msgBox.modal(); + return true; + }; + + onRedraw = [](Window & w, Surface & surface) { surface.writeText/*f*/(100, 100, $("Instance Method!"), 16); }; + } +}; + +extern "C" int +#if defined(__WIN32__) && !defined(__CONSOLE_APP__) + __stdcall WinMain(void * hInstance, void * hPrevInst, char * cmdLine, int show) +#else + main(int argc, char * argv[]) +#endif +{ + GuiApplication app; + HelloForm hello; + +#if !defined(__WIN32__) || defined(__CONSOLE_APP__) + eC_setArgs(app.impl, argc, argv); +#endif + app.main(); + unloadTranslatedStrings(MODULE_NAME); + return app.exitCode; +} diff --git a/samples/bindings/cpp/10_guiWindowRedrawWithButtonV5/guiWindowRedrawWithButton.epj b/samples/bindings/cpp/10_guiWindowRedrawWithButtonV5/guiWindowRedrawWithButton.epj new file mode 100644 index 0000000000..6653809512 --- /dev/null +++ b/samples/bindings/cpp/10_guiWindowRedrawWithButtonV5/guiWindowRedrawWithButton.epj @@ -0,0 +1,85 @@ +{ + "Version" : 0.2, + "ModuleName" : "guiAppTest", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS_C)", + "$(ECERE_SDK_BINDINGS_CPP)" + ], + "TargetType" : "Executable", + "TargetFileName" : "guiAppTest", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS)/c", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/eC", + "Files" : [ + "eC.c", + "eC.h" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/ecere", + "Files" : [ + "ecere.c", + "ecere.h" + ] + } + ], + "Options" : { + "PreprocessorDefinitions" : [ + "ECPRFX=eC_" + ] + } + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS)/cpp", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/eC", + "Files" : [ + "eC.cpp", + "eC.hpp" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/ecere", + "Files" : [ + "ecere.cpp", + "ecere.hpp" + ] + } + ] + } + ] + }, + "guiWindowRedrawWithButton.cpp" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/cpp/11_guiWindowWithCube/guiWindowWithCube.cpp b/samples/bindings/cpp/11_guiWindowWithCube/guiWindowWithCube.cpp new file mode 100644 index 0000000000..3b009c2998 --- /dev/null +++ b/samples/bindings/cpp/11_guiWindowWithCube/guiWindowWithCube.cpp @@ -0,0 +1,123 @@ +#define MODULE_NAME "HelloCube" + +#include "ecere.hpp" + +class HelloCube : public Window +{ +public: + Cube cube; + Light light; + Camera camera; + + REGISTER() { REG_Window(HelloCube); } + CONSTRUCT(HelloCube, Window) + { + caption = $("HelloCube -- Sample App using Ecere Toolkit/C++ Bindings"); + size = { 640, 480 }; + background = DefinedColor::black; +#if 1 + borderStyle = BorderStyle::sizable; + hasClose = true; + hasMaximize = true; + hasMinimize = true; +#else + moveable = true; + borderStyle = BorderStyle::none; + opacity = 0; + alphaBlend = true; + stayOnTop = true; +#endif + displayDriver = "OpenGL"; + + TArray b { 5.0, 3.2, 1.5 }; + TList a; + b = { 5.0, 3.2, 1.5 }; + + TList c { "bgen: ", "Hello", "C++" }; + printLn(c); + + a.add(3.0); + a.add(4.2); + a.add(9.5); + + printLn(a); + // t_args_x(a); + { + ZString z; + ZString s = "#"; + // broken: z.concat("#"); + z.concat(s); + // z.concatx(9); + // z.concatx("works"); + printLn(z.string); + } + { + ConsoleFile con; + con.printLn("#works"); + con.print(1, ":1, ", 2, ":2, ", 3, ":3, ", 'x', ":x, ", 4.3, "\n"); + } + + camera.position = { 0, 0, -300 }; + camera.fov = 53; + + light.orientation = Euler(30, 10); + light.diffuse = DefinedColor::lightCoral; + + if(sizeof(Point) == sizeof(C(Point))) + printLn("sizeof(Point) == sizeof(C(Point))"); + else + printLn("no"); + + } + + bool onLoadGraphics() + { + cube.create(displaySystem); + + Transform transform; + transform.scaling = { 100, 100, 100 }; + transform.orientation = Euler(50, 30, 50); + cube.transform = transform; + + printLn(transform); + cube.updateTransform(); + return true; + } + + void onUnloadGraphics() + { + cube.free(displaySystem); + } + + void onResize(int w, int h) + { + // printLn("onResize"); + camera.setup(w, h, null); + camera.update(); + } + + void onRedraw(Surface & surface) + { + // printLn("onRedraw"); + surface.clear(ClearType::depthBuffer); + display->setLight(0, light); + display->setCamera(surface, camera); + display->drawObject(cube); + display->setCamera(surface, Camera(null)); + } + + bool onKeyDown(Key key, unichar ch) + { + if(key == KeyCode::escape) + destroy(0); + return true; + } +}; + +GuiApplication app; + +REGISTER_CLASS_DEF(HelloCube, Window, app); + +HelloCube cube; + +MAIN_DEFINITION; diff --git a/samples/bindings/cpp/11_guiWindowWithCube/guiWindowWithCube.epj b/samples/bindings/cpp/11_guiWindowWithCube/guiWindowWithCube.epj new file mode 100644 index 0000000000..85c94b0f34 --- /dev/null +++ b/samples/bindings/cpp/11_guiWindowWithCube/guiWindowWithCube.epj @@ -0,0 +1,85 @@ +{ + "Version" : 0.2, + "ModuleName" : "guiAppTest", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS_C)", + "$(ECERE_SDK_BINDINGS_CPP)" + ], + "TargetType" : "Executable", + "TargetFileName" : "guiWindowWithCube", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + } + ], + "Files" : [ + { + "Folder" : "bindings", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS)/c", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/eC", + "Files" : [ + "eC.c", + "eC.h" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_C)/ecere", + "Files" : [ + "ecere.c", + "ecere.h" + ] + } + ], + "Options" : { + "PreprocessorDefinitions" : [ + "ECPRFX=eC_" + ] + } + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS)/cpp", + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/eC", + "Files" : [ + "eC.cpp", + "eC.hpp" + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS_CPP)/ecere", + "Files" : [ + "ecere.cpp", + "ecere.hpp" + ] + } + ] + } + ] + }, + "guiWindowWithCube.cpp" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/cpp/foo.ec b/samples/bindings/cpp/foo.ec new file mode 100644 index 0000000000..c98693d86d --- /dev/null +++ b/samples/bindings/cpp/foo.ec @@ -0,0 +1,2 @@ +class HelloForm3; +HelloForm3 form2 { }; diff --git a/samples/bindings/cpp/form.cpp b/samples/bindings/cpp/form.cpp new file mode 100644 index 0000000000..71501cd6cb --- /dev/null +++ b/samples/bindings/cpp/form.cpp @@ -0,0 +1,183 @@ +#define MODULE_NAME "HelloForm" + +// #define __CONSOLE_APP__ + +#include "ecere.hpp" + +GuiApplication app; + +// MAIN_DEFINITION; + +class Foo : public Instance +{ +public: + int a, b; + Foo(int a, int b) : Foo() { this->a = a, this->b = b; }; + + CONSTRUCT(Foo, Instance) + { + onCopy = [](Foo & self, Foo & other) + { + printf("%d, %d\n", other.a, other.b); + printf("self: %p\n", &self); + self.a = other.a; + self.b = other.b; + }; + + onCompare = [](Foo & self, Foo & other) + { + if(self.a > other.a) return 1; + if(self.a < other.a) return -1; + if(self.b > other.b) return 1; + if(self.b < other.b) return -1; + return 0; + }; + + /*onDisplay = [](Foo & self, Surface & s, int x , int y , int w, void * fd, Alignment a, DataDisplayFlags f) + { + printf("Meh\n"); + }; + */ + } + + REGISTER() + { + //Instance::class_registration(_class); + // Foo&, Surface&, int, int, int, void*, Alignment, DataDisplayFlags + // Foo::Foo_onDisplay_Functor::FunctionType {aka void (*)( + // Foo&, Instance&, int, int, int, void*, eC_Alignment, unsigned int + // Foo&, Surface&, int, int, int, void*, Alignment, DataDisplayFlags + // Foo&, Surface&, int, int, int, void*, Alignment, DataDisplayFlags + register_onDisplay(_class, + [](Foo & _self, Instance & s, int x , int y , int w, void * fd, Alignment a, DataDisplayFlags f) + { + printf("Meh\n"); + }); + } +}; +REGISTER_CLASS_DEF(Foo, Instance, app); + +void testStuff(); + +class HelloForm2 : public Window +{ +public: + Button button; + + CONSTRUCT(HelloForm2, Window) + { + caption = $("My Second Ecere/C++ Bindings App"); + borderStyle = BorderStyle_sizable; + clientSize = { 640, 480 }; + hasClose = true; + hasMaximize = true; + hasMinimize = true; + background = SystemColor_formColor; + font = { "Arial", 30 }; + + button.parent = this; + button.position = { 200, 200 }; + button.caption = $("Yay!!"); + // button.onRedraw = [](Window & w, Surface s){ }; + button.notifyClicked = [](Window & owner, Button & btn, int x, int y, Modifiers mods) -> C(bool) + { + double i = 3.14159265; + char tmp[256]; + __attribute__((unused)) constString s = _onGetString(class_double, &i, tmp, null, null); + //if(s); // silence warning + //PrintLn(class_String, "Hello! -- ", class_String, s, null); // Need to terminate with a null! + //HelloForm2 & self = (HelloForm2 &)owner; + //MessageBox($("C++ Bindings!"), self.button.caption).modal(); + + /*FontResource a("Arial", 20, true); + FontResource b("Comic Sans MS", 20, true); + FontResource c("Arial", 20, true); + + int ab = ((FontResource *)null)->onCompare(b); + //bool ab = ((FontResource *)null)->onCompare(*(FontResource *)null); + //bool ac = a.onCompare(c); + int ac = a.onCompare(*(FontResource *)null); + printf("a compare b = %d\n", ab); + printf("a compare c = %d\n", ac);*/ + + { + Foo obj1 { 2, 3 }; + Foo obj2 { 2, 4 }; + Foo obj3 { 2, 3 }; + /*Foo obj4; obj4.onCopy(obj1); + FontResource fr; + fr.onCopy(a); + printf("result: face = %s, size = %f, bold = %d\n", + (constString)fr.faceName, (float)fr.size, (int)fr.bold); + printf("after"); + */ + Surface s { }; + Foo * o = (Foo *)null; o->onDisplay(s, 0,0,0, null, (Alignment)0, (DataDisplayFlags)0); + //((Foo *)&obj3)->onDisplay(Surface { }, 0,0,0, null, 0, 0); + + int r; + r = obj1.onCompare(obj2); printf("result: %d\n", r); + //r = obj2.onCompare(obj1); printf("result: %d\n", r); + //r = obj1.onCompare(obj3); printf("result: %d\n", r); + //printf("result: a = %d, b = %d\n", obj4.a, obj4.b); + + testStuff(); + } + return true; + }; + + /*onCreate = [](Window & w) + { + MessageBox($("Cool"), $("Creation!")).modal(); + return true; + };*/ + + //onRedraw(Surface()); + //onCreate(); + } + + /*static void myOnRedraw(Window & w, Surface & surface) + { + surface.writeTextf(100, 100, $("Testing stuff!")); + //surface.writeTextf(100, 100, "%d + %d = %d", 2, 3, 2+3); + };*/ + + REGISTER() + { + Window::class_registration(_class); + register_onRedraw(_class, [](Window & w, Surface & surface) + { + surface.writeText/*f*/(100, 100, $("Class Method!!"), 14); + //surface.writeTextf(100, 100, "%d + %d = %d", 2, 3, 2+3); + }); + } +}; +REGISTER_CLASS_DEF(HelloForm2, Window, app); + +void testStuff() +{ + //eC_Class * c = eC_findClass(app.impl, "HelloForm2"); + eC_Class * c = HelloForm2::_class.impl; + ((HelloForm2 *)_INSTANCE(newi(c), c))->modal(); +} + +class HelloForm3 : public HelloForm2 +{ +public: + CONSTRUCT(HelloForm3, HelloForm2) + { + background = 0x50B0F0; + position = { 0, 0 }; + font = { "Monaco", 30, true }; + + onRedraw = [](Window & w, Surface & surface) + { + surface.writeText/*f*/(100, 100, $("Instance Method!"), 16); + //surface.writeTextf(100, 100, "%d + %d = %d", 2, 3, 2+3); + }; + } +}; +REGISTER_CLASS_DEF(HelloForm3, HelloForm2, app); + +//HelloForm2 hello2; +//HelloForm3 hello3; diff --git a/samples/bindings/cpp/helloApp.cpp b/samples/bindings/cpp/helloApp.cpp new file mode 100644 index 0000000000..f4864f4a00 --- /dev/null +++ b/samples/bindings/cpp/helloApp.cpp @@ -0,0 +1,32 @@ +#define MODULE_NAME "MyApp" + +#include "eC.hpp" +#include "ecere.hpp" + +// Demonstrates overriding Application class, evolving instance from REGISTER() + +class MyApp; +extern MyApp app; + +class MyApp : public Application +{ +public: + APP_CONSTRUCT(MyApp, Application) { } + REGISTER() + { + Application::class_registration(_class); + register_main(_class, + [](Application & app) + { + eC_printLn(class_String, "C++: Hello, eC", null); + } + ); + EVOLVE_APP(MyApp, app); + } +}; + +MyApp app; + +REGISTER_CLASS_DEF(MyApp, Application, app); + +MAIN_DEFINITION; diff --git a/samples/bindings/cpp/helloApp2.cpp b/samples/bindings/cpp/helloApp2.cpp new file mode 100644 index 0000000000..87073d9d13 --- /dev/null +++ b/samples/bindings/cpp/helloApp2.cpp @@ -0,0 +1,18 @@ +#define MODULE_NAME "MyApp" + +#include "eC.hpp" + +// Demonstrates 'main()' C++ style override (MyApp is not registered with eC COM) + +class MyApp : public Application +{ +public: + void main() + { + eC_printLn(class_String, "C++: Hello, eC", null); + } +}; + +MyApp app; + +MAIN_DEFINITION diff --git a/samples/bindings/cpp/helloApp3.cpp b/samples/bindings/cpp/helloApp3.cpp new file mode 100644 index 0000000000..c297f691ba --- /dev/null +++ b/samples/bindings/cpp/helloApp3.cpp @@ -0,0 +1,42 @@ +#define MODULE_NAME "MyApp" + +#include "ecere.hpp" + +// Demonstrates overriding GuiApplication class, evolving instance from constructor +// Simpler alternative override of cycle() at instance level commented out +// Still using C++ style override of main() (Could also override just like cycle() in constructor or REGISTER()) + +class MyApp : public GuiApplication +{ +public: + APP_CONSTRUCT(MyApp, GuiApplication) + { + REGISTER_APP_CLASS(MyApp, GuiApplication, *this); + /*cycle = +[](GuiApplication & app, bool idle) + { + PrintLn(class_String, " Cycling!", null); + return true; + };*/ + } + + REGISTER() + { + GuiApplication::class_registration(_class); + register_cycle(_class, [](GuiApplication & app, bool idle) + { + eC_printLn(class_String, " Cycling!", null); + return true; + }); + } + + void main() + { + eC_printLn(class_String, "C++: Hello, eC", null); + GuiApplication::main(); + } +}; + +MyApp app; +Window w; +CLASS_DEF(MyApp); +MAIN_DEFINITION diff --git a/samples/bindings/cpp/helloApp4.cpp b/samples/bindings/cpp/helloApp4.cpp new file mode 100644 index 0000000000..3775e5ddd2 --- /dev/null +++ b/samples/bindings/cpp/helloApp4.cpp @@ -0,0 +1,23 @@ +#define MODULE_NAME "MyApp" + +#include "eC.hpp" + +// Demonstrates overriding Application class, evolving instance from constructor + +class MyApp : public Application +{ +public: + APP_CONSTRUCT(MyApp, Application) + { + REGISTER_APP_CLASS(MyApp, Application, *this); + + main = [](Application & app) + { + eC_printLn(class_String, "C++: Hello, eC", null); + }; + } +}; + +MyApp app; +CLASS_DEF(MyApp); +MAIN_DEFINITION; diff --git a/samples/bindings/cpp/helloGUI.cpp b/samples/bindings/cpp/helloGUI.cpp new file mode 100644 index 0000000000..8ff14ef7cc --- /dev/null +++ b/samples/bindings/cpp/helloGUI.cpp @@ -0,0 +1,35 @@ +#define MODULE_NAME "HelloForm" +// #define __CONSOLE_APP__ + +#include "ecere.hpp" + +class HelloForm : public Window +{ +public: + Label label; + + CONSTRUCT(HelloForm, Window) + { + caption = $("My First Ecere/C++ Bindings App"); + borderStyle = BorderStyle_sizable; + size = { 380, 190 }; + hasClose = true; + + label.parent = this; + label.caption = $("Hello, Bindings!!"); + label.position = { 10, 10 }; + label.font = { "Arial", 30 }; + + //char tmp[256]; + //String s = Instance_onGetString(0, font, tmp, null, null); + //PrintLn(class_String, s, null); // Need to terminate with a null! + } +}; + +GuiApplication app; + +REGISTER_CLASS_DEF(HelloForm, Window, app); + +HelloForm hello; + +MAIN_DEFINITION; diff --git a/samples/bindings/cpp/sample1.cpp b/samples/bindings/cpp/sample1.cpp new file mode 100644 index 0000000000..2d911fa139 --- /dev/null +++ b/samples/bindings/cpp/sample1.cpp @@ -0,0 +1,51 @@ +#define MODULE_NAME "HelloForm" + +#include "ecere.hpp" + +class HelloForm : public Window +{ +public: + Button button; + + CONSTRUCT(HelloForm, Window) + { + caption = $("Sample App using Ecere Toolkit/C++ Bindings"); + borderStyle = BorderStyle_sizable; + clientSize = { 640, 480 }; + hasClose = true; + hasMaximize = true; + hasMinimize = true; + background = SystemColor_formColor; + font = { "Arial", 30 }; + + button.parent = this; + button.position = { 200, 200 }; + button.caption = $("Yay!!"); + button.notifyClicked = [](Window & owner, Button & btn, int x, int y, Modifiers mods) + { + HelloForm & self = (HelloForm &)owner; + MessageBox msgBox; + msgBox.caption = self.button.caption; + msgBox.contents = $("C++ Bindings!"); + msgBox.modal(); + return true; + }; + } + + REGISTER() + { + Window::class_registration(_class); + + register_onRedraw(_class, [](Window & w, Surface & surface) { surface.writeTextf(100, 100, $("Class Method!")); }); + + DESTRUCT(HelloForm) = [](HelloForm & self) { printf("It's the end my friend!\n"); }; + } +}; + +GuiApplication app; + +REGISTER_CLASS_DEF(HelloForm, Window, app); + +HelloForm hello; + +MAIN_DEFINITION; diff --git a/samples/bindings/cpp/sample2.cpp b/samples/bindings/cpp/sample2.cpp new file mode 100644 index 0000000000..a85eb4ac4b --- /dev/null +++ b/samples/bindings/cpp/sample2.cpp @@ -0,0 +1,53 @@ +#define MODULE_NAME "HelloForm" + +#include "ecere.hpp" + +class HelloForm : public Window +{ +public: + Button button; + + CONSTRUCT(HelloForm, Window) + { + caption = $("Sample App using Ecere Toolkit/C++ Bindings"); + borderStyle = sizable; + clientSize = { 640, 480 }; + hasClose = true; + hasMaximize = true; + hasMinimize = true; + background = formColor; + font = { "Arial", 30 }; + + button.parent = this; + button.position = { 200, 200 }; + button.caption = $("Yay!!"); + button.notifyClicked = [](Window & owner, Button & btn, int x, int y, Modifiers mods) + { + HelloForm & self = (HelloForm &)owner; + MessageBox msgBox; + msgBox.caption = self.button.caption; + msgBox.contents = $("C++ Bindings!"); + msgBox.modal(); + return true; + }; + } + + REGISTER() + { + register_onRedraw(_class, [](Window & w, Surface & surface) { surface.writeTextf(100, 100, $("Class Method!")); }); + } +}; + +GuiApplication app; + +CLASS_DEF(HelloForm); + +MAIN_DECLARATION +{ + REGISTER_CLASS(HelloForm, Window, app); + APP_SET_ARGS(app); + HelloForm hello; + app.main(); + unloadTranslatedStrings(MODULE_NAME); + return app.exitCode; +} diff --git a/samples/bindings/cpp/sample3.cpp b/samples/bindings/cpp/sample3.cpp new file mode 100644 index 0000000000..3624a98d70 --- /dev/null +++ b/samples/bindings/cpp/sample3.cpp @@ -0,0 +1,61 @@ +#define MODULE_NAME "HelloForm" + +#include "ecere.hpp" + +class HelloForm : public Window +{ +public: + Button button; + + CONSTRUCT(HelloForm, Window) + { + caption = $("Sample App using Ecere Toolkit/C++ Bindings"); + borderStyle = BorderStyle_sizable; + clientSize = { 640, 480 }; + hasClose = true; + hasMaximize = true; + hasMinimize = true; + background = SystemColor_formColor; + font = { "Arial", 30 }; + + button.parent = this; + button.position = { 200, 200 }; + button.caption = $("Yay!!"); + button.notifyClicked = [](Window & owner, Button & btn, int x, int y, Modifiers mods) + { + HelloForm & self = (HelloForm &)owner; + MessageBox msgBox; + msgBox.caption = self.button.caption; + msgBox.contents = $("C++ Bindings!"); + msgBox.modal(); + return true; + }; + } + + REGISTER() + { + Window::class_registration(_class); + register_onRedraw(_class, [](Window & w, Surface & surface) { surface.writeTextf(100, 100, $("Class Method!")); }); + } +}; + +class MyApplication : public GuiApplication +{ +public: + APP_CONSTRUCT(MyApplication, GuiApplication) { } + + void main() + { + HelloForm hello; + + GuiApplication::main(); + } +}; + +MyApplication app; + +REGISTER_CLASS_DEF(MyApplication, GuiApplication, app); + +REGISTER_CLASS_DEF(HelloForm, Window, app); + +MAIN_DEFINITION; diff --git a/samples/bindings/cpp/sample4.cpp b/samples/bindings/cpp/sample4.cpp new file mode 100644 index 0000000000..559e113908 --- /dev/null +++ b/samples/bindings/cpp/sample4.cpp @@ -0,0 +1,44 @@ +#define MODULE_NAME "HelloForm" + +#include "ecere.hpp" + +class HelloForm : public Window +{ +public: + Button button; + + CONSTRUCT(HelloForm, Window) + { + caption = $("Sample App using Ecere Toolkit/C++ Bindings"); + borderStyle = BorderStyle_sizable; + displayDriver = "OpenGL"; + clientSize = { 640, 480 }; + hasClose = true; + hasMaximize = true; + hasMinimize = true; + background = SystemColor_formColor; + font = { "Arial", 30 }; + + button.parent = this; + button.position = { 200, 200 }; + button.caption = $("Yay!!"); + button.notifyClicked = [](Window & owner, Button & btn, int x, int y, Modifiers mods) + { + HelloForm & self = (HelloForm &)owner; + MessageBox msgBox; + msgBox.caption = self.button.caption; + msgBox.contents = $("C++ Bindings!"); + msgBox.modal(); + return true; + }; + + onRedraw = [](Window & w, Surface & surface) { surface.writeTextf(100, 100, $("Instance Method!")); }; + } +}; +GuiApplication app; + +REGISTER_CLASS_DEF(HelloForm, Window, app); + +HelloForm hello; + +MAIN_DEFINITION; diff --git a/samples/bindings/cpp/sample5.cpp b/samples/bindings/cpp/sample5.cpp new file mode 100644 index 0000000000..03083a7e8d --- /dev/null +++ b/samples/bindings/cpp/sample5.cpp @@ -0,0 +1,54 @@ +#define MODULE_NAME "HelloForm" + +#include "ecere.hpp" + +class HelloForm : public Window +{ +public: + Button button; + + HelloForm() + { + caption = $("Sample App using Ecere Toolkit/C++ Bindings"); + borderStyle = BorderStyle_sizable; + clientSize = { 640, 480 }; + hasClose = true; + hasMaximize = true; + hasMinimize = true; + background = SystemColor_formColor; + font = { "Arial", 30 }; + + button.parent = this; + button.position = { 200, 200 }; + button.caption = $("Yay!!"); + button.notifyClicked = [](Window & owner, Button & btn, int x, int y, Modifiers mods) + { + HelloForm & self = (HelloForm &)owner; + MessageBox msgBox; + msgBox.caption = self.button.caption; + msgBox.contents = $("C++ Bindings!"); + msgBox.modal(); + return true; + }; + + onRedraw = [](Window & w, Surface & surface) { surface.writeTextf(100, 100, $("Instance Method!")); }; + } +}; + +extern "C" int +#if defined(__WIN32__) && !defined(__CONSOLE_APP__) + __stdcall WinMain(void * hInstance, void * hPrevInst, char * cmdLine, int show) +#else + main(int argc, char * argv[]) +#endif +{ + GuiApplication app; + HelloForm hello; + +#if !defined(__WIN32__) || defined(__CONSOLE_APP__) + eC_setArgs(app.impl, argc, argv); +#endif + app.main(); + unloadTranslatedStrings(MODULE_NAME); + return app.exitCode; +} diff --git a/samples/bindings/cpp/test.epj b/samples/bindings/cpp/test.epj new file mode 100644 index 0000000000..a1f20f9a00 --- /dev/null +++ b/samples/bindings/cpp/test.epj @@ -0,0 +1,88 @@ +{ + "Version" : 0.2, + "ModuleName" : "test", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "$(ECERE_SDK_BINDINGS_C)", + "$(ECERE_SDK_BINDINGS_CPP)" + ], + "TargetType" : "Executable", + "TargetFileName" : "test", + "Libraries" : [ + "ecere" + ] + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "Console" : true, + "FastMath" : false + } + }, + { + "Name" : "Release", + "Options" : { + "Debug" : false, + "Optimization" : "Speed", + "FastMath" : true + } + } + ], + "Files" : [ + { + "Folder" : "$(ECERE_SDK_BINDINGS)/c", + "Files" : [ + "$(ECERE_SDK_BINDINGS_C)/eC.c", + { + "FileName" : "$(ECERE_SDK_BINDINGS_C)/eC.h", + "Options" : { + "ExcludeFromBuild" : true + } + }, + "$(ECERE_SDK_BINDINGS_C)/ecere.c", + { + "FileName" : "$(ECERE_SDK_BINDINGS_C)/ecere.h", + "Options" : { + "ExcludeFromBuild" : true + } + } + ] + }, + { + "Folder" : "$(ECERE_SDK_BINDINGS)/cpp", + "Files" : [ + "$(ECERE_SDK_BINDINGS_CPP)/eC.cpp", + { + "FileName" : "$(ECERE_SDK_BINDINGS_CPP)/eC.hpp", + "Options" : { + "ExcludeFromBuild" : true + } + }, + { + "FileName" : "$(ECERE_SDK_BINDINGS_CPP)/ecere.cpp", + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "ExcludeFromBuild" : false + } + } + ] + }, + "$(ECERE_SDK_BINDINGS_CPP)/ecere.hpp" + ] + }, + "form.cpp" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/bindings/py/1.try.py b/samples/bindings/py/1.try.py new file mode 100644 index 0000000000..677bae2912 --- /dev/null +++ b/samples/bindings/py/1.try.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +"""1.try.py ecere sample""" +#from eC import * +#from ecere import * +from ecere import GuiApplication, printLn +# driver="OpenGL" +app = GuiApplication(appGlobals=globals()) +printLn("Testing Variadic Functions! ;)\n", 1, " + ", 2, " = ", 1+2) +printLn("Pi = ", 3.141592653589) diff --git a/samples/bindings/py/2.sample.py b/samples/bindings/py/2.sample.py new file mode 100644 index 0000000000..78c800df3a --- /dev/null +++ b/samples/bindings/py/2.sample.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +"""2.sample.py ecere sample""" +from ecere import * +# driver = "OpenGL" +app = GuiApplication(appGlobals=globals()) +printLn("Testing Variadic Functions!\n", 1, " + ", 2, " = ", 1+2) +printLn("Pi = ", 3.141592653589) + +a = Array("") +a.add(3) +a.add(4) +a.add(5) +printLn(a) + +#b = Array("", [6, 7, 8]) +#printLn(b) + +#c = Array("", [3.4, 5.6, 1.4142135]) +#printLn(c) + +class MyThing: + def something(self): + pass + +class MyController(WindowController): +# def __init__(self): +# WindowController.__init__(self, "") + def init_args(self, args, kwArgs): init_args(MyController, self, args, kwArgs) + def __init__(self, *args, **kwArgs): + self.init_args(list(args), kwArgs) + +@regclass +class MyForm(Window): + def __init__(self): + Window.__init__(self, + displayDriver="OpenGL", + caption=I18N("Hello, Python!!"), + hasClose=True, + hasMaximize=True, + hasMinimize=True, + controller=MyController(), + borderStyle=BorderStyle.sizable, + clientSize=Size(640, 480), + background=ColorAlpha(a=255, color=Color(b=255)), + foreground=ColorAlpha(a=255, color=Color(r=235, b=115, g=200)), + font=FontResource("Merriweather", 30, outlineSize=4.0, outlineFade=0.2)) + printLn("end of MyForm(Window) __init__") + + def myOnRedraw(self, surface): + surface.writeTextf(20, 20, I18N("Writing Stuff on the wall!!")) + self.onDrawOverChildren = myOnRedraw + + def button1Clicked(self, button, x, y, mods): + printLn("I got pushed! (master is ", self.caption, ")") + self.background = ColorAlpha(a=255, color=Color(b=255, g=192, r=64)) + MessageBox(caption=I18N("Hello, Python!"), + contents=I18N("Python is pretty nifty.")).modal() + self.button2.notifyClicked(self, button, x, y, mods) + return True + + self.picture1 = Picture(parent=self, anchor=Anchor(0, 0, 0, 0), + image=BitmapResource("picture.jpg")) + + self.button1 = Button( + parent=self, + caption=I18N("Push It!"), + position=Point(80, 80), + font=FontResource("Merriweather", 30), + notifyClicked=button1Clicked) + + self.button2 = Button( + parent=self, + caption="Another button", + position=Point(280, 280), + font=FontResource("Merriweather", 20), + size=Size(160, 40)) + +MyForm() + +Window( + caption = "Bindings are cool, 中文 too!", + hasClose = True, + clientSize = Size(320, 200), + background = ColorAlpha(a = 255, color = Color(255))) + +printLn("app.main()") +app.main() diff --git a/samples/bindings/py/3.sample.py b/samples/bindings/py/3.sample.py new file mode 100644 index 0000000000..2f54d8fc5a --- /dev/null +++ b/samples/bindings/py/3.sample.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +"""2.sample.py ecere sample""" +from ecere import * +# driver = "OpenGL" +app = GuiApplication(appGlobals=globals()) +printLn("Testing Variadic Functions!\n", 1, " + ", 2, " = ", 1+2) +printLn("Pi = ", 3.141592653589) + +a = Array("") +a.add(3) +a.add(4) +a.add(5) +printLn(a) + +#b = Array("", [6, 7, 8]) +#printLn(b) + +#c = Array("", [3.4, 5.6, 1.4142135]) +#printLn(c) + +class MyThing: + def something(self): + pass + +class MyController(WindowController): +# def __init__(self): +# WindowController.__init__(self, "") + def init_args(self, args, kwArgs): init_args(MyController, self, args, kwArgs) + def __init__(self, *args, **kwArgs): + self.init_args(list(args), kwArgs) + +@regclass +class MyForm(Window): + def __init__(self): + Window.__init__(self, + displayDriver="OpenGL", + caption=I18N("Hello, Python!!"), + hasClose=True, + hasMaximize=True, + hasMinimize=True, + controller=MyController(), + borderStyle=BorderStyle.sizable, + clientSize=Size(640, 480), + background=ColorAlpha(a=255, color=Color(b=255)), + foreground=ColorAlpha(a=255, color=Color(r=235, b=115, g=200)), + font=FontResource("Merriweather", 30, outlineSize=4.0, outlineFade=0.2)) + printLn("end of MyForm(Window) __init__") + + def timerExpired(self): + self.t.stop() + x, y = self.getMousePosition() + printLn("timer: ", "(", x, ", ", y, ")") + return True + + self.t = Timer(self, delay=1.0, started=True, delayExpired=timerExpired) + +# bool DelayExpired() +# self.Update(null) +# return True + + + def myOnRedraw(self, surface): + surface.writeTextf(20, 20, I18N("Writing Stuff on the wall!!")) + self.onDrawOverChildren = myOnRedraw + + def button1Clicked(self, button, x, y, mods): + printLn("I got pushed! (master is ", self.caption, ")") + self.background = ColorAlpha(a=255, color=Color(b=255, g=192, r=64)) + MessageBox(caption=I18N("Hello, Python!"), + contents=I18N("Python is pretty nifty.")).modal() + self.button2.notifyClicked(self, button, x, y, mods) + return True + + self.picture1 = Picture(parent=self, anchor=Anchor(0, 0, 0, 0), + image=BitmapResource("picture.jpg")) + + self.button1 = Button( + parent=self, + caption=I18N("Push It!"), + position=Point(80, 80), + font=FontResource("Merriweather", 30), + notifyClicked=button1Clicked) + + self.button2 = Button( + parent=self, + caption="Another button", + position=Point(280, 280), + font=FontResource("Merriweather", 20), + size=Size(160, 40)) + +MyForm() + +def myMouseMove(self, x, y, mods): + printLn("(", x, ", ", y, ")") +#self.onMouseMove = myMouseMove + +Window( + caption = "Bindings are cool, 中文 too!", + hasClose = True, + clientSize = Size(320, 200), + background = ColorAlpha(a = 255, color = Color(255)))#, + #onMouseMove = myMouseMove) + +printLn("app.main()") +app.main() diff --git a/samples/bindings/py/hello3D.py b/samples/bindings/py/hello3D.py new file mode 100644 index 0000000000..389556a2a8 --- /dev/null +++ b/samples/bindings/py/hello3D.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from ecere import * + +app = GuiApplication(appGlobals=globals()) + +light = Light(diffuse=DefinedColor.lightCoral, + orientation=Euler(pitch=10, yaw=30)) + +camera = Camera(type=CameraType.fixed, + position=Vector3D(0, 0, -300), + orientation=Euler(0, 0, 0), + fov=53) # Field of View + +@regclass +class Hello3D(Window): + + def __init__(self): + Window.__init__(self, + displayDriver="OpenGL", + caption=I18N("Hello 3D"), + hasClose=True, + hasMaximize=True, + hasMinimize=True, + borderStyle=BorderStyle.sizable, + clientSize=Size(640, 480), + background=ColorAlpha(255, DefinedColor.black), + foreground=ColorAlpha(255, DefinedColor.black)) + + self.cube = Cube() + + def h3d_onLoadGraphics(self): + self.cube.create(self.displaySystem) + # NOTE: Assigning to .cube.transform.scaling does not work + # (not sure what Python mechanism could be used to support this nicely) + self.cube.transform = Transform(scaling=(100, 100, 100), + orientation=Euler(50, 30, 50)) + self.cube.updateTransform() + return True + self.onLoadGraphics = h3d_onLoadGraphics + + def h3d_onResize(self, width, height): + camera.setup(width, height, None) + camera.update() + self.onResize = h3d_onResize + + def h3d_onRedraw(self, surface): + surface.clear(ClearType.depthBuffer) + surface.display.setLight(0, light) + surface.display.setCamera(surface, camera) + surface.display.drawObject(self.cube) + surface.display.setCamera(surface, None) + self.onRedraw = h3d_onRedraw + +Hello3D() + +app.main() diff --git a/samples/games/TilesRPG/Nilrem.png b/samples/games/TilesRPG/Nilrem.png new file mode 100644 index 0000000000..9d0f91a96b Binary files /dev/null and b/samples/games/TilesRPG/Nilrem.png differ diff --git a/samples/games/TilesRPG/TilesRPG.epj b/samples/games/TilesRPG/TilesRPG.epj new file mode 100644 index 0000000000..2f6f4619e1 --- /dev/null +++ b/samples/games/TilesRPG/TilesRPG.epj @@ -0,0 +1,49 @@ +{ + "Version" : 0.2, + "ModuleName" : "GameingTime", + "Options" : { + "Warnings" : "All", + "TargetType" : "Executable", + "TargetFileName" : "TilesRPG", + "Libraries" : [ + "ecere" + ] + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + }, + { + "Name" : "Release", + "Options" : { + "Debug" : false, + "Optimization" : "Speed", + "FastMath" : true + } + } + ], + "Files" : [ + { + "Folder" : "tiles", + "Files" : [ + "$(ECERE_SDK_SRC)/extras/tiles/astar.ec", + "$(ECERE_SDK_SRC)/extras/tiles/sequence.ec", + "$(ECERE_SDK_SRC)/extras/tiles/sprite.ec", + "$(ECERE_SDK_SRC)/extras/tiles/tiles.ec" + ] + }, + "tilesRPG.ec" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/games/TilesRPG/bat.png b/samples/games/TilesRPG/bat.png new file mode 100644 index 0000000000..e964aca756 Binary files /dev/null and b/samples/games/TilesRPG/bat.png differ diff --git a/samples/games/TilesRPG/ch-Dagger.png b/samples/games/TilesRPG/ch-Dagger.png new file mode 100644 index 0000000000..0f4e213ee5 Binary files /dev/null and b/samples/games/TilesRPG/ch-Dagger.png differ diff --git a/samples/games/TilesRPG/ch-HeavyShield.png b/samples/games/TilesRPG/ch-HeavyShield.png new file mode 100644 index 0000000000..d96ee55845 Binary files /dev/null and b/samples/games/TilesRPG/ch-HeavyShield.png differ diff --git a/samples/games/TilesRPG/ch-Helmet.png b/samples/games/TilesRPG/ch-Helmet.png new file mode 100644 index 0000000000..b8a1b889f4 Binary files /dev/null and b/samples/games/TilesRPG/ch-Helmet.png differ diff --git a/samples/games/TilesRPG/ch-Leggings.png b/samples/games/TilesRPG/ch-Leggings.png new file mode 100644 index 0000000000..d3c52596a4 Binary files /dev/null and b/samples/games/TilesRPG/ch-Leggings.png differ diff --git a/samples/games/TilesRPG/ch-LightArmor.png b/samples/games/TilesRPG/ch-LightArmor.png new file mode 100644 index 0000000000..f026654eeb Binary files /dev/null and b/samples/games/TilesRPG/ch-LightArmor.png differ diff --git a/samples/games/TilesRPG/ch-LightShield.png b/samples/games/TilesRPG/ch-LightShield.png new file mode 100644 index 0000000000..02d0185082 Binary files /dev/null and b/samples/games/TilesRPG/ch-LightShield.png differ diff --git a/samples/games/TilesRPG/ch-LongSword.png b/samples/games/TilesRPG/ch-LongSword.png new file mode 100644 index 0000000000..ac6ffd8ba5 Binary files /dev/null and b/samples/games/TilesRPG/ch-LongSword.png differ diff --git a/samples/games/TilesRPG/ch-PlateArmor.png b/samples/games/TilesRPG/ch-PlateArmor.png new file mode 100644 index 0000000000..5ff50293fc Binary files /dev/null and b/samples/games/TilesRPG/ch-PlateArmor.png differ diff --git a/samples/games/TilesRPG/ch-SteelBoots.png b/samples/games/TilesRPG/ch-SteelBoots.png new file mode 100644 index 0000000000..1e8d4937ce Binary files /dev/null and b/samples/games/TilesRPG/ch-SteelBoots.png differ diff --git a/samples/games/TilesRPG/ch-battleSword.png b/samples/games/TilesRPG/ch-battleSword.png new file mode 100644 index 0000000000..e4f0bded69 Binary files /dev/null and b/samples/games/TilesRPG/ch-battleSword.png differ diff --git a/samples/games/TilesRPG/character.png b/samples/games/TilesRPG/character.png new file mode 100644 index 0000000000..e159b9deb4 Binary files /dev/null and b/samples/games/TilesRPG/character.png differ diff --git a/samples/games/TilesRPG/giantRat.png b/samples/games/TilesRPG/giantRat.png new file mode 100644 index 0000000000..d01970b235 Binary files /dev/null and b/samples/games/TilesRPG/giantRat.png differ diff --git a/samples/games/TilesRPG/giantSpider.png b/samples/games/TilesRPG/giantSpider.png new file mode 100644 index 0000000000..1c85c613d7 Binary files /dev/null and b/samples/games/TilesRPG/giantSpider.png differ diff --git a/samples/games/TilesRPG/goblin.png b/samples/games/TilesRPG/goblin.png new file mode 100644 index 0000000000..a7233e0b27 Binary files /dev/null and b/samples/games/TilesRPG/goblin.png differ diff --git a/samples/games/TilesRPG/hut.png b/samples/games/TilesRPG/hut.png new file mode 100644 index 0000000000..552e2f642a Binary files /dev/null and b/samples/games/TilesRPG/hut.png differ diff --git a/samples/games/TilesRPG/items.png b/samples/games/TilesRPG/items.png new file mode 100644 index 0000000000..48b702352d Binary files /dev/null and b/samples/games/TilesRPG/items.png differ diff --git a/samples/games/TilesRPG/mapTiles.png b/samples/games/TilesRPG/mapTiles.png new file mode 100644 index 0000000000..82943c895d Binary files /dev/null and b/samples/games/TilesRPG/mapTiles.png differ diff --git a/samples/games/TilesRPG/shop-inside.png b/samples/games/TilesRPG/shop-inside.png new file mode 100644 index 0000000000..87d477ca90 Binary files /dev/null and b/samples/games/TilesRPG/shop-inside.png differ diff --git a/samples/games/TilesRPG/shop.png b/samples/games/TilesRPG/shop.png new file mode 100644 index 0000000000..20d2b19410 Binary files /dev/null and b/samples/games/TilesRPG/shop.png differ diff --git a/samples/games/TilesRPG/slug.png b/samples/games/TilesRPG/slug.png new file mode 100644 index 0000000000..67590b3e19 Binary files /dev/null and b/samples/games/TilesRPG/slug.png differ diff --git a/samples/games/TilesRPG/tilesRPG.ec b/samples/games/TilesRPG/tilesRPG.ec new file mode 100644 index 0000000000..7ce409a423 --- /dev/null +++ b/samples/games/TilesRPG/tilesRPG.ec @@ -0,0 +1,779 @@ +import "ecere" +import "tiles" + +// define NILREM_START = Point { 28, 20 }; + +define NILREM_START = Point { 108, 20 }; + +TileMap * theMap; +int vx, vy; +bool inTheShop; + +define MAPW = 32; +define MAPH = 20; + +define SHOP_POS = Point { 30, 20 }; + +enum ItemType +{ + none = -1, + lightShield = 0, + dagger, + longSword, + battleSword, + heavyShield, + helmet, + steelBoots, + lightArmor, + plateArmor, + plateLeggings +}; + +enum EquipmentSlot +{ + head, + feet, + body, + legs, + leftHand, + rightHand, + ring, + ring2, + ring3, + ring4 +}; + +struct ItemStats +{ + const String name; + int value; + EquipmentSlot slot; + bool twoHands; + int decDamage; + int difficulty; + int damage; +}; + +ItemStats itemStats[ItemType] = +{ + { "Light shield", 20, leftHand, false, 2 }, + { "Dagger", 10, rightHand, false, 0, 2, 2 }, + { "Long sword", 40, rightHand, false, 0, 4, 4 }, + { "Battle sword", 100,rightHand, true, 0, 8, 10 }, + { "Heavy shield", 100, leftHand, false, 5 }, + { "Helmet", 60, head, false, 5 }, + { "Steel boots", 40, feet, false, 2 }, + { "Light armor", 40, body, false, 4 }, + { "Plate armor", 150, body, false, 8 }, + { "Plate leggings", 50, legs, false, 3 } +}; + +class Spell +{ +public: + int difficulty; + int damage; + int manaCost; + + virtual void Backfire(Creature self, Creature opponent) + { + self.health -= damage/4; + } + + virtual void Success(Creature self, Creature opponent) + { + if(damage > opponent.health) damage = opponent.health; + PrintLn(self._class.name, " did ", damage, " damage to ", opponent._class.name, "."); + opponent.health -= damage; + } +} + + +class FireBall : Spell { difficulty = 20, damage = 8; manaCost = 5; }; +class Lightning : Spell { difficulty = 10, damage = 4; manaCost = 3; }; +class Healing : Spell +{ + difficulty = 20; + manaCost = 5; + void Success(Creature self, Creature opponent) + { + self.health += self.maxHealth / 5; + if(self.health > self.maxHealth) self.health = self.maxHealth; + } + + void Backfire(Creature self, Creature opponent) + { + self.health -= damage/4; + } +}; + +class Creature +{ +public: + UnitType type; + int xp; + int health, maxHealth; + int mana, maxMana; + int dexterity; + int magic; + int strength; + int gold; + Array spells; + ItemType equipment[EquipmentSlot]; + + TileUnit * unit; +} + +class Player : Creature +{ + type = player; +public: + int manaPotions; + int healthPotions; + int training; + spells = { [ FireBall { }, Lightning { }, Healing { } ] }; + Player() + { + EquipmentSlot i; + for(i = 0; i <= EquipmentSlot::ring4; i++) + equipment[i] = none; + } +} + +class Slug : Creature { type = slug; xp = 10; maxHealth = 10; dexterity = 7; strength = 5; gold = 1; } +class GiantRat : Creature { type = giantRat; xp = 30; maxHealth = 20; dexterity = 9; strength = 8; gold = 3; } +class GiantSpider : Creature { type = giantSpider; xp = 50; maxHealth = 30; dexterity = 20; strength = 10; gold = 4; } +class Bat : Creature { type = bat; xp = 70; maxHealth = 10; dexterity = 40; strength = 5; gold = 6; } +class Goblin : Creature +{ + type = goblin; xp = 120; maxHealth = 50; dexterity = 50; strength = 25; gold = 10; +} +class Ghoul : Creature { type = ghoul; xp = 250; maxHealth = 70; dexterity = 20; strength = 30; gold = 30; } +class DarkKnight : Creature +{ + type = darkKnight; xp = 500; maxHealth = 100; dexterity = 50; strength = 50; gold = 50; magic = 30; maxMana = 50; + spells = { [ FireBall { } ] }; + + DarkKnight() + { + equipment[head] = helmet; + equipment[feet] = steelBoots; + equipment[body] = plateArmor; + equipment[legs] = plateLeggings; + equipment[leftHand] = heavyShield; + equipment[rightHand] = longSword; + } +}; + +Array badGuys { [ + class(Slug), class(GiantRat), class(GiantSpider), class(Goblin), class(Bat), class(DarkKnight), class(Ghoul) +]}; + +class EvilSorcerer : Creature +{ + type = evilSorcerer; xp = 1000; maxHealth = 1000; dexterity = 75; strength = 50; gold = 20000; magic = 50; maxMana = 500; + spells = { [ FireBall { }, Lightning { }, Healing { } ] }; + + EvilSorcerer() + { + equipment[head] = helmet; + equipment[feet] = steelBoots; + equipment[body] = plateArmor; + equipment[legs] = plateLeggings; + equipment[leftHand] = heavyShield; + equipment[rightHand] = longSword; + } +} + +Player player +{ + xp = 0, maxHealth = 40, health = 40, mana = 20, + maxMana = 20, magic = 20, strength = 10, dexterity = 10, gold = 1000, + training = 2 +}; + +enum UnitType +{ + none, + + // Creatures + player, + slug, + giantRat, + giantSpider, + goblin, + bat, + darkKnight, + ghoul, + evilSorcerer, + + // Buildings + hut, + shop +}; + + +const String unitSpritesFiles[UnitType] = +{ + "", + + // Creatures + "Nilrem.png", + "slug.png", + "giantRat.png", + "giantSpider.png", + "goblin.png", + "bat.png", + "darkKnight.png", + "ghoul.png", + "evilSorcerer.png", + + // Buildings + "hut.png", + "shop.png" +}; + +const Point unitSpritesSize[UnitType] = +{ + { 0, 0 }, + + // Creatures + { 1, 1 }, + { 1, 1 }, + { 1, 1 }, + { 1, 1 }, + { 1, 1 }, + { 1, 1 }, + { 1, 1 }, + { 1, 1 }, + + // Buildings + { 2, 2 }, + { 3, 3 } +}; + +Sprite unitSprites[UnitType]; + +struct UnitPlacement +{ + UnitType type; + Point pos; +}; + +Array unitPlacements +{ [ + { shop, SHOP_POS }, + { hut, { 10, 10 } }, + { hut, { 3, 15 } }, + { hut, { 5, 7 } }, + { hut, { 8, 19 } }, + { hut, { 18, 18 } }, + { hut, { 17, 17 } } //, + //{ giantRat, { 120, 22 } } +] }; + +Array creatures { }; + +define SHOP_ITEMS_POS = Point { 24, 240 }; +define SHOP_ITEMS_SPACING = Point { 48, 48 }; + +class StatsUI : Window +{ + background = black; + + void OnRedraw(Surface surface) + { + surface.background = red; + surface.Area(10, 10, 10 + player.health * 260 / player.maxHealth, 20); + surface.foreground = white; + surface.Rectangle(10, 10, 270, 20); + surface.WriteTextf(10, 24, "Health: %d / %d", + player.health, player.maxHealth); + + surface.background = blue; + surface.Area(10, 50, 10 + player.mana * 260 / player.maxMana, 60); + surface.foreground = white; + surface.Rectangle(10, 50, 270, 60); + surface.WriteTextf(10, 64, "Mana: %d / %d", + player.mana, player.maxMana); + + surface.foreground = gold; + surface.WriteTextf(10, 90, "Gold coins: %d", player.gold); + } +} + +bool shopInventory[ItemType] = +{ + true, true, true, true, true, + true, true, true, true, true +}; + +class ShopUI : Window +{ + ItemType selectedItem; + selectedItem = none; + + background = black; + Picture backPic + { + this, + image = { "shop-inside.png" }; + anchor = { top = 0 }; + clickThrough = true; + }; + Button btnBuy + { + backPic, + master = this, + disabled = true; + position = { 220, 470 }; + caption = "Buy item"; + + bool NotifyClicked(Button button, int x, int y, Modifiers mods) + { + ItemStats eq = itemStats[selectedItem]; + EquipmentSlot slot = eq.slot; + int tradeIn; + ItemType tradeIn1 = none, tradeIn2 = none; + if(slot == ring) + while(player.equipment[slot] != none && slot < ring4) + slot++; + + if(slot == rightHand && eq.twoHands) + { + if(player.equipment[EquipmentSlot::leftHand] != none) + tradeIn1 = player.equipment[EquipmentSlot::leftHand]; + if(player.equipment[EquipmentSlot::rightHand] != none) + tradeIn2 = player.equipment[EquipmentSlot::rightHand]; + } + else if((slot == leftHand || slot == rightHand) && player.equipment[EquipmentSlot::rightHand] != none && + itemStats[player.equipment[EquipmentSlot::rightHand]].twoHands) + { + if(player.equipment[EquipmentSlot::rightHand] != none) + tradeIn1 = player.equipment[EquipmentSlot::rightHand]; + } + else if(player.equipment[slot] != none) + tradeIn1 = player.equipment[slot]; + + tradeIn = ((tradeIn1 != none ? itemStats[tradeIn1].value : 0) + (tradeIn2 != none ? itemStats[tradeIn2].value : 0)) / 2; + if(player.gold + tradeIn < eq.value) + MessageBox { contents = + "Y'aint got enough money! Watcha gonna do now huh? Run away like a coward? Come back when you have what you promised us!" + }.Modal(); + else + { + char message[1024] = ""; + if(tradeIn1 != none && tradeIn2 != none) + strcatf(message, + "You will need to trade in your %s and %s, for %d .", + itemStats[tradeIn1].name, itemStats[tradeIn2].name, tradeIn); + else if(tradeIn1 != none) + strcatf(message, + "You will need to trade in your %s, for %d. ", itemStats[tradeIn1].name, tradeIn); + + strcatf(message, "Are you sure you want to buy this %s for %d?", eq.name, eq.value); + if(MessageBox { type = yesNo, contents = message }.Modal() == yes) + { + if(tradeIn1 != none) shopInventory[tradeIn1] = true; + if(tradeIn2 != none) shopInventory[tradeIn2] = true; + + if(eq.twoHands || (tradeIn1 != none && itemStats[tradeIn1].twoHands)) + { + player.equipment[leftHand] = none; + player.equipment[rightHand] = none; + } + + player.equipment[slot] = selectedItem; + player.gold += tradeIn - eq.value; + + shopInventory[selectedItem] = false; + selectedItem = none; + } + } + return true; + } + }; + + bool OnLeftButtonDown(int x, int y, Modifiers mods) + { + if(x >= SHOP_ITEMS_POS.x && x < SHOP_ITEMS_POS.x + 5 * SHOP_ITEMS_SPACING.x && + y >= SHOP_ITEMS_POS.y && y < SHOP_ITEMS_POS.y + 2 * SHOP_ITEMS_SPACING.y) + { + int ix = (x - SHOP_ITEMS_POS.x) / SHOP_ITEMS_SPACING.x; + int iy = (y - SHOP_ITEMS_POS.y) / SHOP_ITEMS_SPACING.y; + selectedItem = (ItemType)(iy * 5 + ix); + if(!shopInventory[selectedItem]) + selectedItem = none; + btnBuy.disabled = selectedItem == none; + Update(null); + } + return true; + } + + void OnDrawOverChildren(Surface surface) + { + ItemType i; + for(i = 0; i < 10; i++) + { + if(shopInventory[i]) + { + surface.Blit(shopItemsBitmap, + SHOP_ITEMS_POS.x + (i%5) * SHOP_ITEMS_SPACING.x, + SHOP_ITEMS_POS.y + (i/5) * SHOP_ITEMS_SPACING.y, + 0, i * 32, 32, 32); + } + } + if(selectedItem != -1) + { + int ix = selectedItem % 5; + int iy = selectedItem / 5; + ItemStats * stats = &itemStats[selectedItem]; + + surface.foreground = lime; + surface.Rectangle( + SHOP_ITEMS_POS.x + ix * SHOP_ITEMS_SPACING.x - 8, + SHOP_ITEMS_POS.y + iy * SHOP_ITEMS_SPACING.y - 8, + SHOP_ITEMS_POS.x + (ix+1) * SHOP_ITEMS_SPACING.x - 8, + SHOP_ITEMS_POS.y + (iy+1) * SHOP_ITEMS_SPACING.y - 8); + + surface.foreground = white; + surface.CenterTextf(140, 360, stats->name); + surface.foreground = gold; + surface.CenterTextf(140, 400, "%d gold coins", stats->value); + surface.foreground = whiteSmoke; + surface.WriteTextf(80, 420, "Damage: %d", + stats->damage ? stats->damage : -stats->decDamage); + if(stats->difficulty) + surface.WriteTextf(80, 440, "Difficulty: %d", stats->difficulty); + } + } +} + +Bitmap shopItemsBitmap { }; + +class App : GuiApplication +{ + driver = "OpenGL"; + timerResolution = 60; + bool Cycle(bool idle) + { + player.unit->tick++; + UnitUpdate(theMap, player.unit); + mainWindow.Update(null); + + if(vx < 1024 - MAPW && player.unit->pos.x > vx + MAPW*3/4) + vx++; + else if(vx > 0 && player.unit->pos.x < vx + MAPW/4) + vx--; + + if(vy < 1024 - MAPH && player.unit->pos.y > vy + MAPH*3/4) + vy++; + else if(vy > 0 && player.unit->pos.y < vy + MAPH/4) + vy--; + + if(theMap->frames[player.unit->pos.y * theMap->dim[1].x + player.unit->pos.x] == 1) + { + if(GetRandom(1, 100) > 95) + { + // Create the character unit + int rx = GetRandom(0, 2)-1, ry = GetRandom(0, 2)-1; + int px = player.unit->pos.x + rx; + int py = player.unit->pos.y + ry; + if((rx || ry) && px >= 0 && py >= 0 && px < theMap->dim[1].x && py < theMap->dim[1].y && + !theMap->spaces[1][theMap->dim[0].x * py + px] && + theMap->frames[py * theMap->dim[1].x + px] == 1) + { + TileUnit * unit = UnitCreate(theMap, 1, px, py, null); + Creature creature = Goblin { unit = unit }; + unit->direction = rx == -1 ? (ry == 1 ? NorthEast : ry == 0 ? East : SouthEast) : + rx == 0 ? (ry == 1 ? North : South) : + (ry == 1 ? NorthWest : ry == 0 ? West : SouthWest); + unit->w = 1; + unit->h = 1; + unit->sprite = unitSprites[creature.type]; + UnitPlace(theMap, unit); + creatures.Add(creature); + } + } + } + + if(player.unit->pos.x >= SHOP_POS.x && player.unit->pos.x <= SHOP_POS.x + 2 && + player.unit->pos.y >= SHOP_POS.y && player.unit->pos.y <= SHOP_POS.y + 2) + { + if(!inTheShop) + { + mainWindow.shopUI.visible = true; + inTheShop = true; + } + } + else + { + mainWindow.shopUI.visible = false; + inTheShop = false; + } + return true; + } +} + +class MainMap : Window +{ + void OnRedraw(Surface surface) + { + int w = clientSize.w; + int h = clientSize.h; + + surface.background = skyBlue; + + surface.Clear(colorBuffer); + MapRedraw(theMap, surface, vx, vy, MAPW, MAPH); + + //surface.background = green; + + //surface.Area(100, 100, 200, 200); + + //surface.WriteTextf(10, 10, "Welcome to TilesRPG!"); + } + + bool OnRightButtonDown(int x, int y, Modifiers mods) + { + UnitMove(theMap, player.unit, { vx + x / 32, vy + y / 32 }); + return true; + } +} + +static const String chInventoryItems[] = +{ + "ch-LightShield.png", + "ch-Dagger.png", + "ch-LongSword.png", + "ch-battleSword.png", + "ch-HeavyShield.png", + "ch-Helmet.png", + "ch-SteelBoots.png", + "ch-LightArmor.png", + "ch-PlateArmor.png", + "ch-Leggings.png" +}; + +class CharacterUI : Window +{ + ItemType selectedItem; + selectedItem = none; + Bitmap bmpItems[10]; + + background = black; + Picture backPic + { + this, + image = { "character.png" }; + anchor = { top = 0 }; + clickThrough = true; + }; + + bool OnLoadGraphics() + { + ItemType i; + for(i = ItemType::lightShield; i <= ItemType::plateLeggings; i++) + { + bmpItems[i] = { alphaBlend = true }; + bmpItems[i].Load(chInventoryItems[i], null, displaySystem); + } + return true; + } + + void OnDrawOverChildren(Surface surface) + { + EquipmentSlot i; + for(i = 0; i <= EquipmentSlot::ring4; i++) + { + ItemType type = player.equipment[i]; + if(type != none) + { + Bitmap bmp = bmpItems[type]; + if(bmp) + surface.Blit(bmp, 0, 0, 0, 0, bmp.width, bmp.height); + } + } + } +} + +class TilesRPGWindow : Window +{ + caption = $"TilesRPG"; + background = formColor; + borderStyle = sizable; + hasMaximize = true; + hasMinimize = true; + hasClose = true; + // clientSize = { 892, 323 }; + clientSize = { 1370, 700 }; + //position = { }; + //state = maximized; //displayDriver = "OpenGL"; + + MainMap mainMap + { + this, + clientSize = { MAPW * 32, MAPH * 32 }; + position = { 24, 24 }; + borderStyle = bevel; + }; + + CharacterUI characterUI + { + this, + clientSize = { 280, 500 }; + anchor = { top = 0, right = 5 }; + }; + + ShopUI shopUI + { + this, + visible = false; + caption = "The Shop"; + clientSize = { 280, 500 }; + anchor = { top = 0, right = 5 }; + }; + + StatsUI stasUI + { + this, + clientSize = { 280 }; + anchor = { top = 500, right = 5, bottom = 0 }; + }; + + TilesRPGWindow() + { + File f = FileOpen("map1.map", read); + if(f) + { + int y = 0; + char mapLine[1030]; + int i; + + theMap = MapCreate(3, 1024, 1024, 32, 32, 4); + for(i = 0; i < 3; i++) + { + theMap->dim[0] = { 1024, 1024 }; + theMap->spaces[i] = new0 TileUnit * [1024 * 1024]; + theMap->moves[i] = new0 byte[1024 * 1024]; + } + theMap->dim[1] = { 1024, 1024 }; + theMap->dim[2] = { 1024, 1024 }; + while(true) + { + if(f.GetLine(mapLine, sizeof(mapLine)) && y < theMap->maxDim.y) + { + int x; + + for(x = 0; x < theMap->maxDim.x; x++) + { + char ch = mapLine[x]; + if(ch >= '0' && ch <= '9') + { + theMap->frames[y * theMap->maxDim.x + x ] = + ch - '0'; + } + else + break; + } + y++; + } + else + break; + } + delete f; + + for(y = 0; y < 1024; y++) + { + int x; + + for(x = 0; x < 1024; x++) + { + uint16 frame = theMap->frames[y * 1024 + x]; + theMap->moves[1][y * 1024 + x] = frame < 2; + } + } + } + + // Initialize the creatures and buildings sprites + UnitType u; + for(u = player; u <= shop; u++) + unitSprites[u] = { }; + + // Create the character unit + player.unit = UnitCreate(theMap, 1, NILREM_START.x, NILREM_START.y, null); + player.unit->direction = South; + player.unit->w = 1; + player.unit->h = 1; + player.unit->sprite = unitSprites[player]; + UnitPlace(theMap, player.unit); + + // Place the buildings units + for(u : unitPlacements) + { + UnitPlacement p = u; + UnitType t = p.type; + Point pos = p.pos; + TileUnit * unit = UnitCreate(theMap, 0, pos.x, pos.y, null); + if(t != shop) + unit->covering[1] = 1; + unit->direction = North; + unit->w = unitSpritesSize[t].x; + unit->h = unitSpritesSize[t].y; + unit->sprite = unitSprites[t]; + UnitPlace(theMap, unit); + } + } + + bool OnLoadGraphics() + { + bool result = false; + int i; + Bitmap mapTiles { }; + if(mapTiles.Load("mapTiles.png", null, null)) + { + mapTiles.Convert(null, pixelFormat888, null); + + for(i = 0; i < theMap->numTiles; i++) + { + theMap->tileImage[i] = { }; + theMap->tileImage[i].Allocate(null, theMap->tileW, theMap->tileH, 0, pixelFormat888, false); + theMap->tileImage[i].Grab(mapTiles, 0, i * theMap->tileH); + theMap->tileImage[i].MakeDD(displaySystem); + } + result = true; + } + delete mapTiles; + + // Load the creatures and buildings sprites + UnitType u; + for(u = player; u <= shop; u++) + unitSprites[u].Load(unitSpritesFiles[u], null, true, false, null, displaySystem); + + shopItemsBitmap.LoadT("items.png", null, displaySystem); + return result; + } + + bool OnKeyHit(Key key, unichar ch) + { + switch(key) + { + case right: + player.unit->direction = East; + break; + case left: + player.unit->direction = West; + break; + case up: + player.unit->direction = North; + break; + case down: + player.unit->direction = South; + break; + } + Update(null); + return true; + } +} + +TilesRPGWindow mainWindow {}; diff --git a/samples/games/chess/android/AndroidManifest.xml b/samples/games/chess/android/AndroidManifest.xml index 95da55282c..f4d3aa12d3 100644 --- a/samples/games/chess/android/AndroidManifest.xml +++ b/samples/games/chess/android/AndroidManifest.xml @@ -8,12 +8,17 @@ + + - - + - @@ -26,5 +31,5 @@ - + diff --git a/samples/games/chess/android/Chess.java b/samples/games/chess/android/Chess.java new file mode 100644 index 0000000000..cfab3e951b --- /dev/null +++ b/samples/games/chess/android/Chess.java @@ -0,0 +1,10 @@ +package com.ecere.Chess; + +public class Chess extends android.app.NativeActivity +{ + static + { + System.loadLibrary("ecere"); + System.loadLibrary("Chess"); + } +} diff --git a/samples/games/chess/chess.epj b/samples/games/chess/chess.epj index 50304a6cc1..b834a9964a 100644 --- a/samples/games/chess/chess.epj +++ b/samples/games/chess/chess.epj @@ -41,15 +41,17 @@ "TargetFileName" : "Chess", "FastMath" : true, "PostbuildCommands" : [ - "$(call mkdir,$(OBJ)apk/lib/x86)", - "$(call mkdir,$(OBJ)apk/lib/armeabi)", - "$(call cp,../../../ecere/obj/android.linux.$(COMPILER)/libecere.so,$(OBJ)apk/lib/armeabi)", - "$(call cp,$(TARGET),$(OBJ)apk/lib/armeabi)", - "aapt package -v -f -m -M android/AndroidManifest.xml -F $(OBJ)$(MODULE)-unsigned.apk -I C:/android-sdk/platforms/android-20/android.jar -S android/res $(OBJ)apk", - "jarsigner -storepass android -sigalg MD5withRSA -digestalg SHA1 $(OBJ)$(MODULE)-unsigned.apk androiddebugkey -keystore C:/users/jerome/debug.keystore -signedjar $(OBJ)$(MODULE).apk", + "$(call mkdir,$(OBJ)classes)", + "$(call mkdir,$(OBJ)apk/lib/arm64-v8a)", + "javac -verbose -d $(OBJ)/classes -classpath /android/platforms/android-28/android.jar:$(OBJ) -sourcepath . android/$(MODULE).java", + "dx --dex --verbose --output=$(OBJ)apk/classes.dex $(OBJ)classes", + "$(call cp,../../../ecere/obj/android.linux.$(COMPILER)/libecere.so.0.44,$(OBJ)apk/lib/arm64-v8a/libecere.so)", + "$(call cp,$(TARGET),$(OBJ)apk/lib/arm64-v8a)", + "aapt package -v -f -m -M android/AndroidManifest.xml -F $(OBJ)$(MODULE)-unsigned.apk -I /android/platforms/android-28/android.jar -S android/res $(OBJ)apk", + "jarsigner -storepass android -sigalg MD5withRSA -digestalg SHA1 $(OBJ)$(MODULE)-unsigned.apk androiddebugkey -keystore /android/debug.keystore -signedjar $(OBJ)$(MODULE).apk", "adb uninstall com.ecere.$(MODULE)", "adb install $(OBJ)$(MODULE).apk", - "adb shell am start -a android.intent.action.MAIN -n com.ecere.$(MODULE)/android.app.NativeActivity" + "adb shell am start -a android.intent.action.MAIN -n com.ecere.$(MODULE)/.$(MODULE)" ] } }, @@ -121,7 +123,8 @@ } ] }, - "AndroidManifest.xml" + "AndroidManifest.xml", + "Chess.java" ] } ], @@ -310,8 +313,8 @@ "../../../ecere/src/gfx/drivers/gl3/default.vert" ] }, - "C:/Windows/Fonts/tahoma.ttf", - "C:/Windows/Fonts/tahomabd.ttf" + "$(ECERE_SDK_SRC)/ecere/res/fonts/tahoma.ttf", + "$(ECERE_SDK_SRC)/ecere/res/fonts/tahomabd.ttf" ], "Options" : { "ExcludeFromBuild" : true diff --git a/samples/games/chess/src/chess3D.ec b/samples/games/chess/src/chess3D.ec index cbf89dbe62..e01b753b4e 100644 --- a/samples/games/chess/src/chess3D.ec +++ b/samples/games/chess/src/chess3D.ec @@ -142,6 +142,7 @@ class Chess3D : Window void OnUnloadGraphics() { + chessSet.Free(displaySystem); displaySystem.ClearMaterials(); displaySystem.ClearTextures(); displaySystem.ClearMeshes(); diff --git a/samples/guiAndGfx/EcereOffice/Writer/RichEdit.ec b/samples/guiAndGfx/EcereOffice/Writer/RichEdit.ec new file mode 100644 index 0000000000..f1226884f2 --- /dev/null +++ b/samples/guiAndGfx/EcereOffice/Writer/RichEdit.ec @@ -0,0 +1,1820 @@ +import "ecere" +import "HTMLView" +import "ewd" + +#define UTF8_IS_FIRST(x) (__extension__({ byte b = x; (!(b) || !((b) & 0x80) || (b) & 0x40); })) +#define UTF8_NUM_BYTES(x) (__extension__({ byte b = x; (b & 0x80 && b & 0x40) ? ((b & 0x20) ? ((b & 0x10) ? 4 : 3) : 2) : 1; })) + +class RichEdit : HTMLView +{ + hasVertScroll = true; + hasHorzScroll = true; + + bool readOnly, edit, selecting; + int caretX, caretY; + + edit = true; + + bool loadHTML(File f, const String path) + { + bool result = true; + Block fontBlock; + +#if 0 //defined(_DEBUG) + { + ConsoleFile con { }; + char fn[100]; + sprintf(fn, "File://%p", con); + f.CopyTo(fn); + delete con; + } +#endif + f.Seek(0, start); + textBlock = null; + + OpenFile(f, path); + + fontBlock = (Block)html.body.subBlocks.first; + if(fontBlock && fontBlock.type == TEXT) + textBlock = fontBlock, fontBlock = null; + if(!fontBlock) + fontBlock = html.body; + + if(!textBlock) + textBlock = fontBlock.subBlocks.first; + if(!textBlock) + { + textBlock = { type = TEXT, parent = fontBlock, font = fontBlock.font, text = CopyString("") }; + fontBlock.subBlocks.Insert(null, textBlock); + selBlock = textBlock; + } + selBlock = textBlock; + curPosition = selPosition = 0; + + ComputeMinSizes(); + ComputeSizes(); + positionCaret(true); + Update(null); + + return result; + } + + bool loadDocument(File f, const String path) + { + bool result = false; + TempFile out { }; + + ewd2html(f, out, false); + + result = loadHTML(out, path); + + delete out; + + return result; + } + + bool saveDocument(File f) + { + return writeEWD(html, f); + } + + bool OnCreate() + { + TempFile f { }; + Block fontBlock, newBlock; + f.PrintLn( + "" + ); + f.Seek(0, start); + OpenFile(f, null); + delete f; + + fontBlock = html.body.subBlocks.first; + + newBlock = { type = TEXT, parent = fontBlock, font = fontBlock.font, text = CopyString("") }; + fontBlock.subBlocks.Insert(null, newBlock); + textBlock = newBlock; + selBlock = textBlock; + return HTMLView::OnCreate(); + } + + bool OnLeftButtonDown(int x, int y, Modifiers mods) + { + bool result = HTMLView::OnLeftButtonDown(x, y, mods); + + if(edit && textBlock) + { + Block b = null; + int c = 0; + if(PickHTML(x, y, &b, &c)) + { + textBlock = b; + + curPosition = c; + selBlock = textBlock; + selPosition = curPosition; + + positionCaret(true); + selecting = true; + Update(null); + Capture(); + } + } + return result; + } + + bool OnLeftButtonUp(int x, int y, Modifiers mods) + { + if(selecting) + { + ReleaseCapture(); + selecting = false; + } + return true; + } + + bool OnMouseMove(int x, int y, Modifiers mods) + { + if(edit && selecting) + { + Block b = null; + int c = 0; + if(PickHTML(x, y, &b, &c)) + { + textBlock = b; + curPosition = c; + } + + positionCaret(true); + Update(null); + } + return HTMLView::OnMouseMove(x, y, mods); + } + + bool OnLeftDoubleClick(int mx, int my, Modifiers mods) + { + if(edit && textBlock) + { + int c; + int start = -1; + int numBytes; + + PickHTML(mx, my, &textBlock, &curPosition); + selPosition = curPosition; + selBlock = textBlock; + + // TODO: Word spanning multiple formatting... + for(c = curPosition; c >= 0; c--) + { + unichar ch; + while(c > 0 && !UTF8_IS_FIRST(textBlock.text[c])) c--; + ch = UTF8GetChar(textBlock.text + c, &numBytes); + if(!CharMatchCategories(ch, letters|numbers|marks|connector)) + break; + start = c; + } + if(start != -1) + { + for(c = start; c < textBlock.textLen; c += numBytes) + { + unichar ch = UTF8GetChar(textBlock.text + c, &numBytes); + if(!CharMatchCategories(ch, letters|numbers|marks|connector)) + break; + } + selPosition = start; + curPosition = c; + + positionCaret(true); + Update(null); + return false; + } + } + return true; + } + + bool OnOpen(char * href) + { + + return true; + } + + bool sameFont(Block a, Block b) + { + if(!a || !b) return false; + + while(a && a.type != FONT && a.parent) + a = a.parent; + while(b && b.type != FONT && b.parent) + b = b.parent; + + return a.attribs == b.attribs && + a.font.size == b.font.size && + a.textColor == b.textColor && + !strcmpi(a.font.face, b.font.face); + } + + void deleteSelection() + { + if(textBlock != selBlock || curPosition != selPosition) + { + if(textBlock == selBlock) + { + // Within same block + int start = Min(curPosition, selPosition); + int end = Max(curPosition, selPosition); + memmove(textBlock.text + start, textBlock.text + end, textBlock.textLen - end); + textBlock.textLen -= end-start; + textBlock.text = renew textBlock.text char[textBlock.textLen + 1]; + curPosition = start; + selPosition = start; + } + else + { + int startSel, endSel; + Block startSelBlock = null, endSelBlock = null, b; + bool keepTwoBlocks = false; + + NormalizeSelection(&startSelBlock, &startSel, &endSelBlock, &endSel); + + if(startSelBlock.type == TEXT && endSelBlock.type == TEXT && sameFont(startSelBlock, endSelBlock)) + { + // Merge the text + startSelBlock.text = renew startSelBlock.text char[startSel + endSelBlock.textLen - endSel + 1]; + memcpy(startSelBlock.text + startSel, endSelBlock.text + endSel, endSelBlock.textLen - endSel + 1); + startSelBlock.textLen = startSel + endSelBlock.textLen - endSel; + } + else + { + // Keep Two Blocks + keepTwoBlocks = true; + startSelBlock.text = renew startSelBlock.text char[startSel + 1]; + startSelBlock.textLen = startSel; + + memmove(endSelBlock.text, endSelBlock.text + endSel, endSelBlock.textLen - endSel + 1); + endSelBlock.text = renew endSelBlock.text char[endSelBlock.textLen - endSel + 1]; + endSelBlock.textLen = endSelBlock.textLen - endSel; + } + + b = startSelBlock; + do + { + b = GetNextBlock(b); + } while(b && b.type != TEXT && b.type != BR); + + while(b) + { + bool isEnd = b == endSelBlock; + Block parent, next = b; + + if(isEnd && keepTwoBlocks) break; + + do + { + next = GetNextBlock(next); + } while(next && (next.type != TEXT && next.type != BR)); + parent = b.parent; + + parent.subBlocks.Remove(b); + delete b; + + while(parent && !parent.subBlocks.count) + { + Block p = parent.parent; + p.subBlocks.Remove(parent); + delete parent; + parent = p; + } + + if(isEnd) + break; + b = next; + } + textBlock = startSelBlock; + selBlock = startSelBlock; + curPosition = startSel; + selPosition = startSel; + } + ComputeMinSizes(); + ComputeSizes(); + positionCaret(true); + Update(null); + } + } + + String getSelectionString() + { + String selection = null; + if(textBlock == selBlock) + { + // Within same block + int start = Min(curPosition, selPosition); + int end = Max(curPosition, selPosition); + int len = end - start; + selection = new char[len + 1]; + memcpy(selection, textBlock.text + start, len); + selection[len] = 0; + } + else + { + int startSel, endSel; + Block startSelBlock = null, endSelBlock = null, b; + int totalLen = 0; + + NormalizeSelection(&startSelBlock, &startSel, &endSelBlock, &endSel); + + // Compute length + for(b = startSelBlock; b; b = GetNextBlock(b)) + { + int start = (b == startSelBlock) ? startSel : 0; + int end = (b == endSelBlock) ? endSel : b.textLen; + int len = end - start; + totalLen += len; + if(b == endSelBlock) + break; + else if(b.type == BR) + totalLen++; + } + + selection = new char[totalLen + 1]; + totalLen = 0; + for(b = startSelBlock; b; b = GetNextBlock(b)) + { + int start = (b == startSelBlock) ? startSel : 0; + int end = (b == endSelBlock) ? endSel : b.textLen; + int len = end - start; + memcpy(selection + totalLen, b.text + start, len); + totalLen += len; + if(b == endSelBlock) + break; + else if(b.type == BR) + selection[totalLen++] = '\n'; + } + selection[totalLen] = 0; + } + return selection; + } + + void copySelection() + { + String s = getSelectionString(); + if(s) + { + int len = strlen(s); + ClipBoard cb { }; + if(cb.Allocate(len + 1)) + { + memcpy(cb.text, s, len + 1); + cb.Save(); + } + delete cb; + delete s; + } + } + + bool OnKeyDown(Key key, unichar ch) + { + if(edit) + { + switch(key) + { + case escape: + OnLeftButtonDown(0,0,0); + return false; + case Key { end, shift = true }: + case end: + curPosition = textBlock.textLen; + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + positionCaret(true); + Update(null); + break; + case Key { home, shift = true }: + case home: + curPosition = 0; + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + positionCaret(true); + Update(null); + break; + case Key { home, ctrl = true, shift = true }: + case ctrlHome: + curPosition = 0; + while(textBlock.prev) + textBlock = textBlock.prev.prev; + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + positionCaret(true); + Update(null); + return false; + case Key { end, ctrl = true, shift = true }: + case ctrlEnd: + while(textBlock.next && textBlock.next.next) + textBlock = textBlock.next.next; + curPosition = textBlock.textLen; + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + positionCaret(true); + Update(null); + return false; + } + } + else + return HTMLView::OnKeyDown(key, ch); + return true; + } + + bool OnKeyHit(Key key, unichar ch) + { + if(edit) + { + switch(key) + { + case Key { up, shift = true }: + case up: + { + if(caretY == textBlock.startY) + { + Block block = textBlock; + bool passedBR = false; + + while(true) + { + if(block.subBlocks.last) + block = block.subBlocks.last; + else if(block.prev) + block = block.prev; + else + { + block = block.parent; + while(block && !block.prev) block = block.parent; + if(block) block = block.prev; + } + if(block && block.type == BR) + passedBR = true; + if(!block || (passedBR && block.type == TEXT)) + break; + } + + if(passedBR) + { + textBlock = block; + curPosition = Min(curPosition, textBlock.textLen); + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + Update(null); + positionCaret(false); + caretY = MAXINT; + } + else + return false; + } + + { + int th = 0; + int textPos = 0; + int sx = textBlock.startX, sy = textBlock.startY; + char * text = textBlock.text; + int maxW; + Block block = textBlock; + while(block && block.type != TD) block = block.parent; + if(block) + { + Block table = block; + while(table && table.type != TABLE) table = table.parent; + if(table) + maxW = block.w - 2* table.cellPadding; + else + maxW = clientSize.w - 10 - sx; + } + else + maxW = clientSize.w - 10 - sx; + display.FontExtent(textBlock.font.font, " ", 1, null, &th); + + do + { + int startPos = textPos; + int width = 0; + int x = 0; + bool lineComplete = false; + for(; textPos maxW && x > 0) + { + lineComplete = true; + break; + } + textPos += len; + width += w; + if(nextSpace) + { + x += width; + width = 0; + } + if(textPos == textBlock.textLen || (sy == caretY - th && caretX <= x + width + sx)) + { + x += width; + curPosition = textPos; + while(curPosition > 0 && x + sx > caretX && textPos > startPos) + { + int len; + while(curPosition > 0 && !UTF8_IS_FIRST(text[--curPosition])); + len = curPosition - startPos; + display.FontExtent(textBlock.font.font, text + startPos, len, &x, null); + } + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + Update(null); + + positionCaret(false); + return false; + } + } + if(sy == caretY - th || textPos == textBlock.textLen) + { + if(textPos != textBlock.textLen) + { + int c = textPos - 1; + while(c > 0 && text[c] == ' ') c--; + curPosition = c + 1; + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + Update(null); + } + else + { + curPosition = textBlock.textLen; + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + Update(null); + } + positionCaret(false); + return false; + } + sy += th; + sx = textBlock.startX; + } while(textPos < textBlock.textLen); + return false; + } + return false; + } + case Key { down, shift = true }: + case down: + { + int th = 0; + int textPos = 0; + int sx = textBlock.startX, sy = textBlock.startY; + char * text = textBlock.text; + int maxW; + Block block = textBlock; + while(block && block.type != TD) block = block.parent; + if(block) + { + Block table = block; + while(table && table.type != TABLE) table = table.parent; + if(table) + maxW = block.w - 2* table.cellPadding; + else + maxW = clientSize.w - 10 - sx; + } + else + maxW = clientSize.w - 10 - sx; + display.FontExtent(textBlock.font.font, " ", 1, null, &th); + + while(!textPos || textPos < textBlock.textLen) + { + int startPos = textPos; + int width = 0; + int x = 0; + bool lineComplete = false; + for(; (textPos < textBlock.textLen) && !lineComplete;) + { + int w; + int len; + char * nextSpace = strchr(text + textPos, ' '); + + if(nextSpace) + len = (nextSpace - (text + textPos)) + 1; + else + len = textBlock.textLen - textPos; + + display.FontExtent(textBlock.font.font, text + textPos, len, &w, &th); + + if(x + width + w > maxW && x > 0) + { + lineComplete = true; + break; + } + textPos += len; + width += w; + if(nextSpace) + { + x += width; + width = 0; + } + if(sy > caretY && (textPos == textBlock.textLen || caretX <= x + width + sx)) + { + curPosition = textPos; + x += width; + while(curPosition > 0 && x + sx > caretX && textPos > startPos) + { + int len; + while(curPosition > 0 && !UTF8_IS_FIRST(text[--curPosition])); + len = curPosition - startPos; + display.FontExtent(textBlock.font.font, text + startPos, len, &x, null); + } + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + Update(null); + positionCaret(false); + return false; + } + } + if(sy > caretY) + { + curPosition = textBlock.textLen; + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + Update(null); + positionCaret(false); + return false; + } + else if(textPos == textBlock.textLen && textBlock.next && textBlock.next.next) + { + startPos = 0; + textPos = 0; + textBlock = textBlock.next.next; + sy = textBlock.startY; + sx = textBlock.startX; + text = textBlock.text; + } + else + { + sy += th; + sx = textBlock.startX; + } + } + + /*if(textBlock.next && textBlock.next.next) + { + textBlock = textBlock.next.next; + selPosition = curPosition = Min(curPosition, textBlock.textLen); + selBlock = textBlock; + positionCaret(false); + }*/ + break; + } + case Key { right, shift = true, ctrl = true }: + case ctrlRight: + { + bool foundAlpha = false; + bool found = false; + Block block = textBlock, lastBlock = null; + int lastC = 0; + bool passedBR = false; + + while(block) + { + int start = (block == textBlock) ? curPosition : 0; + int c; + for(c = start; c < block.textLen; c++) + { + char ch = block.text[c]; + bool isAlUnder = CharMatchCategories(ch, letters|numbers|marks|connector); + if(key.shift ? isAlUnder : !isAlUnder) + { + foundAlpha = true; + lastC = c; + lastBlock = block; + } + else if(foundAlpha) + { + found = true; + if(!key.shift) + { + selPosition = curPosition = c; + selBlock = textBlock = block; + Update(null); + positionCaret(true); + } + break; + } + } + // No next word found, + if(!found && (/*c != curPosition || */passedBR)) //block != textBlock)) + { + found = true; + lastBlock = block; + lastC = block.textLen-1; + if(key.shift) + break; + else + { + selPosition = curPosition = block.textLen; + selBlock = textBlock = block; + Update(null); + positionCaret(true); + } + } + if(found) break; + + if(!key.shift && start < block.textLen) + foundAlpha = true; + + while(true) + { + if(block.subBlocks.first) + block = block.subBlocks.first; + else if(block.next) + block = block.next; + else + { + block = block.parent; + while(block && !block.next) block = block.parent; + if(block) block = block.next; + } + if(block && block.type == BR) + passedBR = true; + if(!block || block.type == TEXT) + break; + } + + if(!block && !found && lastBlock) + { + curPosition = lastC+1; + textBlock = lastBlock; + if(!key.shift && !found) + { + selBlock = textBlock; + selPosition = curPosition; + } + positionCaret(true); + Update(null); + } + else if(!block && !found && key.shift) + { + curPosition = textBlock.textLen; + positionCaret(true); + Update(null); + } + + if(!key.shift && passedBR) + foundAlpha = true; + } + if(key.shift && found) + { + curPosition = lastC+1; + textBlock = lastBlock; + positionCaret(true); + Update(null); + } + else if(!key.shift && !found) + { + // Deselect + selBlock = textBlock; + selPosition = curPosition; + Update(null); + } + break; + } + case Key { left, ctrl = true, shift = true }: + case ctrlLeft: + { + bool foundAlpha = false; + bool found = false; + Block block = textBlock, lastBlock = null; + int lastC = 0; + bool passedBR = false; + + while(block && !found) + { + int start, c; + if(curPosition == 0 && block != textBlock && passedBR) + { + foundAlpha = true; + if(!lastBlock) + { + lastC = block.textLen; + lastBlock = block; + } + break; + } + start = (block == textBlock ? curPosition : block.textLen)-1; + for(c = start; c>=0; c--) + { + if(CharMatchCategories(block.text[c], letters|numbers|marks|connector)) + { + foundAlpha = true; + lastC = c; + lastBlock = block; + } + else if(foundAlpha) + { + found = true; + break; + } + } + if(found) break; + + // No next word found, + /*if(curPosition > 0) + { + foundAlpha = true; + lastC = 0; + lastBlock = block; + break; + }*/ + + while(true) + { + if(block.subBlocks.last) + block = block.subBlocks.last; + else if(block.prev) + block = block.prev; + else + { + block = block.parent; + while(block && !block.prev) block = block.parent; + if(block) block = block.prev; + } + if(block && block.type == BR) + passedBR = true; + if(!block || block.type == TEXT) + break; + } + } + if(foundAlpha) + { + textBlock = lastBlock; + curPosition = lastC; + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + positionCaret(true); + Update(null); + } + if(!key.shift && !found) + { + // Deselect + selBlock = textBlock; + selPosition = curPosition; + Update(null); + } + break; + } + case Key { right, shift = true }: + case right: + if(!key.shift && (textBlock != selBlock || curPosition != selPosition)) + { + selPosition = curPosition; + selBlock = textBlock; + positionCaret(true); + Update(null); + } + else if(curPosition < textBlock.textLen) + { + curPosition += UTF8_NUM_BYTES(textBlock.text[curPosition]); + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + positionCaret(true); + Update(null); + } + else + { + Block block = textBlock; + bool passedBR = textBlock.type == BR; + + while(true) + { + if(block.subBlocks.first) + block = block.subBlocks.first; + else if(block.next) + block = block.next; + else + { + block = block.parent; + while(block && !block.next) block = block.parent; + if(block) block = block.next; + } + if(block && block.type == BR) + passedBR = true; + if(!block || ((block.type == TEXT && block.textLen) || (block.type == BR && textBlock.startY != block.startY))) + break; + } + if(block) + { + textBlock = block; + curPosition = 0; + if(!passedBR && curPosition < textBlock.textLen) + curPosition += UTF8_NUM_BYTES(textBlock.text[curPosition]); + + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + positionCaret(true); + Update(null); + } + } + break; + case Key { left, shift = true }: + case left: + if(!key.shift && (textBlock != selBlock || curPosition != selPosition)) + { + selPosition = curPosition; + selBlock = textBlock; + positionCaret(true); + Update(null); + } + else if(curPosition > 0) + { + while(curPosition > 0 && !UTF8_IS_FIRST(textBlock.text[--curPosition])); + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + positionCaret(true); + Update(null); + } + else + { + Block block = textBlock; + bool passedBR = false; //block.type == BR; + + while(true) + { + if(block.subBlocks.last) + block = block.subBlocks.last; + else if(block.prev) + block = block.prev; + else + { + block = block.parent; + while(block && !block.prev) block = block.parent; + if(block) block = block.prev; + } + if(block && block.type == BR) + passedBR = true; + if(!block || ((block.type == TEXT && block.textLen) || block.type == BR)) + break; + } + if(block) + { + textBlock = block; + curPosition = textBlock.textLen; + if(!passedBR && curPosition > 0) + while(curPosition > 0 && !UTF8_IS_FIRST(textBlock.text[--curPosition])); + if(!key.shift) + { + selPosition = curPosition; + selBlock = textBlock; + } + positionCaret(true); + Update(null); + } + } + break; + case backSpace: + case Key { backSpace, shift = true }: + if(readOnly) break; + if(textBlock == selBlock && curPosition == selPosition) + { + Block block = textBlock, brBlock = null; + bool change = false; + + if(!curPosition) + { + while(true) + { + if(block.subBlocks.last) + block = block.subBlocks.last; + else if(block.prev) + block = block.prev; + else + { + block = block.parent; + while(block && !block.prev) block = block.parent; + if(block) block = block.prev; + } + if(block && block.type == BR) + brBlock = block; + if(!block || brBlock || (block.type == TEXT && block.textLen)) + break; + } + if(!brBlock && block) + { + selBlock = textBlock = block; + selPosition = curPosition = block.textLen; + } + } + + if(curPosition) + { + int c = curPosition; + int nb = 1; + while(c > 0 && !UTF8_IS_FIRST(textBlock.text[--c])) nb++; + memmove(textBlock.text + curPosition - nb, textBlock.text + curPosition, textBlock.textLen - curPosition + 1); + textBlock.textLen -= nb; + textBlock.text = renew textBlock.text char[textBlock.textLen + 1]; + curPosition -= nb; + selPosition = curPosition; + selBlock = textBlock; + change = true; + } + else if(brBlock) + { + Block prevBlock = block; + if(textBlock.type == TEXT && prevBlock && prevBlock.type == TEXT && sameFont(textBlock, prevBlock)) + { + prevBlock.text = renew prevBlock.text char[prevBlock.textLen + textBlock.textLen + 1]; + memcpy(prevBlock.text + prevBlock.textLen, textBlock.text, textBlock.textLen + 1); + + curPosition = prevBlock.textLen; + prevBlock.textLen += textBlock.textLen; + textBlock.parent.subBlocks.Remove(textBlock); + delete textBlock; + selBlock = textBlock = prevBlock; + selPosition = curPosition; + } + brBlock.parent.subBlocks.Remove(brBlock); + delete brBlock; + + change = true; + } + if(change) + { + ComputeMinSizes(); + ComputeSizes(); + positionCaret(true); + Update(null); + } + } + else + deleteSelection(); + break; + case del: + if(readOnly) break; + if(textBlock != selBlock || curPosition != selPosition) + deleteSelection(); + else + { + bool changed = false; + Block block = textBlock, brBlock = block.type == BR ? block : null; + + if(brBlock || curPosition == textBlock.textLen) + { + while(true) + { + if(block.subBlocks.first) + block = block.subBlocks.first; + else if(block.next) + block = block.next; + else + { + block = block.parent; + while(block && !block.next) block = block.parent; + if(block) block = block.next; + } + if(block && block.type == BR) + brBlock = block; + if(!block || (block.type == TEXT && block.textLen)) + break; + } + + if(!brBlock && block) + { + textBlock = block; + curPosition = 0; + } + } + + if(textBlock.textLen > curPosition) + { + int nb = UTF8_NUM_BYTES(textBlock.text[curPosition]); + memmove(textBlock.text + curPosition, textBlock.text + curPosition + nb, textBlock.textLen - curPosition + 1 - nb + 1); + textBlock.textLen -= nb; + textBlock.text = renew textBlock.text char[textBlock.textLen + 1]; + + changed = true; + } + else if(block && block != textBlock) + { + Block nextBlock = block; + + if(textBlock.type == BR) + textBlock = nextBlock; + else if(sameFont(nextBlock, textBlock)) + { + textBlock.text = renew textBlock.text char[textBlock.textLen + nextBlock.textLen + 1]; + memcpy(textBlock.text + textBlock.textLen, nextBlock.text, nextBlock.textLen + 1); + textBlock.textLen += nextBlock.textLen; + if(nextBlock) + { + nextBlock.parent.subBlocks.Remove(nextBlock); + delete nextBlock; + } + changed = true; + } + if(brBlock) + { + brBlock.parent.subBlocks.Remove(brBlock); + delete brBlock; + changed = true; + } + } + selBlock = textBlock; + selPosition = curPosition; + + if(changed) + { + ComputeMinSizes(); + ComputeSizes(); + positionCaret(true); + Update(null); + } + } + break; + case enter: + { + int th = 0; + Block block; + Block newBlock; + int startY, startX; + + if(readOnly) break; + deleteSelection(); + + block = { type = BR, parent = textBlock.parent, font = textBlock.font }; + newBlock = { type = TEXT, parent = textBlock.parent, font = textBlock.font }; + startY = textBlock.startY; + startX = textBlock.startX; + + display.FontExtent(textBlock.font.font, " ", 1, null, &th); + textBlock.parent.subBlocks.Insert(textBlock, block); + textBlock.parent.subBlocks.Insert(block, newBlock); + + startY += th; + + newBlock.textLen = textBlock.textLen - curPosition; + newBlock.text = new char[newBlock.textLen+1]; + if(textBlock.type == TEXT) + { + memcpy(newBlock.text, textBlock.text + curPosition, textBlock.textLen - curPosition + 1); + textBlock.textLen = curPosition; + textBlock.text[curPosition] = 0; + } + else + newBlock.text[0] = 0; + + newBlock.startY = startY; + newBlock.startX = startX; + selPosition = curPosition = 0; + selBlock = textBlock = newBlock; + + ComputeMinSizes(); + ComputeSizes(); + positionCaret(true); + Update(null); + break; + } + case ctrlX: + case Key { del, shift = true }: + if(readOnly) break; + // Cut + copySelection(); + deleteSelection(); + break; + case ctrlC: + case ctrlInsert: + // Copy + copySelection(); + break; + // Paste + case shiftInsert: + case ctrlV: + if(!readOnly) + { + ClipBoard clipBoard { }; + if(clipBoard.Load()) + { + int c; + char * text = clipBoard.memory; + char ch; + int start = 0; + Block parent; + FontEntry font; + + deleteSelection(); + + parent = textBlock.parent; + font = textBlock.font; + + for(c = 0; ; c++) + { + ch = text[c]; + if(ch == '\n' || ch == '\r' || !ch) + { + int len = c - start; + textBlock.text = renew textBlock.text char[textBlock.textLen + 1 + len]; + memmove(textBlock.text + curPosition + len, textBlock.text + curPosition, textBlock.textLen - curPosition + 1); + memcpy(textBlock.text + curPosition, text + start, len); + textBlock.textLen += len; + curPosition += len; + selPosition = curPosition; + selBlock = textBlock; + if(!ch) break; + { + Block block { type = BR, parent = parent, font = font }; + Block newBlock { type = TEXT, parent = parent, font = font }; + int startY = textBlock.startY, startX = textBlock.startX; + int th = 0; + + display.FontExtent(textBlock.font.font, " ", 1, null, &th); + textBlock.parent.subBlocks.Insert(textBlock, block); + textBlock.parent.subBlocks.Insert(block, newBlock); + + startY += th; + + newBlock.textLen = textBlock.textLen - curPosition; + newBlock.text = new char[newBlock.textLen+1]; + memcpy(newBlock.text, textBlock.text + curPosition, textBlock.textLen - curPosition + 1); + textBlock.textLen = curPosition; + textBlock.text[curPosition] = 0; + + newBlock.startY = startY; + newBlock.startX = startX; + selPosition = curPosition = 0; + selBlock = textBlock; + textBlock = newBlock; + } + if(ch == '\r' && text[c+1] == '\n') c++; + start = c + 1; + } + } + ComputeMinSizes(); + ComputeSizes(); + positionCaret(true); + Update(null); + } + delete clipBoard; + } + break; + case ctrlB: case ctrlI: + case ctrl1: case ctrl2: case ctrl3: case ctrl4: case ctrl5: + case ctrl6: case ctrl7: case ctrl8: case ctrl9: case ctrl0: + case Key { code = equal, ctrl = true }: + case Key { code = minus, ctrl = true }: + { + Block font = textBlock.parent; + Block parent = font.parent; + uint flag = key == ctrlB ? 1 : key == ctrlI ? 2 : 0; + uint attribs = font.font.attribs ^ flag; + Color color = key.code == k0 ? black : + key.code >= k1 && key.code <= k9 ? GetDefaultPalette()[key.code - k1 + 1] : font.textColor; + float size = font.font.size; + FontEntry entry; + + if(key == Key { code = equal, ctrl = true }) + size *= 1.333333333333333333333333; + else if(key == Key { code = minus, ctrl = true }) + size /= 1.333333333333333333333333; + + for(entry = fontCache.first; entry; entry = entry.next) + { + if(!strcmpi(entry.face, font.font.face) && + entry.size == size && + entry.attribs == attribs) + break; + } + if(!entry) + { + display.Lock(false); + entry = FontEntry + { + font = displaySystem.LoadFont(font.font.face, size, attribs), + size = size, attribs = attribs, face = CopyString(font.font.face) + }; + fontCache.Add(entry); + display.Unlock(); + } + + if(textBlock.type == BR || textBlock.text[0] || textBlock.prev) + { + Block fontBlock + { + parent = parent, + type = FONT, + font = entry, textColor = color, size = size, attribs = attribs, face = CopyString(font.font.face) + }; + bool afterCurrent = (textBlock.type == BR || curPosition || !textBlock.textLen); + + // TODO: Cut blocks in two + parent.subBlocks.Insert(afterCurrent ? font : font.prev, fontBlock); + if(textBlock.type == TEXT && textBlock.text[0]) + { + Block newBlock { type = TEXT, parent = fontBlock, font = fontBlock.font, text = CopyString("") }; + + // Move anything after this block to the new font block... + if(afterCurrent) + { + Block block = textBlock.next; + while(block) + { + Block next = block.next; + font.subBlocks.Remove(block); + fontBlock.subBlocks.Add(block); + block.parent = fontBlock; + block.font = fontBlock.font; + block = next; + } + } + + selBlock = textBlock = newBlock; + selPosition = curPosition = 0; + fontBlock.subBlocks.Insert(null, newBlock); + } + else if(afterCurrent) + { + Block block = textBlock; + while(block) + { + Block next = block.next; + font.subBlocks.Remove(block); + fontBlock.subBlocks.Add(block); + block.parent = fontBlock; + block.font = fontBlock.font; + block = next; + } + } + } + else + { + delete font.face; + font.font = entry, font.textColor = color, font.size = size, font.attribs = attribs, + font.face = CopyString(font.font.face); + textBlock.font = font.font; + } + + ComputeMinSizes(); + ComputeSizes(); + positionCaret(true); + Update(null); + + break; + } + case ctrlA: + { + selBlock = html.body.subBlocks.first; + while(selBlock && selBlock.type != TEXT) // && selBlock.type != BR) + selBlock = GetNextBlock(selBlock); + selPosition = 0; + textBlock = selBlock; + while(true) + { + Block next = GetNextBlock(textBlock); + while(next && next.type != TEXT) // && next.type != BR) + next = GetNextBlock(next); + + if(next) + textBlock = next, curPosition = textBlock.textLen; + else + break; + } + ComputeMinSizes(); + ComputeSizes(); + positionCaret(true); + Update(null); + break; + } + default: + { + // eC BUG HERE: (Should be fixed) + if(!readOnly && !key.ctrl && !key.alt && ch >= 32 && ch != 128 /*&& ch < 128*/) + { + char string[5]; + int len = UTF32toUTF8Len(&ch, 1, string, 5); + int c; + + deleteSelection(); + + if(textBlock.type != TEXT) + { + Block parent = textBlock.parent; + Block newBlock { type = TEXT, parent = parent, font = parent.font, text = CopyString(""); }; + parent.subBlocks.Insert(textBlock.prev, newBlock); + textBlock = newBlock; + curPosition = 0; + } + + textBlock.text = renew textBlock.text char[textBlock.textLen + len + 1]; + memmove(textBlock.text + curPosition + len, textBlock.text + curPosition, textBlock.textLen - curPosition + 1); + + for(c = 0; c= endTextPos) + break; + + switch(block.type) + { + case INPUT: + if(block.window) + x += block.window.size.w; + break; + case BODY: + break; + case IMAGE: + { + int bw = block.pWidth ? (w * block.pWidth / 100) : block.w; + //int bh = block.pHeight ? (h * block.pHeight / 100) : block.h; + int dx; //, dy; + + //ColorAlpha fg = surface.GetForeground(); + surface.SetForeground(white); + + switch(block.halign) + { + case HorizontalAlignment::left: + block.valign = top; + dx = x; + break; + case HorizontalAlignment::right: + block.valign = top; + dx = x + w - bw; + dx = Max(x, dx); + break; + case middle: + dx = x; + break; + } + /* + switch(block.valign) + { + case bottom: dy = y + h - bh; break; + case top: dy = y; break; + case middle: dy = y + (h - bh) / 2; break; + } + */ + x += bw; + break; + } + case BR: + { + if(block == textBlock) + { + int th; + + surface.TextExtent(" ", 1, null, &th); + *psx = x; + *psy = h - th + y; + result = true; + } + break; + } + case TEXT: + { + int tw, th; + int endPos = (block == endBlock) ? endTextPos : block.textLen; + int len = endPos - textPos; + + if(startSelBlock && block == startSelBlock && startSel >= textPos && startSel <= textPos + len) + { + int l = startSel - textPos; + if(block.text) + { + int pPrevGlyph = prevGlyph; + surface.TextExtent2(block.text + textPos, l, &tw, &th, prevGlyph, &prevGlyph, null); + if(block == textBlock && pos >= textPos && pos <= textPos + l) + { + int ttw; + surface.TextExtent2(block.text + textPos, pos - textPos, &ttw, null, pPrevGlyph, &pPrevGlyph, null); + if(!th) + surface.TextExtent(" ", 1, null, &th); + + *psx = x + ttw; + *psy = h - th + y; + result = true; + break; + } + x += tw; + } + textPos += l; + this.isSelected = true; + len -= l; + } + + if(endSelBlock && block == endSelBlock && endPos > textPos && endSel >= textPos && endSel < textPos + len) + len = endSel - textPos; + + if(block.text) + { + int pPrevGlyph = prevGlyph; + if(this.isSelected) + { + surface.background = Color { 10, 36, 106 }; + surface.foreground = white; + surface.textOpacity = true; + } + surface.TextExtent2(block.text + textPos, len, &tw, &th, prevGlyph, &prevGlyph, null); + if(block == textBlock && pos >= textPos && pos <= textPos + len) + { + int ttw; + surface.TextExtent2(block.text + textPos, pos - textPos, &ttw, null, pPrevGlyph, &pPrevGlyph, null); + if(!th) + surface.TextExtent(" ", 1, null, &th); + *psx = x + ttw; + *psy = h - th + y; + result = true; + break; + } + + x += tw; + if(this.isSelected) + { + surface.background = back; + surface.foreground = fore; + surface.textOpacity = false; + } + } + textPos += len; + if(block == endSelBlock && textPos >= endSel) + this.isSelected = false; + + if(endPos > textPos) + { + int l = endPos - textPos; + if(block.text) + { + int pPrevGlyph = prevGlyph; + surface.TextExtent2(block.text + textPos, l, &tw, &th, prevGlyph, &prevGlyph, null); + if(block == textBlock && pos >= textPos && pos <= textPos + l) + { + int ttw; + surface.TextExtent2(block.text + textPos, pos - textPos, &ttw, null, pPrevGlyph, &pPrevGlyph, null); + if(!th) + surface.TextExtent(" ", 1, null, &th); + *psx = x + ttw; + *psy = h - th + y; + result = true; + break; + } + + x += tw; + } + textPos += l; + } + break; + } + case FONT: + case ANCHOR: + surface.TextFont(block.font.font); + break; + case TABLE: + // TODO: FindCaretTable(this, surface, x, y, w, h, left, right, block); + block = NextBlockUp(surface, block, null, RenderFlags { render = true }); + textPos = 0; + break; + } + if(result) break; + + if(block == endBlock && textPos >= endTextPos) + break; + + if(textPos >= block.textLen) + { + block = NextBlock(surface, block, null, RenderFlags { render = true }); + textPos = 0; + } + } + return result; + } + + void findCaretPos(Block textBlock, int pos, int * psx, int * psy) + { + int sx = 0, sy = 0; + Block block = html.body; + int y = TOP_MARGIN; + int textPos = 0; + int centered = 0; + int maxH = clientSize.h - BOTTOM_MARGIN; + OldList leftObjects { }; + OldList rightObjects { }; + Font font; + Surface surface = display.GetSurface(0,0,null); + AlignedObject object, nextObject; + + if(html.defaultFont && html.defaultFont.font) + surface.TextFont(html.defaultFont.font.font); + + while(block) + { + Block nextBlock; + int nextTextPos; + int w, newH; + int left, right; + int x, maxW; + int thisLineCentered = centered; + bool changeLine; + + left = LEFT_MARGIN; + right = clientSize.w - RIGHT_MARGIN; + + for(object = leftObjects.last; object; object = nextObject) + { + nextObject = object.prev; + if(y < object.untilY || object.next) + left += object.w; + else + leftObjects.Delete(object); + } + for(object = rightObjects.last; object; object = nextObject) + { + nextObject = object.prev; + if(y < object.untilY || object.next) + right -= object.w; + else + rightObjects.Delete(object); + } + right = Max(left, right); + maxW = right - left; + + font = surface.font; + newH = ComputeLine(surface, block, textPos, &nextBlock, &nextTextPos, ¢ered, &w, maxW, maxH - y, RenderFlags {}, y, &leftObjects, &rightObjects, &changeLine, false, 0, 0); + surface.font = font; + if(thisLineCentered) + x = Max(left,(left + right - w) / 2); + else + x = left; + + surface.TextFont(font); + + if(findCaretLine(surface, x - scroll.x, y - scroll.y, maxW, newH, block, textPos, + nextBlock, nextTextPos, left - scroll.x, right - scroll.x, textBlock, pos, &sx, &sy)) + break; + + if(changeLine) + y += newH; + block = nextBlock; + textPos = nextTextPos; + } + delete surface; + + *psx = sx; + *psy = sy; + } + + // Temporary brute force clean up... + void clearEmptyBlocks() + { + Block block = html.body; + + while(block) + { + Block next = GetNextBlock(block); + + if(block.type == FONT && !block.subBlocks.first) + { + block.parent.subBlocks.Remove(block); + delete block; + } + else if(block.type == TEXT && block != textBlock && !block.textLen) + { + block.parent.subBlocks.Remove(block); + delete block; + } + block = next; + } + } + + void positionCaret(bool setCaretX) + { + if(edit && textBlock) + { + int sx, sy; + int th; + + clearEmptyBlocks(); + findCaretPos(textBlock, curPosition, &sx, &sy); + + display.FontExtent(textBlock.font.font, " ", 1, null, &th); + + if(setCaretX) + caretX = sx; + caretY = sy; + SetCaret(sx-1, sy, th); + { + Point scrollPos = scroll; + bool doScroll = false; + if(sy - scroll.y + th > clientSize.h) + { + scrollPos.y = sy + th - clientSize.h; + doScroll = true; + } + else if(sy - scroll.y < 0) + { + scrollPos.y = sy; + doScroll = true; + } + if(sx - scroll.x + 10 > clientSize.w) + { + scrollPos.x = sx + 10 - clientSize.w; + doScroll = true; + } + else if(sx - scroll.x < 10) + { + scrollPos.x = sx - 10; + doScroll = true; + } + if(doScroll) + scroll = scrollPos; + } + } + else + SetCaret(0,0,0); + } +} diff --git a/samples/guiAndGfx/EcereOffice/Writer/Writer.ec b/samples/guiAndGfx/EcereOffice/Writer/Writer.ec new file mode 100644 index 0000000000..6cf0e825b8 --- /dev/null +++ b/samples/guiAndGfx/EcereOffice/Writer/Writer.ec @@ -0,0 +1,231 @@ +import "ecere" + +import "RichEdit" + +static FileFilter ewdFilters[] = +{ + { "Ecere Writer Documents (*.ewd)", "ewd" }, + { "All files", null } +}; +static FileType ewdTypes[] = +{ + { "Ecere Writer Documents", "ewd", always } +}; + +FileDialog fileDialog { types = ewdTypes, sizeTypes = sizeof(ewdTypes), filters = ewdFilters, sizeFilters = sizeof(ewdFilters) }; + +static FileFilter htmlFilters[] = +{ + { "HTML Files (*.html)", "html" }, + { "All files", null } +}; +static FileType htmlTypes[] = +{ + { "HTML", "html", always } +}; +FileDialog htmlDialog { types = htmlTypes, sizeTypes = sizeof(htmlTypes), filters = htmlFilters, sizeFilters = sizeof(htmlFilters) }; + +class WriterDocument : Window +{ + displayDriver = "OpenGL"; + caption = "Ecere Writer"; + size = { 1024, 768 }; + state = maximized; + borderStyle = sizable; + background = darkGray; + hasClose = true; + hasMinimize = true; + hasMaximize = true; + hasMenuBar = true; + isDocument = true; + hasHorzScroll = true; + hasVertScroll = true; + scrollArea = { 840, 1140 }; + saveDialog = fileDialog; + + menu = { }; + Menu fileMenu { menu, "File", f }; + MenuItem newItem + { + fileMenu, "New", n, ctrlN; + + bool NotifySelect(MenuItem selection, Modifiers mods) + { + WriterDocument { }.Create(); + return true; + } + }; + MenuItem openItem + { + fileMenu, "Open", o, ctrlO; + + bool NotifySelect(MenuItem selection, Modifiers mods) + { + fileDialog.master = this; + fileDialog.type = open; + fileDialog.caption = "Open Document..."; + + if(fileDialog.Modal() == ok) + { + File f = FileOpenBuffered(fileDialog.filePath, read); + if(f) + { + fileName = fileDialog.filePath; + edit.loadDocument(f, fileDialog.filePath); + delete f; + } + } + return true; + } + }; + MenuItem closeItem { fileMenu, "Close", c, ctrlF4; NotifySelect = MenuFileExit }; // MenuFileClose is for MDI apps... + MenuDivider { fileMenu }; + MenuItem saveItem + { + fileMenu, "Save", s, ctrlS; + + bool NotifySelect(MenuItem selection, Modifiers mods) + { + fileDialog.master = this; + fileDialog.type = save; + fileDialog.caption = "Save Document..."; + + return MenuFileSave(selection, mods); + } + }; + MenuItem saveAsItem + { + fileMenu, "Save As...", a; + + bool NotifySelect(MenuItem selection, Modifiers mods) + { + fileDialog.master = this; + fileDialog.type = save; + fileDialog.caption = "Save Document..."; + + return MenuFileSaveAs(selection, mods); + } + }; + MenuDivider { fileMenu }; + MenuItem importHTMLItem + { + fileMenu, "Import from HTML...", i; + + bool NotifySelect(MenuItem selection, Modifiers mods) + { + htmlDialog.master = this; + htmlDialog.type = open; + htmlDialog.caption = "Import Document..."; + + if(htmlDialog.Modal() == ok) + { + File f = FileOpen(htmlDialog.filePath, read); + if(f) + { + /* + TempFile ewd { }; + if(html2ewd(f, ewd)) + { + ewd.Seek(0, start); + edit.loadDocument(ewd); + } + delete ewd; + */ + edit.loadHTML(f, htmlDialog.filePath); + delete f; + } + } + return true; + } + }; + MenuItem exportHTMLItem + { + fileMenu, "Export to HTML...", h; + + bool NotifySelect(MenuItem selection, Modifiers mods) + { + htmlDialog.master = this; + htmlDialog.type = save; + htmlDialog.caption = "Export Document..."; + + if(htmlDialog.Modal() == ok) + { + File f = FileOpen(htmlDialog.filePath, write); + if(f) + { + TempFile ewd { }; + if(edit.saveDocument(ewd)) + { + ewd.Seek(0, start); + ewd2html(ewd, f, true); + } + delete ewd; + delete f; + } + } + return true; + } + }; + MenuDivider { fileMenu }; + MenuItem exitItem + { + fileMenu, "Exit", x, altF4; + + bool NotifySelect(MenuItem selection, Modifiers mods) + { + ((GuiApplication)__thisModule.application).desktop.Destroy(0); + return true; + } + }; + + bool OnSaveFile(const char * fileName) + { + File f = FileOpen(fileName, write); + if(f) + { + edit.saveDocument(f); + + modifiedDocument = false; + delete f; + return true; + } + return false; + } + + Size pageSize { 800, 1100 }; + Box margins { 50, 50, 50, 50 }; + + RichEdit edit { this, size = { pageSize.w - margins.left - margins.right, pageSize.h - margins.top - margins.bottom } }; + + void OnResize(int width, int height) + { + Point o { Max(20,(clientSize.w - pageSize.w) / 2) + margins.left, Max(20, (clientSize.h - pageSize.h) / 2) + margins.top }; + edit.position = o; + } + + void OnRedraw(Surface surface) + { + Box sClip = surface.box; + Point o { Max(20,(clientSize.w - pageSize.w) / 2), Max(20, (clientSize.h - pageSize.h) / 2) }; + Box box { o.x - scroll.x, o.y - scroll.y }; + + box.right = box.left + pageSize.w - 1; + box.bottom = box.top + pageSize.h - 1; + box.Clip(surface.box); + + surface.foreground = lightGray; + surface.Rectangle(o.x + 1 - scroll.x, o.y + 1 - scroll.y, o.x + pageSize.w - scroll.x + 3, o.y + pageSize.h - scroll.y + 3); + surface.foreground = gray; + surface.Rectangle(o.x - scroll.x, o.y - scroll.y, o.x + pageSize.w - scroll.x + 2, o.y + pageSize.h - scroll.y + 2); + surface.Clip(box); + surface.background = white; + surface.Clear(colorBuffer); + + surface.Clip(sClip); + surface.foreground = black; + surface.Rectangle(o.x - 1 - scroll.x, o.y - 1 - scroll.y, o.x + pageSize.w - scroll.x, o.y + pageSize.h - scroll.y); + surface.Rectangle(o.x - 2 - scroll.x, o.y - 2 - scroll.y, o.x + pageSize.w - scroll.x + 1, o.y + pageSize.h - scroll.y + 1); + } +} + +WriterDocument writerWindow { }; diff --git a/samples/guiAndGfx/EcereOffice/Writer/Writer.epj b/samples/guiAndGfx/EcereOffice/Writer/Writer.epj new file mode 100644 index 0000000000..55819aad28 --- /dev/null +++ b/samples/guiAndGfx/EcereOffice/Writer/Writer.epj @@ -0,0 +1,61 @@ +{ + "Version" : 0.2, + "ModuleName" : "Writer", + "Options" : { + "Warnings" : "All", + "PreprocessorDefinitions" : [ + "IMPORT_STATIC=\"\"" + ], + "IncludeDirs" : [ + "$(ECERE_SDK_SRC)/ecere/src/gfx/drivers/gl3", + "$(ECERE_SDK_SRC)/ecere/src/gfx/newFonts/cc", + "$(ECERE_SDK_SRC)/butterbur/src/imagesAndText", + "$(ECERE_SDK_SRC)/deps/libtess" + ], + "TargetType" : "Executable", + "TargetFileName" : "Writer", + "Libraries" : [ + "ecere" + ] + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + }, + { + "Name" : "Release", + "Options" : { + "Debug" : false, + "Optimization" : "Speed", + "FastMath" : true + } + } + ], + "Files" : [ + { + "Folder" : "HTMLView", + "Files" : [ + "$(ECERE_SDK_SRC)/extras/html/htmlParser.ec", + "$(ECERE_SDK_SRC)/extras/html/HTMLView.ec", + "$(ECERE_SDK_SRC)/extras/html/lines.ec", + "$(ECERE_SDK_SRC)/extras/html/tables.ec" + ] + }, + "Writer.ec", + "$(ECERE_SDK_SRC)/ecere/src/gfx/newFonts/cc/ccstr.c", + "RichEdit.ec", + "ewd.ec" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/guiAndGfx/EcereOffice/Writer/convertDoc.ec b/samples/guiAndGfx/EcereOffice/Writer/convertDoc.ec new file mode 100644 index 0000000000..45b1a37155 --- /dev/null +++ b/samples/guiAndGfx/EcereOffice/Writer/convertDoc.ec @@ -0,0 +1,39 @@ +import "ecere" +import "ewd" + +class App : Application +{ + void Main() + { + char inExt[MAX_EXTENSION] = "html"; + char outExt[MAX_EXTENSION] = ""; + File in, out; + + if(argc > 1) + { + GetExtension(argv[1], inExt); + in = FileOpen(argv[1], read); + } + else + in = ConsoleFile { }; + + if(argc > 2) + { + GetExtension(argv[2], outExt); + out = FileOpen(argv[2], write); + } + else + out = ConsoleFile { }; + + if(in && out) + { + if(!strcmpi(inExt, "html") && (!outExt[0]|| !strcmpi(outExt, "ewd"))) + html2ewd(in, out); + else if(!strcmpi(inExt, "ewd") && (!outExt[0] || !strcmpi(outExt, "html"))) + ewd2html(in, out, true); + } + + delete in; + delete out; + } +} diff --git a/samples/guiAndGfx/EcereOffice/Writer/convertDoc.epj b/samples/guiAndGfx/EcereOffice/Writer/convertDoc.epj new file mode 100644 index 0000000000..b12088aaa0 --- /dev/null +++ b/samples/guiAndGfx/EcereOffice/Writer/convertDoc.epj @@ -0,0 +1,55 @@ +{ + "Version" : 0.2, + "ModuleName" : "convertDoc", + "Options" : { + "Warnings" : "All", + "IncludeDirs" : [ + "../../../../ecere/src/gfx/newFonts/cc" + ], + "TargetType" : "Executable", + "TargetFileName" : "convertDoc", + "Libraries" : [ + "ecere" + ], + "Console" : true + }, + "Configurations" : [ + { + "Name" : "Debug", + "Options" : { + "Debug" : true, + "Optimization" : "None", + "PreprocessorDefinitions" : [ + "_DEBUG" + ], + "FastMath" : false + } + }, + { + "Name" : "Release", + "Options" : { + "Debug" : false, + "Optimization" : "Speed", + "FastMath" : true + } + } + ], + "Files" : [ + { + "Folder" : "HTML", + "Files" : [ + "$(ECERE_SDK_SRC)/extras/html/htmlParser.ec", + "$(ECERE_SDK_SRC)/extras/html/lines.ec", + "$(ECERE_SDK_SRC)/extras/html/tables.ec", + "$(ECERE_SDK_SRC)/extras/html/HTMLView.ec" + ] + }, + "ewd.ec", + "$(ECERE_SDK_SRC)/ecere/src/gfx/newFonts/cc/ccstr.c", + "convertDoc.ec" + ], + "ResourcesPath" : "", + "Resources" : [ + + ] +} diff --git a/samples/guiAndGfx/EcereOffice/Writer/ewd.ec b/samples/guiAndGfx/EcereOffice/Writer/ewd.ec new file mode 100644 index 0000000000..f0637dfd75 --- /dev/null +++ b/samples/guiAndGfx/EcereOffice/Writer/ewd.ec @@ -0,0 +1,344 @@ +// ******** Ecere Writer Document format **************** + +import "ecere" +import "htmlParser" + +#include "ccstr.h" + +// Currently here for lack of another better low dependency place... +public int printDoubleDec(double value, int numDec, char * s, int size) +{ + int l = ccStrPrintDouble(s, size, numDec, value); + if(strchr(s, '.')) + { + while(l > 1 && (s[l-1] == '0' || s[l-1] == '.') && (s[l-2] == '.' || isdigit(s[l-2]))) + { + l--; + if(s[l] == '.') break; + } + s[l] = 0; + } + return l; +} + +bool html2ewd(File f, File out) +{ + bool result = false; + HTMLFile html { }; + + if(html.Parse(f)) + result = writeEWD(html, out); + delete html; + return result; +} + +bool ewd2html(File f, File out, bool htmlExport) +{ + bool result = true; + bool inFont = false; + bool bold = false, italic = false; + Color textColor = black; + char textColorHex[10]; + int fontSize = 3; + char face[256]; + bool fontChanged = !htmlExport; + bool italicWasOn = false; + bool boldWasOn = false; + char ch; + bool lastIsSpace = false; + + strcpy(face, "Times New Roman"); // TODO: make this a define? html.defaultFont.face); + + out.PrintLn(""); + + while(f.Getc(&ch)) + { + bool outputChar = false; + if(ch == '\\' || ch == '*' || ch == '_') + { + char control; + if(ch == '\\') + { + if(f.Getc(&control)) + { + switch(control) + { + case 'n': + out.PrintLn("
"); + break; + case 'f': + { + int i = 0; + bool quoted = false; + + f.Getc(&ch); + if(ch == '\"') + quoted = true; + else + f.Seek(-1, current); + + while(i < 255 && f.Getc(&ch)) + { + if((quoted && ch == '\"') || (!quoted && !isalnum(ch))) + { + if(!quoted) + f.Seek(-1, current); + break; + } + else + face[i++] = ch; + } + face[i] = 0; + + fontChanged = true; + break; + } + case 'c': + { + char color[7]; + int i = 0; + + while(i < 6 && f.Getc(&ch)) + { + if(isdigit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) + color[i++] = ch; + else + { + f.Seek(-1, current); + break; + } + } + color[i] = 0; + textColor = strtol(color, null, 16); + sprintf(textColorHex, "%06X", textColor); + + fontChanged = true; + break; + } + case 's': + { + char number[32]; + int i = 0; + double size; + + #define NUM_FONT_SIZES 7 + static float fontSizes[NUM_FONT_SIZES] = { 7.5f, 10, 12, 13.5f, 18, 24, 36 }; + + while(i < 31 && f.Getc(&ch)) + { + if(ch == '.' || isdigit(ch)) + number[i++] = ch; + else + { + f.Seek(-1, current); + break; + } + } + number[i] = 0; + size = strtod(number, null); + + for(i = 1; i <= NUM_FONT_SIZES; i++) + { + if(fabs(size - fontSizes[i-1]) < fabs(size - fontSizes[fontSize])) + fontSize = i; + } + + fontChanged = true; + break; + } + default: + ch = control; + outputChar = true; + break; + } + } + } + else if(ch == '*') + bold ^= true; + else if(ch == '_') + italic ^= true; + } + else + outputChar = true; + + if(outputChar) + { + if(ch != '\n') //!isspace(ch)) + { + if(fontChanged || (!htmlExport && (boldWasOn != bold || italicWasOn != italic))) + { + if(italicWasOn) out.Print(""); + if(boldWasOn) out.Print(""); + if(inFont) out.Print(""); + sprintf(textColorHex, "%06X", textColor); + out.Print(""); + inFont = true; + if(bold) out.Print(""); + if(italic) out.Print(""); + } + else if(italicWasOn != italic) + { + out.Print("<", italic ? "" : "/", "em>"); + } + else if(boldWasOn != bold) + { + if(italicWasOn) out.Print(""); + out.Print("<", bold ? "" : "/", "b>"); + if(italic) out.Print(""); + } + + fontChanged = false; + boldWasOn = bold; + italicWasOn = italic; + } + switch(ch) + { + case '<': out.Puts("<"); break; + case '>': out.Puts(">"); break; + case ' ': + if(lastIsSpace) + { + out.Puts(" "); + break; + } + else + lastIsSpace = true; + default: + out.Putc(ch); + } + if(ch != ' ') + lastIsSpace = false; + } + } + + if(italic) out.Print(""); + if(bold) out.Print(""); + if(inFont) out.Print(""); + + out.PrintLn("\n"); + return result; +} + +bool writeEWD(HTMLFile html, File f) +{ + bool result = true; + Block block = html.body; + Block font = html.defaultFont; + ColorAlpha color = black; + char face[256]; + double size = 10; + bool bold = false, italic = false; + + strcpy(face, font.face); + + while(block) + { + switch(block.type) + { + case FONT: + font = block; + break; + case BR: + f.Puts("\\n\n"); + break; + case TEXT: + { + const char * text = block.text; + + if(text[0]) + { + int i, start = 0; + + if(!(font.attribs & 2) && italic) + f.Puts("_"), italic = false; + if(!(font.attribs & 1) && bold) + f.Puts("*"), bold = false; + + if(strcmpi(font.face, face)) + { + f.Puts("\\f\""); + f.Puts(font.face); + f.Puts("\""); + strcpy(face, font.face); + } + if(font.size != size) + { + char tmp[32]; + f.Puts("\\s"); + printDoubleDec(font.size, 2, tmp, 32); + f.Puts(tmp); + size = font.size; + } + if(font.textColor != color) + { + f.Puts("\\c"); + color = font.textColor; + f.Printf("%06X", color.color); + } + if((font.attribs & 1) && !bold) + f.Puts("*"), bold = true; + if((font.attribs & 2) && !italic) + f.Puts("_"), italic = true; + + for(i = 0;; i++) + { + char ch = text[i]; + if(!ch || (ch == '\\' || ch == '*' || ch == '_')) + { + if(i > start) + { + f.Write(text + start, 1, i - start); + start = i+1; + } + if(!ch) + break; + else + { + f.Putc('\\'); + f.Putc(ch); + } + } + } + } + break; + } + } + + if(block.subBlocks.first) + block = block.subBlocks.first; + else + { + if(block.type == FONT) + { + font = block.parent; + while(font && font.type != FONT) font = font.parent; + if(!font) font = html.defaultFont; + } + + if(block.next) + block = block.next; + else + { + block = block.parent; + if(block && block.type == FONT) + { + font = block.parent; + while(font && font.type != FONT) font = font.parent; + if(!font) font = html.defaultFont; + } + while(block && !block.next) + { + if(block && block.type == FONT) + { + font = block.parent; + while(font && font.type != FONT) font = font.parent; + if(!font) font = html.defaultFont; + } + block = block.parent; + } + if(block) block = block.next; + } + } + } + return result; +} diff --git a/samples/guiAndGfx/EquaGraph/data/equa.equ b/samples/guiAndGfx/EquaGraph/data/equa.equ new file mode 100644 index 0000000000..bc1b62f1ad --- /dev/null +++ b/samples/guiAndGfx/EquaGraph/data/equa.equ @@ -0,0 +1,270 @@ +Equations in txt: 50 +Graphic color: B: 0 G: 207 C: 255 + +Models: 23 + +Name: "Straight Line" +Form: "Ax + By + C = 0" +Solutions: 1 +Method: "a=#\a*A\a+C\a/-B\a:a;" +Parameters: 3 +A range: L: -10, H: 10, S: 0.01, D: -1, E: "" +B range: L: -10, H: 10, S: 0.01, D: 1, E: "" +C range: L: -10, H: 10, S: 0.01, D: 0, E: "" +x range: L: -15, H: 15, S: 0.01, D: 0, E: "" +Additionals: 3 +Text: "Slope : ", Method: "a=-A\a/B\a:a;" +Text: "x intercept : ", Method: "a=-C\a/A\a:a;" +Text: "y intercept : ", Method: "a=-C\a/B\a:a;" + +Name: "Slope-Intercept Form" +Form: "y = A * x + B" +Solutions: 1 +Method: "a=#\a*A\a+B\a:a;" +Parameters: 2 +A range: L: -10, H: 10, S: 0.01, D: 1, E: "" +B range: L: -10, H: 10, S: 0.01, D: 0, E: "" +x range: L: -15, H: 15, S: 0.01, D: 0, E: "" +Additionals: 1 +Text: "x intercept : ", Method: "a=-B\a/A\a:a;" + +Name: "Intercept Form" +Form: "(x / A) + (y / B) = 1" +Solutions: 1 +Method: "a=#\a/A\b=1\b-a\b*B\a:b;" +Parameters: 2 +A range: L: -10, H: 10, S: 0.01, D: 1, E: "" +B range: L: -10, H: 10, S: 0.01, D: 1, E: "" +x range: L: -15, H: 15, S: 0.01, D: 0, E: "" +Additionals: 1 +Text: "Slope : ", Method: "a=-B\a/A\a:a;" + +Name: "Two-Point Form" +Form: "(y - A) / (x - B) = (C - A) / (D - B)" +Solutions: 1 +Method: "a=C\a-A\b=D\b-B\a/b\b=#\b-B\a*b\a+A\a:a;" +Parameters: 4 +A range: L: -10, H: 10, S: 0.01, D: 0, E: "" +B range: L: -10, H: 10, S: 0.01, D: 0, E: "" +C range: L: -10, H: 10, S: 0.01, D: 1, E: "" +D range: L: -10, H: 10, S: 0.01, D: 1, E: "" +x range: L: -15, H: 15, S: 0.01, D: 0, E: "" +Additionals: 3 +Text: "Slope : ", Method: "a=C\a-A\b=D\b-B\a/b\a:a;" +Text: "x intercept : ", Method: "a=C\a-A\b=D\b-B\a/b\c=a\a*B\b=A\b-a\b/-c\a:b;" +Text: "y intercept : ", Method: "a=C\a-A\b=D\b-B\a/b\a*B\b=A\b-a\a:b;" + +Name: "Quadratic Equation" +Form: "Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0" +Solutions: 2 +Method: "a=C\b=#\b*B\b+E\c=#\c*c\c*A\d=#\d*D\c+d\c+F\d=b\d*d\e=a\e*c\e*4\d-e\d__sqrt\a*2\e=-b\e+d\e/a\a:e\e=-b\e-d\e/a\b:e;" +Parameters: 6 +A range: L: -10, H: 10, S: 0.01, D: 1, E: "" +B range: L: -10, H: 10, S: 0.01, D: 1, E: "" +C range: L: -10, H: 10, S: 0.01, D: 1, E: "" +D range: L: -10, H: 10, S: 0.01, D: 1, E: "" +E range: L: -10, H: 10, S: 0.01, D: 6, E: "" +F range: L: -10, H: 10, S: 0.01, D: 1, E: "" +x range: L: -15, H: 15, S: 0.01, D: 0, E: "" +Additionals: 0 + +Name: "Circle" +Form: "(x - A)^2 + (y - B)^2 = C^2" +Solutions: 2 +Method: "a=#\a-A\a*a\b=C\b*b\b-a\b__sqrt\a=b\a+B\a:a\a=-b\a+B\b:a; +Parameters: 3 +A range: L: -10, H: 10, S: 0.01, D: 0, E: "" +B range: L: -10, H: 10, S: 0.01, D: 0, E: "" +C range: L: -10, H: 10, S: 0.01, D: 1, E: "" +x range: L: -15, H: 15, S: 0.01, D: 0, E: "" +Additionals: 0 + +Name: "Ellipse" +Form: "(x - A)^2 / B + (y - C)^2 / D = 1" +Solutions: 2 +Method: "a=#\a-A\a*a\a/B\a/B\b=1\b-a\b*D\b*D\b__sqrt\a=C\a+b\a:a\a=C\a-b\b:a; +Parameters: 4 +A range: L: -10, H: 10, S: 0.01, D: 0, E: "" +B range: L: -10, H: 10, S: 0.01, D: 2, E: "" +C range: L: -10, H: 10, S: 0.01, D: 0, E: "" +D range: L: -10, H: 10, S: 0.01, D: 1, E: "" +x range: L: -15, H: 15, S: 0.01, D: 0, E: "" +Additionals: 0 + +Name: "Parabola" +Form: "(x - A)^2 = 4*B(y - C)" +Solutions: 1 +Method: "a=#\a-A\a*a\a/B\a/4\a+C\a:a;" +Parameters: 3 +A range: L: -10, H: 10, S: 0.01, D: 0, E: "" +B range: L: -20, H: 20, S: 0.01, D: 1, E: "" +C range: L: -10, H: 10, S: 0.01, D: 0, E: "" +x range: L: -20, H: 20, S: 0.01, D: 0, E: "" +Additionals: 0 + +Name: "Hyperbola" +Form: "(x - A)^2 / B - (y - C)^2 / D = 1" +Solutions: 2 +Method: "a=#\a-A\a*a\a/B\a/B\b=1\b+a\b*D\b*D\b__sqrt\a=C\a+b\a:a\a=C\a-b\b:a; +Parameters: 4 +A range: L: -10, H: 10, S: 0.01, D: 0, E: "" +B range: L: -10, H: 10, S: 0.01, D: 2, E: "" +C range: L: -10, H: 10, S: 0.01, D: 0, E: "" +D range: L: -10, H: 10, S: 0.01, D: 1, E: "" +x range: L: -15, H: 15, S: 0.01, D: 0, E: "" +Additionals: 0 + +Name: "Exponential Curve" +Form: "y = A * B ^ (x - C) + D" +Solutions: 1 +Method: "a=#\a-C\b=B\b_a_pow\b*A\b+D\a:b;" +Parameters: 4 +A range: L: -4, H: 4, S: 0.01, D: 1, E: "" +B range: L: 0, H: 10, S: 0.01, D: 2, E: "0\1" +C range: L: -10, H: 10, S: 0.01, D: 0, E: "" +D range: L: -10, H: 10, S: 0.01, D: 0, E: "" +x range: L: -15, H: 15, S: 0.01, D: 0, E: "" +Additionals: 0 + +Name: "Logarithmic Curve" +Form: "y = A * log B C(x - D) + E" +Solutions: 1 +Method: "a=#\a-D\a*C\a_B_log\a*A\a+E\a:a;" +Parameters: 5 +A range: L: -10, H: 10, S: 0.01, D: 1, E: "" +B range: L: 0, H: 10, S: 0.01, D: 2, E: "0\1" +C range: L: -1, H: 1, S: 2, D: 1, E: "ñ" +D range: L: -10, H: 10, S: 0.01, D: 0, E: "" +E range: L: -10, H: 10, S: 0.01, D: 0, E: "" +x range: L: -15, H: 15, S: 0.01, D: 0, E: "" +Additionals: 0 + +Name: "Sine" +Form: "y = A * sin ( ( x - B ) * C ) + D" +Solutions: 1 +Method: "a=#\a-B\a*C\a__sin\a*A\a+D\a:a;" +Parameters: 4 +A range: L: -10, H: 10, S: 0.01, D: 2, E: "" +B range: L: -10, H: 10, S: 0.01, D: 0, E: "" +C range: L: -10, H: 10, S: 0.01, D: .5, E: "" +D range: L: -10, H: 10, S: 0.01, D: 0, E: "" +x range: L: -15, H: 15, S: 0.01, D: 0, E: "" +Additionals: 0 + +Name: "Cosine" +Form: "y = A * cos ( ( x - B ) * C ) + D" +Solutions: 1 +Method: "a=#\a-B\a*C\a__cos\a*A\a+D\a:a;" +Parameters: 4 +A range: L: -10, H: 10, S: 0.01, D: 2, E: "" +B range: L: -10, H: 10, S: 0.01, D: 0, E: "" +C range: L: -10, H: 10, S: 0.01, D: .5, E: "" +D range: L: -10, H: 10, S: 0.01, D: 0, E: "" +x range: L: -15, H: 15, S: 0.01, D: 0, E: "" +Additionals: 0 + +Name: "Tangent" +Form: "y = A * tan ( ( x - B ) * C ) + D" +Solutions: 1 +Method: "a=#\a-B\a*C\a__tan\a*A\a+D\a:a;" +Parameters: 4 +A range: L: -10, H: 10, S: 0.01, D: 2, E: "" +B range: L: -10, H: 10, S: 0.01, D: 0, E: "" +C range: L: -10, H: 10, S: 0.01, D: .5, E: "" +D range: L: -10, H: 10, S: 0.01, D: 0, E: "" +x range: L: -15, H: 15, S: 0.01, D: 0, E: "" +Additionals: 0 + +Name: "Projection (Hi vs Ho)" +Form: "y / B = x / A" +Solutions: 1 +Method: "a=#\a/A\a*B\a:a;" +Parameters: 2 +A range: L: -50, H: 50, S: 0.5, D: 2, E: "" +A range: L: -50, H: 50, S: 0.5, D: 2, E: "" +x range: L: -50, H: 50, S: 0.5, D: 0, E: "" +Additionals: 1 +Text: "G = ", Method: "a=B\a/A\a:a;" + +Name: "Shadow (Hshadow vs Hobject)" +Form: "y = A + B(x - A) / C" +Solutions: 1 +Method: "a=#\a-A\a*B\a/C\a+A\a:a;" +Parameters: 3 +A range: L: -50, H: 50, S: 0.5, D: 0, E: "" +B range: L: -50, H: 50, S: 0.5, D: 2, E: "" +C range: L: -50, H: 50, S: 0.5, D: 2, E: "" +x range: L: -50, H: 50, S: 0.5, D: 0, E: "" +Additionals: 1 +Text: "Hpen = ", Method: "a=B\a-C\a*A\a/C\a:a;" + +Name: "Descartes Parabolic Mirrors (q vs p)" +Form: "1/x + 1/y = 1/A" +Solutions: 1 +Method: "a=1\a/A\b=1\b/#\a-b\b=1\b/a\a:b;" +Parameters: 1 +A range: L: -250, H: 250, S: 0.01, D: 2, E: "" +x range: L: -50, H: 50, S: 0.01, D: 0, E: "" +Additionals: 2 +Text: "r = ", Method: "a=A\a*2\a:a;" +Text: "G = ", Method: "a=1\a/A\b=1\b/#\a-b\b=1\b/a\b/-#\a:b;" + +Name: "Descartes Parabolic Mirrors (q vs G)" +Form: "(-x + 1) / y = 1/A" +Solutions: 1 +Method: "a=-#\a+1\a*A\a:a;" +Parameters: 1 +A range: L: -250, H: 250, S: 0.01, D: 2, E: "" +x range: L: -50, H: 50, S: 0.01, D: 0, E: "" +Additionals: 2 +Text: "r = ", Method: "a=A\a*2\a:a;" +Text: "p = ", Method: "a=-#\a+1\a*A\b=-a\b/#\a:b;" + +Name: "Newton Parabolic Mirrors (li vs lo)" +Form: "xy = A^2" +Solutions: 1 +Method: "a=A\a*a\a/#\a:a;" +Parameters: 1 +A range: L: -50, H: 50, S: 0.01, D: 2, E: "" +x range: L: -50, H: 50, S: 0.01, D: 0, E: "" +Additionals: 2 +Text: "r = ", Method: "a=A\a*2\a:a;" +Text: "G = ", Method: "a=A\a/#\a:a;" + +Name: "Refraction (Ar vs Ai)" +Form: "A sin x = B sin y" +Solutions: 1 +Method: "a=#\a*3.1416\a/180\a__sin\a*A\a/B\a__asin\a*180\a/3.1416\a:a;" +Parameters: 2 +A range: L: -5, H: 5, S: 0.01, D: 1, E: "" +B range: L: -5, H: 5, S: 0.01, D: 1, E: "" +x range: L: 0, H: 90, S: 0.01, D: 45, E: "" +Additionals: 1 +Text: "n1->2 = ", Method: "a=B\a/A\a:a;" + +Name: "Pressure (p vs n)" +Form: "y * A = x * 8.31 * B" +Solutions: 1 +Method: "a=#\a*8.31\a*B\a/A\a:a;" +Parameters: 2 +A range: L: 0, H: 100, S: 0.1, D: 1, E: "" +B range: L: 0, H: 1000, S: 0.5, D: 273.15, E: "" +x range: L: 0, H: 100, S: 0.01, D: 1, E: "" +Additionals: 0 + +Name: "Devoir 1, #2 a)" +Form: "y = (x+3)/(x-4)" +Solutions: 1 +Method: "a=#\a+3\b=#\b-4\a/b\a:a;" +Parameters: 0 +x range: L: -100, H: 100, S: 0.01, D: 1, E: "" +Additionals: 0 + +Name: "Devoir 1, #2 b)" +Form: "y = 2x / (x^2 - 16)" +Solutions: 1 +Method: "a=#\a*2\b=#\b*b\b-16\a/b\a:a;" +Parameters: 0 +x range: L: -100, H: 100, S: 0.01, D: 1, E: "" +Additionals: 0 + diff --git a/samples/guiAndGfx/EquaGraph/data/equa.pcx b/samples/guiAndGfx/EquaGraph/data/equa.pcx new file mode 100644 index 0000000000..40d94f878e Binary files /dev/null and b/samples/guiAndGfx/EquaGraph/data/equa.pcx differ diff --git a/samples/guiAndGfx/EquaGraph/equa.ec b/samples/guiAndGfx/EquaGraph/equa.ec new file mode 100644 index 0000000000..b26bd02d1e --- /dev/null +++ b/samples/guiAndGfx/EquaGraph/equa.ec @@ -0,0 +1,575 @@ +import "ecere" + +import "equation" +import "fileUtils" +// import "war2Skin" + +define NUMSCROLLBARS = 27; + +ColorAlpha * palette; + +Bitmap pointer; +ScrollBar bars[NUMSCROLLBARS]; +ScrollBar scrollBar27; +bool needUpdate; +bool fullScreen = false; + +#define SCALE_X 20.0 +#define SCALE_Y 20.0 +#define XTS(x) ( x * SCALE_X * clientSize.w / 640 + clientSize.w/2) +#define YTS(y) (-y * SCALE_Y * clientSize.h / 480 + clientSize.h/2) + +struct Range +{ + float low, high, step, except[256], def; + uint16 numexcept; + bool sign; +}; + +define TXTNAME = "equalist.txt"; +define PCXNAME = "equagfx.pcx"; + +static bool splashScreen = true; + +struct Model +{ + char name[256]; + char form[256]; + char method[256]; + Range range[27]; + char carMethod[10][256]; + char carName[10][256]; + uint16 count,solutions,caracters; +}; + +static Model models[256]; +Equation equation; +Bitmap back, splash; +uint16 equCount=1, numModels=0, model=0; +byte cBack, cGraph, cCurve; + +bool EQUA_LoadModel(const char *filename) +{ + File f = FileOpen(filename, read); + if(f) + { + int i; + uint16 v,c,p,m; + char temp[256], str[256]; + + File_Find(f,"",true); + + File_GetINT(f,"Equations in txt:",&i); equCount=(uint16)i; + File_GetINT(f,"B:",&i); cBack=(byte)i; + File_GetINT(f,"G:",&i); cGraph=(byte)i; + File_GetINT(f,"C:",&i); cCurve=(byte)i; + File_GetINT(f,"Models:",&i); numModels=(uint16)i; + if(numModels>256)numModels=256; + + for(m=0; m26) i=26; models[m].solutions=(uint16)i; + File_GetSTR(f,"Method:",models[m].method, 256, true); + File_GetINT(f,"Parameters:",&i); models[m].count=(uint16)i; + for(v=0; v<=models[m].count; v++) + { + if(v==models[m].count) v=26; + File_GetFLT(f,"L:",&models[m].range[v].low); + File_GetFLT(f,"H:",&models[m].range[v].high); + File_GetFLT(f,"S:",&models[m].range[v].step); + File_GetFLT(f,"D:",&models[m].range[v].def); + File_GetSTR(f,"E:",temp,256, true); + models[m].range[v].sign=(temp[0]=='ñ'); + models[m].range[v].numexcept=0; + for(c=0; temp[c];) + { + for(p=0; ((temp[c]!='\\')&&temp[c]); p++, c++) + str[p]=temp[c]; + str[p]=0; if(temp[c]=='\\')c++; + models[m].range[v].except[models[m].range[v].numexcept++]=(float)strtod(str, null); + } + } + File_GetINT(f,"Additionals:",&i); models[m].caracters=(uint16)i; + if(models[m].caracters>10)models[m].caracters=10; + for(c=0; clow) equation.var[v]=range->low; + if(equation.var[v]>range->high) equation.var[v]=range->high; + while(!valid) + { + valid=true; + for(e=0; enumexcept; e++) + if(equation.var[v]==range->except[e]) + { + valid=false; + if(dir>0) + { + if(equation.var[v]high)equation.var[v]+=range->step; + else if(equation.var[v]>range->low)equation.var[v]-=range->step; + if(equation.var[v]==range->high) dir*=-1; + } + else + { + if(equation.var[v]>range->low)equation.var[v]-=range->step; + else if(equation.var[v]high)equation.var[v]+=range->step; + if(equation.var[v]==range->low) dir*=-1; + } + bars[v].Action(setPosition, (int)((equation.var[v]-range->low)/range->step), 0); + } + } +} + +void EQUA_SetEquation(bool rand) +{ + uint16 v; + Range *range; + float delta; + int num; + + for(v=0; v<=models[model].count; v++) + { + if(v==models[model].count) + v=26; + range=&models[model].range[v]; + delta=range->high-range->low; + num=(int)(delta/range->step+1); + bars[v].range = num; //1,num; + if(rand) + equation.var[v]=GetRandom(0, num-1)*range->step+range->low; + else + equation.var[v]=range->def; + EQUA_Valid(v, 1); + bars[v].Action(setPosition, (int)((equation.var[v]-range->low)/range->step), 0); + } + needUpdate = true; +} + +void EQUA_TextEquation(bool color, char *temp) +{ + uint16 t,c,v; + char ch, temp2[256]; + + for(t=0, c=0; models[model].form[c]; c++) + { + ch=models[model].form[c]; + if((ch>='A')&&(ch<='Z')) + { + if(color)temp[t++]='\004'; + if(models[model].range[ch-'A'].sign) + { + if(equation.var[ch-'A']<0) + sprintf(temp2,"-"); + else if(equation.var[ch-'A']>0) + temp2[0]=0; + else + sprintf(temp2,"0"); + } + else + sprintf(temp2,"%0.2f",equation.var[ch-'A']); + for(v=0; temp2[v]; v++) + temp[t++]=temp2[v]; + if(color)temp[t++]='\001'; + + } + else + temp[t++]=ch; + } + temp[t]=0; +} + +DisplaySystem lfbDS { }; + +class EquaGraph : Window +{ + caption = $"EquaGraph 1.01"; + tabCycle = true; + hasMaximize = true; + hasMinimize = true; + hasClose = true; + // borderStyle = sizable; + clientSize = { 640, 480 }; + + FontResource bigFont { "Arial", size = 15, window = this }; + FontResource mediumFont { "Arial", size = 11, window = this }; + + FontResource lfbVeryBigFont { "Arial", size = 20 }; + FontResource lfbMediumFont { "Arial", size = 11 }; + FontResource lfbSmallFont { "Arial", size = 6}; + + foreground = white; + + Timer splashTimer + { + this, started = true, delay = 2.5; + + bool DelayExpired() + { + disabled = false; + splashScreen = false; + Update(null); + return true; + } + }; + + EquaGraph() + { + int c; + + lfbDS.Create("LFB", null, false); + + lfbDS.LoadResource(lfbVeryBigFont); + lfbDS.LoadResource(lfbMediumFont); + lfbDS.LoadResource(lfbSmallFont); + + // Constants Scrollbars + for(c=0; cstep + range->low; + if(action == up || action == pageUp) + EQUA_Valid((uint16)var, -1); + else + EQUA_Valid((uint16)var, 1); + + needUpdate = true; + Update(null); + } + }; + incref bars[c]; + } + + // Equation Selector + scrollBar27 = ScrollBar + { + this, direction = horizontal, anchor = { top = 0 }, size = { 211, 0 }; + range = numModels; // 1, numModels + + void NotifyScrolling(ScrollBar scrollBar, ScrollBarAction action, int position, Key key) + { + if(model != position) + { + int c; + + model = (uint16)position; + EQUA_SetEquation(false); + for(c=0; c2.rgb", null); + if(!palette) + palette = GetDefaultPalette(); + // display.SetPalette(palette, true); + return true; + } + + bool OnStateChange(WindowState state, Modifiers mods) + { + if(state == maximized && (Key)mods == hotKey) // TOCHECK: + { + fullScreen = true; + app.SwitchMode(true, null, res640x480, pixelFormat8, 0, null, true); + // SetStyle(_class.style); + clientSize = { 640, 480 }; + anchor = { }; + return false; + } + return true; + } + + bool OnLoadGraphics() + { + Surface surface; + + if(splashScreen) + { + splash = { }; + if(!splash.Load(":equa.pcx", null, null)) + delete splash; + } + + // To run on Linux... + back = { }; + back.Allocate(null, 640,480, 0, pixelFormat888, true); + + // To run on 8 bit + // back.AllocateDD(displaySystem, 640, 480); + + if(back && back.palette) + memcpy(back.palette, palette, 256 * sizeof(ColorAlpha)); + + //Create Cursors + pointer = Bitmap { }; + if(!pointer.LoadT("<:war2skin>312.pcx", null, displaySystem)) + delete pointer; + surface = back.GetSurface(0,0, null); + if(surface) + { + if(splash) + surface.Blit(splash, 0, 0, 0,0, splash.width, splash.height); + + // surface.font = War2Skin_GetFont(window, 1); + surface.font = lfbVeryBigFont.font; + surface.foreground = white; + surface.WriteTextf(400,430, "EQUAGraph 1.01"); + // surface.font = War2Skin_GetFont(window, 4); + surface.font = lfbSmallFont.font; + surface.WriteTextf(400,470, "by Jerome St-Louis (1998)"); + delete surface; + } + delete splash; + + needUpdate = true; + return true; + } + + void OnUnloadGraphics() + { + pointer.Free(); + back.Free(); + } + + void OnDrawOverChildren(Surface surface) + { + if(splashScreen) + { + //Display + // surface.Stretch(back, 0,0, 0,0, window.cw, window.ch, back.width, back.height); + surface.Blit(back, 0,0, 0,0, back.width,back.height); + } + } + + void OnRedraw(Surface surface) + { + if(needUpdate && !splashScreen) + { + renderGraphic(); + needUpdate = false; + } + + if(!splashScreen) + { + int c; + float x,y; + + //Display + // surface.Stretch(back, 0,0, 0,0, window.cw, window.ch, back.width, back.height); + surface.Blit(back, 0,0, 0,0, back.width,back.height); + + //Show model name + // surface.font = War2Skin_GetFont(window, 3); + surface.font = bigFont.font; + surface.WriteText(10,10, models[model].name, strlen(models[model].name)); + + //Show parameters names and values + // surface.font = War2Skin_GetFont(window, 2); + surface.font = mediumFont.font; + for(c=0; c='a')&&(str[p]<='z')) + { + s=(uint16)(str[p]-'a'); if(s>25) return errorInMethod; + if(str[1]==':') + { + if(s==num) dest=y; else continue; + } + else + dest=&space[s]; + } + else if((str[p]>='A')&&(str[p]<='Z')) + { + s=(uint16)(str[p]-'A'); if(s>25) return errorInMethod; + dest=&var[s]; + } + p+=2; + + //Source if not a function + if(str[p]=='-') {neg=-1; p++;} else neg=1; + if(str[p]=='#') + source=x; + else if((str[p]>='a')&&(str[p]<='z')) + { + s=(uint16)(str[p]-'a'); if(s>25) return errorInMethod; + source=space[s]; + } + else if((str[p]>='A')&&(str[p]<='Z')) + { + s=(uint16)(str[p]-'A'); if(s>25) return errorInMethod; + source=var[s]; + } + else + source=(float)strtod(str+2, null); + switch(str[1]) + { + case ':': case '=': *dest =neg*source; break; + case '+': *dest+=neg*source; break; + case '-': *dest-=neg*source; break; + case '*': *dest*=neg*source; break; + case '/': + if(!source) return divideByZero; + *dest/=neg*source; break; + case '_': + fn=strstr(str+2,"_")+1; + //*************************************** + // MATHS FUNCTIONS // + //*************************************** + if(!strcmp(fn,"abs")) + { + *dest=(float)fabs(*dest); + } + if(!strcmp(fn,"mod")) + { + if(!source)return divideByZero; + *dest=(float)fmod(*dest, source); + } + if(!strcmp(fn,"pow")) + { + if(source==0.5) + { + if(*dest<0) return sqrtError; + *dest=(float)sqrt(*dest); + } + else + *dest=(float)pow(*dest,source); + } + if(!strcmp(fn,"sqrt")) + { + if(*dest<0) return sqrtError; + *dest=(float)sqrt(*dest); + } + if(!strcmp(fn,"log")) + { + if(*dest<=0) return logError; + if((source<=0)||(source==1)) + return logError; + *dest=(float)(log10(*dest)/log10(source)); + } + if(!strcmp(fn,"sin")) + { + *dest=(float)sin(*dest); + } + if(!strcmp(fn,"cos")) + { + *dest=(float)cos(*dest); + } + if(!strcmp(fn,"tan")) + { + *dest=(float)tan(*dest); + } + if(!strcmp(fn,"asin")) + { + if((*dest<-1)||(*dest>1)) return trigonometry; + *dest=(float)asin(*dest); + } + if(!strcmp(fn,"acos")) + { + if((*dest<-1)||(*dest>1)) return trigonometry; + *dest=(float)acos(*dest); + } + if(!strcmp(fn,"atan")) + { + *dest=(float)atan(*dest); + } + break; + } + if(method[c]==';') + { + if(Abs(*dest)>32767) return huge; else return solutionFound; + } + } + return methodNotComplete; + } + + void graph(Surface surface, char * method, uint16 numSolutions, float scaleX, float scaleY, byte graph, byte color, ColorAlpha * palette) + { + uint16 s; + float ex,ey; + float x,y; + + surface.SetForeground(palette[graph]); + surface.HLine(0,surface.width-1,surface.height/2); + surface.VLine(0,surface.height-1,surface.width/2); + for(x=scaleX; x=0)) + surface.PutPixel((int)x,(int)y); + } + } + } + } +}; diff --git a/samples/guiAndGfx/EquaGraph/fileUtils.ec b/samples/guiAndGfx/EquaGraph/fileUtils.ec new file mode 100644 index 0000000000..805da719e9 --- /dev/null +++ b/samples/guiAndGfx/EquaGraph/fileUtils.ec @@ -0,0 +1,144 @@ +import "ecere" + +// Numbers separated by commas, * makes rest of line a comment +char File_NextChar(File f) +{ + char ch; + while(!f.eof) + { + ch = ' '; + while(isspace(ch)) + f.Getc(&ch); + if(ch == '*') + while(ch != '\n') + f.Getc(&ch); + else + return ch; + } + return 0; +} + +int File_GetNumber(File f) +{ + char ch; + int sign=1; + int num; + + num=0; + if((ch=File_NextChar(f))=='-') + { + sign=-1; + ch=File_NextChar(f); + } + while (isdigit(ch)) + { + num=num*10+ch-'0'; + ch=File_NextChar(f); + } + return num*sign; +} + +// Seek for a string then return a value +static char line[256]=""; +static char *pointer=line; + +char * File_Find(File f, const char * string, bool reset) +{ + if(reset) { line[0]=0; pointer=line; return 0; } + if(!string) return null; + for(;;) + { + if(string[0]) + { + if(strstr(pointer, string)) + { + pointer=strstr(pointer, string)+strlen(string); + return pointer; + } + } + else if(pointer[0]) + return pointer; + pointer=line; + if(!f.GetLine(line, 256)) + return null; + } +} + +bool File_GetINT(File f, const char *string, int *data) +{ + uint16 c; + char * str; + *data = 0; + str=File_Find(f, string, 0); + if(!str) return 0; + for(c=0; str[c]; c++) + if(!isspace(str[c]) && str[c]!='\t') break; + if(!str[c]) return false; + if((str[c]!='-')&&(str[c]!='.')&&(!isdigit(str[c]))) + return false; + *data=atoi(str+c); + return true; +} + +bool File_GetFLT(File f, const char *string, float *data) +{ + uint16 c; + char * str; + *data = 0; + str=File_Find(f, string, 0); + if(!str) return 0; + for(c=0; str[c]; c++) + if(!isspace(str[c]) && str[c]!='\t') break; + if(!str[c]) return false; + if((str[c]!='-')&&(str[c]!='.')&&(!isdigit(str[c]))) + return false; + *data=(float)strtod(str+c, null); + return true; +} + +bool File_GetDBL(File f, const char *string, double *data) +{ + uint16 c; + char * str; + *data = 0; + str=File_Find(f, string, 0); + if(!str) return 0; + for(c=0; str[c]; c++) + if(!isspace(str[c]) && str[c]!='\t') break; + if(!str[c]) return false; + if((str[c]!='-')&&(str[c]!='.')&&(!isdigit(str[c]))) + return false; + *data = strtod(str+c, null); + return true; +} + +bool File_GetSTR(File f, const char *string, char *data, uint16 max, bool enclosed) +{ + uint16 c,i; + char * str; + + str=File_Find(f, string, 0); + if(!str) return 0; + if(enclosed) + { + for(c=0; str[c]; c++) + if(str[c] == '\"') + break; + } + else + { + for(c=0; str[c]; c++) + if(str[c]!=32 && str[c] != '\t') + break; + } + if(!str[c++]) return false; + for(i=0; i bootstrapping development environment ...") +# proc = subprocess.Popen(['make', '-j8'], shell=False) +# proc.communicate() + +#class BuildPyCommand(setuptools.command.build_py.build_py): +# """Custom build command.""" +# def run(self): +# self.run_command('make -j8') +# setuptools.command.build_py.build_py.run(self) + +class CustomInstallCommand(install): + """Customized setuptools install command.""" + def run(self): + print('error !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!') + proc = subprocess.Popen(['make', 'V=1', 'ECERE_AUDIO=y', '-j4'], shell=False) + proc.communicate() + if proc.returncode == 0: + install.run(self) + +#class TestConsole(TestCase): +# def test_basic(self): +# main() + +print(' -- before setup -- ') +print(__file__) +print('arg zero: ', sys.argv[0]) +print('count: ', len(sys.argv)) +print('args: ', str(sys.argv)) +print('version: ', str(sys.version)) + +#print(mods) + +#print(find_packages(where=pyDir)) + +#exit() + +# ['-c', 'install', '--record', '~/install-record.txt', '--single-version-externally-managed', '--compile'] + +if 'sdist' in sys.argv or 'build' in sys.argv or 'install' in sys.argv: + print('buildOk = buildAll()') + buildOk = buildAll() +else: + print('buildOk = True') + buildOk = True + +if not buildOk: + raise Exception('build failed error: not calling setup() -- abort!') +else: + if sys.platform == 'win32': + #libdir = path.join('..', '..', 'obj', 'win32', 'bin') + #bindir = path.join('..', '..', 'obj', 'win32', 'bin') + libdir = path.join('obj', 'win32', 'bin') + bindir = path.join('obj', 'win32', 'bin') + libpfx = '' + libext = '.dll' + exeext = '.exe' + elif sys.platform == 'linux' or sys.platform == 'linux2': + #libdir = path.join('..', '..', 'obj', 'linux', 'lib') + #bindir = path.join('..', '..', 'obj', 'linux', 'bin') + libdir = path.join(rwd, 'obj', 'linux', 'lib') + bindir = path.join(rwd, 'obj', 'linux', 'bin') + libpfx = 'lib' + libext = '.so' + exeext = '' + elif sys.platform == 'darwin': + libdir = path.join(rwd, 'obj', 'apple', 'lib') + bindir = path.join(rwd, 'obj', 'apple', 'bin') + libpfx = 'lib' + libext = '.dylib' + exeext = '' + else: + print('error: unexpected platform') + print(sys.platform) + print(os.name) + + #pyDir = path.join(rwd, 'bindings', 'py') + pyDir = path.join('bindings', 'py') + + from shutil import copyfile + if sys.argv[0] == 'setup.py': + if not 'install' in sys.argv: + #copyfile(path.join(pyDir, 'eC.py'), path.join('EcereSDK', 'eC.py')) + #copyfile(path.join(pyDir, 'ecere.py'), path.join('EcereSDK', 'ecere.py')) + #copyfile(path.join(pyDir, 'EDA.py'), path.join('EcereSDK', 'EDA.py')) + copyfile(path.join(pyDir, 'eC.py'), 'eC.py') + copyfile(path.join(pyDir, 'ecere.py'), 'ecere.py') + copyfile(path.join(pyDir, 'EDA.py'), 'EDA.py') + + bld_eC = path.join(pyDir, 'build_eC.py') + bld_ecere = path.join(pyDir, 'build_ecere.py') + bld_EDA = path.join(pyDir, 'build_EDA.py') + mods = [bld_eC + ':ffi_eC', bld_ecere + ':ffi_ecere', bld_EDA + ':ffi_EDA'] + #foundPackages = find_packages() + #foundPackages = [] + #mod_eC = path.join(pyDir, 'eC') + #mod_ecere = path.join(pyDir, 'ecere') + #mod_EDA = path.join(pyDir, 'EDA') + + #print('foundPackages:', foundPackages) + + setup( + name='EcereSDK', + version=version_from_git + '.dev17', + description='The Ecere Software Development Kit (SDK)', + long_description=long_description, + long_description_content_type='text/markdown', + url='https://ecere.org', + author='Jérôme Jacovella-St-Louis, Ecere Corporation', + author_email='jerome@ecere.com', + license='BSD 3-Clause (Revised)', + classifiers=[ + 'Development Status :: 4 - Beta', + 'Environment :: Console', + #'Environment :: Console :: Curses', + #'Environment :: Console :: Framebuffer', + 'Environment :: Handhelds/PDA\'s', + 'Environment :: MacOS X', + 'Environment :: No Input/Output (Daemon)', + 'Environment :: Web Environment', + 'Environment :: Win32 (MS Windows)', + 'Environment :: X11 Applications', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: Android', + #'Operating System :: BeOS', + #'Operating System :: iOS', + #'Operating System :: MacOS', + #'Operating System :: MacOS :: MacOS 9', + 'Operating System :: MacOS :: MacOS X', + #'Operating System :: Microsoft', + #'Operating System :: Microsoft :: MS-DOS', + #'Operating System :: Microsoft :: Windows', + 'Operating System :: Microsoft :: Windows :: Windows 10', + #'Operating System :: Microsoft :: Windows :: Windows 3.1 or Earlier', + 'Operating System :: Microsoft :: Windows :: Windows 7', + 'Operating System :: Microsoft :: Windows :: Windows 8', + 'Operating System :: Microsoft :: Windows :: Windows 8.1', + #'Operating System :: Microsoft :: Windows :: Windows 95/98/2000', + #'Operating System :: Microsoft :: Windows :: Windows CE', + #'Operating System :: Microsoft :: Windows :: Windows NT/2000', + 'Operating System :: Microsoft :: Windows :: Windows Server 2003', + 'Operating System :: Microsoft :: Windows :: Windows Server 2008', + #'Operating System :: Microsoft :: Windows :: Windows Vista', + #'Operating System :: Microsoft :: Windows :: Windows XP', + #'Operating System :: OS/2', + #'Operating System :: OS Independent', + #'Operating System :: Other OS', + #'Operating System :: PalmOS', + #'Operating System :: PDA Systems', + #'Operating System :: POSIX', + #'Operating System :: POSIX :: AIX', + 'Operating System :: POSIX :: BSD', + #'Operating System :: POSIX :: BSD :: BSD/OS', + #'Operating System :: POSIX :: BSD :: FreeBSD', + #'Operating System :: POSIX :: BSD :: NetBSD', + #'Operating System :: POSIX :: BSD :: OpenBSD', + #'Operating System :: POSIX :: GNU Hurd', + #'Operating System :: POSIX :: HP-UX', + #'Operating System :: POSIX :: IRIX', + 'Operating System :: POSIX :: Linux', + #'Operating System :: POSIX :: Other', + #'Operating System :: POSIX :: SCO', + #'Operating System :: POSIX :: SunOS/Solaris', + #'Operating System :: Unix', + 'Programming Language :: C', + #'Programming Language :: eC', + #'Programming Language :: Python :: 2', + #'Programming Language :: Python :: 3 :: Only', + 'Programming Language :: Python :: 3', + 'Topic :: Software Development', + 'Topic :: Software Development :: User Interfaces', + ], + keywords='cross-platform gui 2D 3D development', + #packages=['eceresdk'], + #packages=foundPackages, + #py_modules = [mod_eC, mod_ecere, mod_EDA], + #py_modules = ['_pyeC', '_pyecere', '_pyEDA'], + py_modules = ['eC', 'ecere', 'EDA'], + #package_dir={'':pyDir}, + package_dir={'':'.'}, + #cmdclass={'install': CustomInstallCommand}, + #cmdclass={ + #'pylint': PylintCommand, + #'build_py': BuildPyCommand, + #'build_py': BuildPyCommand, + # 'build': BuildCommand, + #}, + # include everything in source control + #include_package_data=True, + # ...but exclude README.txt from all packages + #exclude_package_data={'': ['.gitattributes', '.gitignore', '.mailmap', '.travis.yml', '.appveyor.yml', '*.3ds', '*.3DS', 'samples']}, + + #python_requires='>2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', + python_requires='>=3.4', + #setup_requires=['setuptools_git >= 0.3', 'cffi >= 1.11.5'], # 1.0.0 1.11.5 1.6.0 + setup_requires=['pip >= 18.0', 'wheel', 'cffi >= 1.0.0', 'setuptools-git >= 1.0'], + cffi_modules=mods, + #cffi_modules=[path.join('bindings', 'py', 'build_eC.py:ffi_eC')], + #cffi_modules=['build_eC.py:ffi_eC', 'build_ecere.py:ffi_ecere'], + install_requires=['cffi >= 1.0.0'], + #extras_require={ + # 'dev': ['check-manifest'], + # 'test': ['coverage'], + #}, + package_data={ + # 'sample': ['package_data.dat'], + }, + data_files=[ + ('', [ + os.path.join(libdir, libpfx + 'ecereCOM' + libext), + os.path.join(libdir, libpfx + 'ecereCOM_c' + libext), + os.path.join(libdir, libpfx + 'ecere' + libext), + os.path.join(libdir, libpfx + 'ecere_c' + libext), + os.path.join(libdir, libpfx + 'ec' + libext), + os.path.join(libdir, libpfx + 'ec2' + libext), + os.path.join(libdir, libpfx + 'EcereAudio' + libext), + os.path.join(libdir, libpfx + 'EDA' + libext), + os.path.join(libdir, libpfx + 'EDA_c' + libext), + os.path.join(libdir, libpfx + 'EDASQLite' + libext), + #os.path.join(libdir, libpfx + 'EDASQLiteCipher' + libext), + os.path.join(bindir, 'ecp' + exeext), + os.path.join(bindir, 'ecc' + exeext), + os.path.join(bindir, 'ecs' + exeext), + os.path.join(bindir, 'ear' + exeext), + os.path.join(bindir, 'epj2make' + exeext), + os.path.join(bindir, 'bgen' + exeext), + os.path.join(bindir, 'documentor' + exeext), + os.path.join(bindir, 'ecere-ide' + exeext), + ]) + ], + #entry_points={ + # 'console_scripts': [ + # #'build_eC=build_eC:compile_eC', + # 'sample=sample:main', + # ], + #}, + )