summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPiotr Dziwiński <piotrdz@gmail.com>2012-08-12 10:15:19 -0700
committerPiotr Dziwiński <piotrdz@gmail.com>2012-08-12 10:15:19 -0700
commit50deedb6cbc927f6c7d6c3c5e9134d11c20782ae (patch)
tree8eb3fbd914cfea11660534b296bfddcffcc5f7b4
parentbc24b9f9e516e657fcc0034808e010287fc2e393 (diff)
parent62b545128f37746c343760e464872655b5523c6f (diff)
downloadcolobot-50deedb6cbc927f6c7d6c3c5e9134d11c20782ae.tar.gz
colobot-50deedb6cbc927f6c7d6c3c5e9134d11c20782ae.tar.bz2
colobot-50deedb6cbc927f6c7d6c3c5e9134d11c20782ae.zip
Merge pull request #36 from Erihel/dev
Merge of new features: plugins and INI profile, changes from dev-graphics
-rw-r--r--CMakeLists.txt3
-rw-r--r--Doxyfile1670
-rw-r--r--lib/simpleini/ConvertUTF.c539
-rw-r--r--lib/simpleini/ConvertUTF.h149
-rw-r--r--lib/simpleini/Makefile28
-rw-r--r--lib/simpleini/SimpleIni.h3370
-rw-r--r--lib/simpleini/SimpleIni.sln29
-rw-r--r--lib/simpleini/SimpleIni.vcproj291
-rw-r--r--lib/simpleini/ini.syn36
-rw-r--r--lib/simpleini/package.cmd26
-rw-r--r--lib/simpleini/simpleini.doxy1321
-rw-r--r--lib/simpleini/simpleini.dsp178
-rw-r--r--lib/simpleini/simpleini.dsw29
-rw-r--r--lib/simpleini/snippets.cpp123
-rw-r--r--lib/simpleini/test.cmd24
-rw-r--r--lib/simpleini/test1-expected.ini85
-rw-r--r--lib/simpleini/test1-input.ini76
-rw-r--r--lib/simpleini/test1.cpp166
-rw-r--r--lib/simpleini/testsi-EUCJP.ini52
-rw-r--r--lib/simpleini/testsi-SJIS.ini51
-rw-r--r--lib/simpleini/testsi-UTF8.ini50
-rw-r--r--lib/simpleini/testsi.cpp309
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/app/README.txt7
-rw-r--r--src/app/app.cpp98
-rw-r--r--src/app/app.h18
-rw-r--r--src/app/main.cpp5
-rw-r--r--src/app/system.h9
-rw-r--r--src/app/system_linux.h9
-rw-r--r--src/app/system_other.h9
-rw-r--r--src/app/system_windows.h9
-rw-r--r--src/common/README.txt7
-rw-r--r--src/common/event.h63
-rw-r--r--src/common/iman.cpp15
-rw-r--r--src/common/iman.h8
-rw-r--r--src/common/logger.cpp16
-rw-r--r--src/common/logger.h16
-rw-r--r--src/common/profile.cpp111
-rw-r--r--src/common/profile.h98
-rw-r--r--src/common/stringutils.cpp10
-rw-r--r--src/common/test/CMakeLists.txt8
-rw-r--r--src/common/test/colobot.ini15
-rw-r--r--src/common/test/profile_test.cpp43
-rw-r--r--src/graphics/README.txt13
-rw-r--r--src/graphics/core/README.txt13
-rw-r--r--src/graphics/core/color.h11
-rw-r--r--src/graphics/core/device.h27
-rw-r--r--src/graphics/core/light.h5
-rw-r--r--src/graphics/core/material.h5
-rw-r--r--src/graphics/core/texture.h9
-rw-r--r--src/graphics/core/vertex.h5
-rw-r--r--src/graphics/d3d/README.txt7
-rw-r--r--src/graphics/engine/README.txt17
-rw-r--r--src/graphics/engine/camera.cpp81
-rw-r--r--src/graphics/engine/camera.h9
-rw-r--r--src/graphics/engine/cloud.cpp262
-rw-r--r--src/graphics/engine/cloud.h83
-rw-r--r--src/graphics/engine/engine.cpp2718
-rw-r--r--src/graphics/engine/engine.h613
-rw-r--r--src/graphics/engine/lightman.h5
-rw-r--r--src/graphics/engine/lightning.cpp68
-rw-r--r--src/graphics/engine/lightning.h7
-rw-r--r--src/graphics/engine/modelfile.h5
-rw-r--r--src/graphics/engine/particle.cpp281
-rw-r--r--src/graphics/engine/particle.h98
-rw-r--r--src/graphics/engine/planet.cpp164
-rw-r--r--src/graphics/engine/planet.h77
-rw-r--r--src/graphics/engine/pyro.cpp158
-rw-r--r--src/graphics/engine/pyro.h56
-rw-r--r--src/graphics/engine/terrain.cpp1802
-rw-r--r--src/graphics/engine/terrain.h263
-rw-r--r--src/graphics/engine/text.cpp781
-rw-r--r--src/graphics/engine/text.h284
-rw-r--r--src/graphics/engine/water.cpp635
-rw-r--r--src/graphics/engine/water.h152
-rw-r--r--src/graphics/opengl/README.txt13
-rw-r--r--src/graphics/opengl/gldevice.cpp58
-rw-r--r--src/graphics/opengl/gldevice.h12
-rw-r--r--src/math/README.txt13
-rw-r--r--src/math/all.h9
-rw-r--r--src/math/const.h18
-rw-r--r--src/math/func.h29
-rw-r--r--src/math/geometry.h15
-rw-r--r--src/math/intpoint.h18
-rw-r--r--src/math/intsize.h61
-rw-r--r--src/math/matrix.h24
-rw-r--r--src/math/point.h9
-rw-r--r--src/math/size.h66
-rw-r--r--src/math/vector.h9
-rw-r--r--src/object/README.txt10
-rw-r--r--src/object/object.h229
-rw-r--r--src/object/robotmain.cpp2
-rw-r--r--src/physics/README.txt7
-rw-r--r--src/physics/physics.h82
-rw-r--r--src/plugins/plugininterface.h65
-rw-r--r--src/plugins/pluginloader.cpp126
-rw-r--r--src/plugins/pluginloader.h88
-rw-r--r--src/plugins/pluginmanager.cpp132
-rw-r--r--src/plugins/pluginmanager.h88
-rw-r--r--src/plugins/test/CMakeLists.txt12
-rw-r--r--src/plugins/test/colobot.ini3
-rw-r--r--src/plugins/test/manager_test.cpp31
-rw-r--r--src/sound/README.txt7
-rw-r--r--src/sound/plugins/oalsound/CMakeLists.txt24
-rw-r--r--src/sound/plugins/oalsound/alsound.cpp553
-rw-r--r--src/sound/plugins/oalsound/alsound.h95
-rw-r--r--src/sound/plugins/oalsound/buffer.cpp80
-rw-r--r--src/sound/plugins/oalsound/buffer.h48
-rw-r--r--src/sound/plugins/oalsound/channel.cpp304
-rw-r--r--src/sound/plugins/oalsound/channel.h99
-rw-r--r--src/sound/plugins/oalsound/check.h (renamed from src/plugins/plugin.h)26
-rw-r--r--src/sound/plugins/oalsound/test/CMakeLists.txt11
-rw-r--r--src/sound/plugins/oalsound/test/plugin_test.cpp40
-rw-r--r--src/sound/sound.h13
-rw-r--r--src/ui/README.txt7
115 files changed, 18090 insertions, 2342 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 98e9732..4b7363b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,6 +8,7 @@ project(colobot C CXX)
find_package(OpenGL 1.4 REQUIRED)
find_package(SDL 1.2.10 REQUIRED)
find_package(SDL_image 1.2 REQUIRED)
+find_package(SDL_ttf 2.0 REQUIRED)
find_package(PNG 1.2 REQUIRED)
# GLEW requirement depends on platform
@@ -30,4 +31,4 @@ set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wold-style-cast -std=gnu++0x")
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${colobot_SOURCE_DIR}/cmake")
# Subdirectory with sources
-add_subdirectory(src bin)
+add_subdirectory(src bin lib)
diff --git a/Doxyfile b/Doxyfile
index 9e4fe82..cdbf278 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -1,103 +1,103 @@
-# Doxyfile 1.8.0
+# Doxyfile 1.8.1.2
# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
+# doxygen (www.doxygen.org) for a project.
#
-# All text after a hash (#) is considered a comment and will be ignored
+# All text after a hash (#) is considered a comment and will be ignored.
# The format is:
# TAG = value [value, ...]
# For lists items can also be appended using:
# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
+# Values that contain spaces should be placed between quotes (" ").
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
# http://www.gnu.org/software/libiconv for the list of possible encodings.
DOXYFILE_ENCODING = UTF-8
-# The PROJECT_NAME tag is a single word (or sequence of words) that should
-# identify the project. Note that if you do not use Doxywizard you need
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
# to put quotes around the project name if it contains spaces.
PROJECT_NAME = Colobot
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
# if some version control system is used.
-PROJECT_NUMBER =
+PROJECT_NUMBER =
-# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
# a quick idea about the purpose of the project. Keep the description short.
-PROJECT_BRIEF =
+PROJECT_BRIEF =
-# With the PROJECT_LOGO tag one can specify an logo or icon that is
-# included in the documentation. The maximum height of the logo should not
-# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
# Doxygen will copy the logo to the output directory.
-PROJECT_LOGO =
+PROJECT_LOGO =
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
OUTPUT_DIRECTORY = "./doc"
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
# otherwise cause performance problems for the file system.
CREATE_SUBDIRS = NO
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
OUTPUT_LANGUAGE = English
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
# Set to NO to disable this.
BRIEF_MEMBER_DESC = YES
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
REPEAT_BRIEF = YES
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
# "represents" "a" "an" "the"
ABBREVIATE_BRIEF = "The $name class" \
@@ -112,246 +112,246 @@ ABBREVIATE_BRIEF = "The $name class" \
an \
the
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
# description.
ALWAYS_DETAILED_SEC = NO
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
INLINE_INHERITED_MEMB = NO
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
# to NO the shortest path that makes the file name unique will be used.
FULL_PATH_NAMES = YES
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
# path to strip.
-STRIP_FROM_PATH =
+STRIP_FROM_PATH =
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
# are normally passed to the compiler using the -I flag.
-STRIP_FROM_INC_PATH =
+STRIP_FROM_INC_PATH =
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful if your file system
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
# doesn't support long names like on DOS, Mac, or CD-ROM.
SHORT_NAMES = NO
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
# (thus requiring an explicit @brief command for a brief description.)
JAVADOC_AUTOBRIEF = NO
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
# an explicit \brief command for a brief description.)
QT_AUTOBRIEF = NO
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
# description. Set this tag to YES if you prefer the old behaviour instead.
MULTILINE_CPP_IS_BRIEF = NO
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
# re-implements.
INHERIT_DOCS = YES
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
# be part of the file/class/namespace that contains it.
SEPARATE_MEMBER_PAGES = NO
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
# Doxygen uses this value to replace tabs by spaces in code fragments.
TAB_SIZE = 8
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
# You can put \n's in the value part of an alias to insert newlines.
-ALIASES =
+ALIASES =
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding
-# "class=itcl::class" will allow you to use the command class in the
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
# itcl::class meaning.
-TCL_SUBST =
+TCL_SUBST =
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
# of all members will be omitted, etc.
OPTIMIZE_OUTPUT_FOR_C = NO
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
# scopes will look different, etc.
OPTIMIZE_OUTPUT_JAVA = NO
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
# Fortran.
OPTIMIZE_FOR_FORTRAN = NO
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
# VHDL.
OPTIMIZE_OUTPUT_VHDL = NO
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this
-# tag. The format is ext=language, where ext is a file extension, and language
-# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
-# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
-# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
-# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
-EXTENSION_MAPPING =
+EXTENSION_MAPPING =
-# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
-# comments according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
-# The output of markdown processing is further processed by doxygen, so you
-# can mix doxygen, HTML, and XML commands with Markdown formatting.
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
# Disable only in case of backward compatibilities issues.
MARKDOWN_SUPPORT = YES
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also makes the inheritance and collaboration
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
BUILTIN_STL_SUPPORT = NO
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
CPP_CLI_SUPPORT = NO
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
# instead of private inheritance when no explicit protection keyword is present.
SIP_SUPPORT = NO
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
# methods anyway, you should set this option to NO.
IDL_PROPERTY_SUPPORT = YES
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
DISTRIBUTE_GROUP_DOC = NO
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
# the \nosubgrouping command.
SUBGROUPING = YES
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
-# unions are shown inside the group in which they are included (e.g. using
-# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
# section (for LaTeX and RTF).
INLINE_GROUPED_CLASSES = NO
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
-# unions with only public data fields will be shown inline in the documentation
-# of the scope in which they are defined (i.e. file, namespace, or group
-# documentation), provided this scope is documented. If set to NO (the default),
-# structs, classes, and unions are shown on a separate page (for HTML and Man
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
# pages) or section (for LaTeX and RTF).
INLINE_SIMPLE_STRUCTS = NO
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
# types are typedef'ed and only the typedef is referenced, never the tag name.
TYPEDEF_HIDES_STRUCT = NO
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penalty.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will roughly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
# corresponding to a cache size of 2^16 = 65536 symbols.
SYMBOL_CACHE_SIZE = 0
-# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
-# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
-# their name and scope. Since this can be an expensive process and often the
-# same symbol appear multiple times in the code, doxygen keeps a cache of
-# pre-resolved symbols. If the cache is too small doxygen will become slower.
-# If the cache is too large, memory is wasted. The cache size is given by this
-# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
# corresponding to a cache size of 2^16 = 65536 symbols.
LOOKUP_CACHE_SIZE = 0
@@ -360,484 +360,485 @@ LOOKUP_CACHE_SIZE = 0
# Build related configuration options
#---------------------------------------------------------------------------
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
EXTRACT_ALL = NO
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
# will be included in the documentation.
EXTRACT_PRIVATE = NO
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
-# scope will be included in the documentation.
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation.
EXTRACT_PACKAGE = NO
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
# will be included in the documentation.
EXTRACT_STATIC = NO
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
# If set to NO only classes defined in header files are included.
EXTRACT_LOCAL_CLASSES = YES
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
# If set to NO (the default) only methods in the interface are included.
EXTRACT_LOCAL_METHODS = NO
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
# anonymous namespaces are hidden.
EXTRACT_ANON_NSPACES = NO
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
# This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_MEMBERS = NO
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
# overviews. This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_CLASSES = NO
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
# documentation.
HIDE_FRIEND_COMPOUNDS = NO
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
# function's detailed documentation block.
HIDE_IN_BODY_DOCS = NO
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
# Set it to YES to include the internal documentation.
INTERNAL_DOCS = NO
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
# and Mac users are advised to set this option to NO.
CASE_SENSE_NAMES = NO
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
# documentation. If set to YES the scope will be hidden.
HIDE_SCOPE_NAMES = NO
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
# of that file.
SHOW_INCLUDE_FILES = YES
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
# rather than with sharp brackets.
FORCE_LOCAL_INCLUDES = NO
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
# is inserted in the documentation for inline members.
INLINE_INFO = YES
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
# declaration order.
SORT_MEMBER_DOCS = YES
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
# declaration order.
SORT_BRIEF_DOCS = NO
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
-# will sort the (brief and detailed) documentation of class members so that
-# constructors and destructors are listed first. If set to NO (the default)
-# the constructors will appear in the respective orders defined by
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
SORT_MEMBERS_CTORS_1ST = NO
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
# the group names will appear in their defined order.
SORT_GROUP_NAMES = NO
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
# alphabetical list.
SORT_BY_SCOPE_NAME = NO
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
-# do proper type resolution of all parameters of a function it will reject a
-# match between the prototype and the implementation of a member function even
-# if there is only one candidate or it is obvious which candidate to choose
-# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
# will still accept a match between prototype and implementation in such cases.
STRICT_PROTO_MATCHING = NO
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
# commands in the documentation.
GENERATE_TODOLIST = YES
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
# commands in the documentation.
GENERATE_TESTLIST = YES
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
# commands in the documentation.
GENERATE_BUGLIST = YES
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
# \deprecated commands in the documentation.
GENERATE_DEPRECATEDLIST= YES
-# The ENABLED_SECTIONS tag can be used to enable conditional
+# The ENABLED_SECTIONS tag can be used to enable conditional
# documentation sections, marked by \if sectionname ... \endif.
-ENABLED_SECTIONS =
+ENABLED_SECTIONS =
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or macro consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and macros in the
-# documentation can be controlled using \showinitializer or \hideinitializer
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
# command in the documentation regardless of this setting.
MAX_INITIALIZER_LINES = 30
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
# list will mention the files that were used to generate the documentation.
SHOW_USED_FILES = YES
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES = NO
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
# Folder Tree View (if specified). The default is YES.
SHOW_FILES = YES
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page. This will remove the Namespaces entry from the Quick Index
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
# and from the Folder Tree View (if specified). The default is YES.
SHOW_NAMESPACES = YES
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
# is used as the file version. See the manual for examples.
-FILE_VERSION_FILTER =
+FILE_VERSION_FILTER =
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. The create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option.
-# You can optionally specify a file name after the option, if omitted
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
# DoxygenLayout.xml will be used as the name of the layout file.
-LAYOUT_FILE =
+LAYOUT_FILE =
-# The CITE_BIB_FILES tag can be used to specify one or more bib files
-# containing the references data. This must be a list of .bib files. The
-# .bib extension is automatically appended if omitted. Using this command
-# requires the bibtex tool to be installed. See also
-# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
-# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
# feature you need bibtex and perl available in the search path.
-CITE_BIB_FILES =
+CITE_BIB_FILES =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
-# The QUIET tag can be used to turn on/off the messages that are generated
+# The QUIET tag can be used to turn on/off the messages that are generated
# by doxygen. Possible values are YES and NO. If left blank NO is used.
QUIET = NO
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
# NO is used.
WARNINGS = YES
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
# automatically be disabled.
-WARN_IF_UNDOCUMENTED = YES
+WARN_IF_UNDOCUMENTED = NO
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
# don't exist or using markup commands wrongly.
WARN_IF_DOC_ERROR = YES
-# The WARN_NO_PARAMDOC option can be enabled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
# documentation.
WARN_NO_PARAMDOC = NO
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
# be obtained via FILE_VERSION_FILTER)
WARN_FORMAT = "$file:$line: $text"
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
# to stderr.
-WARN_LOGFILE =
+WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = "src" "src/CBot" "src/doc"
+INPUT = "src" \
+ "src/CBot"
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
# the list of possible encodings.
INPUT_ENCODING = UTF-8
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
-# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
# *.f90 *.f *.for *.vhd *.vhdl
FILE_PATTERNS = *.h \
- *.cpp
+ *.cpp \
+ README.txt
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used.
RECURSIVE = YES
-# The EXCLUDE tag can be used to specify files and/or directories that should be
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-# Note that relative paths are relative to the directory from which doxygen is
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
# run.
-EXCLUDE = "src/old" "src/metafile"
+EXCLUDE = "src/old" \
+ "src/metafile"
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
-# directories that are symbolic links (a Unix file system feature) are excluded
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
EXCLUDE_SYMLINKS = NO
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
# for example use the pattern */test/*
-EXCLUDE_PATTERNS =
+EXCLUDE_PATTERNS =
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
# AClass::ANamespace, ANamespace::*Test
-EXCLUDE_SYMBOLS =
+EXCLUDE_SYMBOLS =
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
# the \include command).
-EXAMPLE_PATH =
+EXAMPLE_PATH =
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
EXAMPLE_PATTERNS = *
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
# Possible values are YES and NO. If left blank NO is used.
EXAMPLE_RECURSIVE = NO
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
# the \image command).
-IMAGE_PATH =
+IMAGE_PATH =
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
# ignored.
-INPUT_FILTER =
+INPUT_FILTER =
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis. Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match. The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty or if
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
# non of the patterns match the file name, INPUT_FILTER is applied.
-FILTER_PATTERNS =
+FILTER_PATTERNS =
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
# files to browse (i.e. when SOURCE_BROWSER is set to YES).
FILTER_SOURCE_FILES = NO
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
-# and it is also possible to disable source filtering for a specific pattern
-# using *.ext= (so without naming a filter). This option only has effect when
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
# FILTER_SOURCE_FILES is enabled.
-FILTER_SOURCE_PATTERNS =
+FILTER_SOURCE_PATTERNS =
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
# VERBATIM_HEADERS is set to NO.
SOURCE_BROWSER = NO
-# Setting the INLINE_SOURCES tag to YES will include the body
+# Setting the INLINE_SOURCES tag to YES will include the body
# of functions and classes directly in the documentation.
INLINE_SOURCES = NO
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C, C++ and Fortran comments will always remain visible.
STRIP_CODE_COMMENTS = YES
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
# functions referencing it will be listed.
REFERENCED_BY_RELATION = NO
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
# called/used by that function will be listed.
REFERENCES_RELATION = NO
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code. Otherwise they will link to the documentation.
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
REFERENCES_LINK_SOURCE = YES
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
# will need version 4.8.6 or higher.
USE_HTAGS = NO
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
# which an include is specified. Set to NO to disable this.
VERBATIM_HEADERS = YES
@@ -846,157 +847,160 @@ VERBATIM_HEADERS = YES
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
# contains a lot of classes, structs, unions or interfaces.
ALPHABETICAL_INDEX = YES
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
# in which this list will be split (can be a number in the range [1..20])
COLS_IN_ALPHA_INDEX = 5
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
# should be ignored while generating the index headers.
-IGNORE_PREFIX =
+IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
# generate HTML output.
GENERATE_HTML = YES
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `html' will be used as the default path.
HTML_OUTPUT = html
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
# doxygen will generate files with .html extension.
HTML_FILE_EXTENSION = .html
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header. Note that when using a custom header you are responsible
-# for the proper inclusion of any scripts and style sheets that doxygen
-# needs, which is dependent on the configuration options used.
-# It is advised to generate a default header using "doxygen -w html
-# header.html footer.html stylesheet.css YourConfigFile" and then modify
-# that header. Note that the header is subject to change so you typically
-# have to redo this when upgrading to a newer version of doxygen or when
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
# changing the value of configuration settings such as GENERATE_TREEVIEW!
-HTML_HEADER =
+HTML_HEADER =
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
# standard footer.
-HTML_FOOTER =
+HTML_FOOTER =
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
# style sheet in the HTML output directory as well, or it will be erased!
-HTML_STYLESHEET =
+HTML_STYLESHEET =
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the HTML output directory. Note
-# that these files will be copied to the base HTML output directory. Use the
-# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
# the files will be copied as-is; there are no commands or markers available.
-HTML_EXTRA_FILES =
+HTML_EXTRA_FILES =
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the style sheet and background images
-# according to this color. Hue is specified as an angle on a colorwheel,
-# see http://en.wikipedia.org/wiki/Hue for more information.
-# For instance the value 0 represents red, 60 is yellow, 120 is green,
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
# The allowed range is 0 to 359.
HTML_COLORSTYLE_HUE = 220
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
-# the colors in the HTML output. For a value of 0 the output will use
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
# grayscales only. A value of 255 will produce the most vivid colors.
HTML_COLORSTYLE_SAT = 100
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
-# the luminance component of the colors in the HTML output. Values below
-# 100 gradually make the output lighter, whereas values above 100 make
-# the output darker. The value divided by 100 is the actual gamma applied,
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
# and 100 does not change the gamma.
HTML_COLORSTYLE_GAMMA = 80
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
# this to NO can help when comparing the output of multiple runs.
HTML_TIMESTAMP = YES
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
HTML_DYNAMIC_SECTIONS = NO
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
+# and will result in a full expanded tree by default.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
# for more information.
GENERATE_DOCSET = NO
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
# can be grouped.
DOCSET_FEEDNAME = "Doxygen generated docs"
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
# will append .docset to the name.
DOCSET_BUNDLE_ID = org.doxygen.Project
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
# string, e.g. com.mycompany.MyDocSet.documentation.
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
@@ -1005,220 +1009,216 @@ DOCSET_PUBLISHER_ID = org.doxygen.Publisher
DOCSET_PUBLISHER_NAME = Publisher
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
# of the generated HTML documentation.
GENERATE_HTMLHELP = NO
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
# written to the html output directory.
-CHM_FILE =
+CHM_FILE =
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
# the HTML help compiler on the generated index.hhp.
-HHC_LOCATION =
+HHC_LOCATION =
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
# it should be included in the master .chm file (NO).
GENERATE_CHI = NO
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
# content.
-CHM_INDEX_ENCODING =
+CHM_INDEX_ENCODING =
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
# normal table of contents (NO) in the .chm file.
BINARY_TOC = NO
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
# to the contents of the HTML help documentation and to the tree view.
TOC_EXPAND = NO
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
-# that can be used as input for Qt's qhelpgenerator to generate a
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
# Qt Compressed Help (.qch) of the generated HTML documentation.
GENERATE_QHP = NO
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
# The path specified is relative to the HTML output folder.
-QCH_FILE =
+QCH_FILE =
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
# http://doc.trolltech.com/qthelpproject.html#namespace
QHP_NAMESPACE = org.doxygen.Project
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
# http://doc.trolltech.com/qthelpproject.html#virtual-folders
QHP_VIRTUAL_FOLDER = doc
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
-# add. For more information please see
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
# http://doc.trolltech.com/qthelpproject.html#custom-filters
-QHP_CUST_FILTER_NAME =
+QHP_CUST_FILTER_NAME =
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
# Qt Help Project / Custom Filters</a>.
-QHP_CUST_FILTER_ATTRS =
+QHP_CUST_FILTER_ATTRS =
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
# Qt Help Project / Filter Attributes</a>.
-QHP_SECT_FILTER_ATTRS =
+QHP_SECT_FILTER_ATTRS =
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
# .qhp file.
-QHG_LOCATION =
+QHG_LOCATION =
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-# will be generated, which together with the HTML files, form an Eclipse help
-# plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
# the help appears.
GENERATE_ECLIPSEHELP = NO
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
# this name.
ECLIPSE_DOC_ID = org.doxygen.Project
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
-# at top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it. Since the tabs have the same information as the
-# navigation tree you can set this option to NO if you already set
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
# GENERATE_TREEVIEW to YES.
DISABLE_INDEX = NO
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
-# Since the tree basically has the same information as the tab index you
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
# could consider to set DISABLE_INDEX to NO when enabling this option.
GENERATE_TREEVIEW = NO
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
-# (range [0,1..20]) that doxygen will group on one line in the generated HTML
-# documentation. Note that a value of 0 will completely suppress the enum
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
# values from appearing in the overview section.
ENUM_VALUES_PER_LINE = 4
-# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list.
-
-USE_INLINE_TREES = NO
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
# is shown.
TREEVIEW_WIDTH = 250
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
# links to external symbols imported via tag files in a separate window.
EXT_LINKS_IN_WINDOW = NO
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
# to force them to be regenerated.
FORMULA_FONTSIZE = 10
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are
-# not supported properly for IE 6.0, but are supported on all modern browsers.
-# Note that when changing this option you need to delete any form_*.png files
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
# in the HTML output before the changes have effect.
FORMULA_TRANSPARENT = YES
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
-# (see http://www.mathjax.org) which uses client side Javascript for the
-# rendering instead of using prerendered bitmaps. Use this if you do not
-# have LaTeX installed or if you want to formulas look prettier in the HTML
-# output. When enabled you may also need to install MathJax separately and
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
# configure the path to it using the MATHJAX_RELPATH option.
USE_MATHJAX = NO
-# When MathJax is enabled you need to specify the location relative to the
-# HTML output directory using the MATHJAX_RELPATH option. The destination
-# directory should contain the MathJax.js script. For instance, if the mathjax
-# directory is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to
-# the MathJax Content Delivery Network so you can quickly see the result without
-# installing MathJax. However, it is strongly recommended to install a local
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.
+# However, it is strongly recommended to install a local
# copy of MathJax from http://www.mathjax.org before deployment.
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
-# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
# names that should be enabled during MathJax rendering.
-MATHJAX_EXTENSIONS =
+MATHJAX_EXTENSIONS =
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box
-# for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
-# (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
SEARCHENGINE = YES
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a PHP enabled web server instead of at the web client
-# using Javascript. Doxygen will generate the search PHP script and index
-# file to put on the web server. The advantage of the server
-# based approach is that it scales better to large projects and allows
-# full text search. The disadvantages are that it is more difficult to setup
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
# and does not have live searching capabilities.
SERVER_BASED_SEARCH = NO
@@ -1227,97 +1227,97 @@ SERVER_BASED_SEARCH = NO
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
# generate Latex output.
GENERATE_LATEX = NO
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `latex' will be used as the default path.
LATEX_OUTPUT = latex
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
# Makefile that is written to the output directory.
LATEX_CMD_NAME = latex
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
# default command name.
MAKEINDEX_CMD_NAME = makeindex
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_LATEX = NO
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, letter, legal and
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = a4
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.
-EXTRA_PACKAGES =
+EXTRA_PACKAGES =
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
# standard header. Notice: only use this tag if you know what you are doing!
-LATEX_HEADER =
+LATEX_HEADER =
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
-# the generated latex document. The footer should contain everything after
-# the last chapter. If it is left blank doxygen will generate a
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
# standard footer. Notice: only use this tag if you know what you are doing!
-LATEX_FOOTER =
+LATEX_FOOTER =
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
# This makes the output suitable for online browsing using a pdf viewer.
PDF_HYPERLINKS = YES
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
# higher quality PDF documentation.
USE_PDFLATEX = YES
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
# This option is also used when generating formulas in HTML.
LATEX_BATCHMODE = NO
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
# in the output.
LATEX_HIDE_INDICES = NO
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
# such as SOURCE_BROWSER.
LATEX_SOURCE_CODE = NO
-# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
-# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
# http://en.wikipedia.org/wiki/BibTeX for more info.
LATEX_BIB_STYLE = plain
@@ -1326,68 +1326,68 @@ LATEX_BIB_STYLE = plain
# configuration options related to the RTF output
#---------------------------------------------------------------------------
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
# other RTF readers or editors.
GENERATE_RTF = NO
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `rtf' will be used as the default path.
RTF_OUTPUT = rtf
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_RTF = NO
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
# Note: wordpad (write) and others do not support links.
RTF_HYPERLINKS = NO
-# Load style sheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
-RTF_STYLESHEET_FILE =
+RTF_STYLESHEET_FILE =
-# Set optional variables used in the generation of an rtf document.
+# Set optional variables used in the generation of an rtf document.
# Syntax is similar to doxygen's config file.
-RTF_EXTENSIONS_FILE =
+RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
# generate man pages
GENERATE_MAN = NO
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `man' will be used as the default path.
MAN_OUTPUT = man
-# The MAN_EXTENSION tag determines the extension that is added to
+# The MAN_EXTENSION tag determines the extension that is added to
# the generated man pages (default is the subroutine's section .3)
MAN_EXTENSION = .3
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
# would be unable to find the correct page. The default is NO.
MAN_LINKS = NO
@@ -1396,33 +1396,33 @@ MAN_LINKS = NO
# configuration options related to the XML output
#---------------------------------------------------------------------------
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
# the code including all documentation.
GENERATE_XML = NO
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `xml' will be used as the default path.
XML_OUTPUT = xml
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
# syntax of the XML files.
-XML_SCHEMA =
+XML_SCHEMA =
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
# syntax of the XML files.
-XML_DTD =
+XML_DTD =
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
# enabling this will significantly increase the size of the XML output.
XML_PROGRAMLISTING = YES
@@ -1431,10 +1431,10 @@ XML_PROGRAMLISTING = YES
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
# and incomplete at the moment.
GENERATE_AUTOGEN_DEF = NO
@@ -1443,97 +1443,99 @@ GENERATE_AUTOGEN_DEF = NO
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
# moment.
GENERATE_PERLMOD = NO
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
# to generate PDF and DVI output from the Perl module output.
PERLMOD_LATEX = NO
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader. This is useful
-# if you want to understand what is going on. On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
# and Perl will parse it just the same.
PERLMOD_PRETTY = YES
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
# Makefile don't overwrite each other's variables.
-PERLMOD_MAKEVAR_PREFIX =
+PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
# files.
ENABLE_PREPROCESSING = YES
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
# way by setting EXPAND_ONLY_PREDEF to YES.
MACRO_EXPANSION = NO
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
# PREDEFINED and EXPAND_AS_DEFINED tags.
EXPAND_ONLY_PREDEF = NO
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# pointed to by INCLUDE_PATH will be searched when a #include is found.
SEARCH_INCLUDES = YES
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
# the preprocessor.
-INCLUDE_PATH =
+INCLUDE_PATH =
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
# be used.
-INCLUDE_FILE_PATTERNS =
+INCLUDE_FILE_PATTERNS =
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
# instead of the = operator.
-PREDEFINED =
+PREDEFINED =
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition that
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
# overrules the definition found in the source code.
-EXPAND_AS_DEFINED =
+EXPAND_AS_DEFINED =
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all references to function-like macros
-# that are alone on a line, have an all uppercase name, and do not end with a
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
# semicolon, because these will confuse the parser if not removed.
SKIP_FUNCTION_MACROS = YES
@@ -1542,37 +1544,39 @@ SKIP_FUNCTION_MACROS = YES
# Configuration::additions related to external references
#---------------------------------------------------------------------------
-# The TAGFILES option can be used to specify one or more tagfiles. For each
-# tag file the location of the external documentation should be added. The
-# format of a tag file without this location is as follows:
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths
-# or URLs. Note that each tag file must have a unique name (where the name does
-# NOT include the path). If a tag file is not located in the directory in which
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
# doxygen is run, you must also specify the path to the tagfile here.
-TAGFILES =
+TAGFILES =
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
-GENERATE_TAGFILE =
+GENERATE_TAGFILE =
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
# will be listed.
ALLEXTERNALS = NO
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
# be listed.
EXTERNAL_GROUPS = YES
-# The PERL_PATH should be the absolute path and name of the perl script
+# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of `which perl').
PERL_PATH = /usr/bin/perl
@@ -1581,222 +1585,222 @@ PERL_PATH = /usr/bin/perl
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option also works with HAVE_DOT disabled, but it is recommended to
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
# install and use dot, since it yields more powerful graphs.
CLASS_DIAGRAMS = NO
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
-MSCGEN_PATH =
+MSCGEN_PATH =
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
# or is not a class.
HIDE_UNDOC_RELATIONS = YES
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
# have no effect if this option is set to NO (the default)
HAVE_DOT = YES
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
-# allowed to run in parallel. When set to 0 (the default) doxygen will
-# base this on the number of processors available in the system. You can set it
-# explicitly to a value larger than 0 to get control over the balance
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
# between CPU load and processing speed.
DOT_NUM_THREADS = 0
-# By default doxygen will use the Helvetica font for all dot files that
-# doxygen generates. When you want a differently looking font you can specify
-# the font name using DOT_FONTNAME. You need to make sure dot is able to find
-# the font, which can be done by putting it in a standard location or by setting
-# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
# directory containing the font.
DOT_FONTNAME = Helvetica
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
# The default size is 10pt.
DOT_FONTSIZE = 10
-# By default doxygen will tell dot to use the Helvetica font.
-# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
# set the path where dot can find it.
-DOT_FONTPATH =
+DOT_FONTPATH =
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
# CLASS_DIAGRAMS tag to NO.
CLASS_GRAPH = YES
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
# class references variables) of the class with other documented classes.
COLLABORATION_GRAPH = NO
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for groups, showing the direct groups dependencies
GROUP_GRAPHS = YES
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
UML_LOOK = NO
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside
-# the class node. If there are many fields or methods and many nodes the
-# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
-# threshold limits the number of items for each type to make the size more
-# managable. Set this to 0 for no limit. Note that the threshold may be
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
# exceeded by 50% before the limit is enforced.
UML_LIMIT_NUM_FIELDS = 10
-# If set to YES, the inheritance and collaboration graphs will show the
+# If set to YES, the inheritance and collaboration graphs will show the
# relations between templates and their instances.
TEMPLATE_RELATIONS = NO
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
# other documented files.
INCLUDE_GRAPH = YES
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
# indirectly include this file.
INCLUDED_BY_GRAPH = NO
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
# for selected functions only using the \callgraph command.
CALL_GRAPH = NO
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
# graphs for selected functions only using the \callergraph command.
CALLER_GRAPH = NO
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
# will generate a graphical hierarchy of all classes instead of a textual one.
GRAPHICAL_HIERARCHY = YES
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
# relations between the files in the directories.
DIRECTORY_GRAPH = YES
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are svg, png, jpg, or gif.
-# If left blank png will be used. If you choose svg you need to set
-# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
# visible in IE 9+ (other browsers do not have this requirement).
DOT_IMAGE_FORMAT = png
-# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
-# enable generation of interactive SVG images that allow zooming and panning.
-# Note that this requires a modern browser other than Internet Explorer.
-# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
-# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
# visible. Older versions of IE do not have SVG support.
INTERACTIVE_SVG = NO
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found in the path.
-DOT_PATH =
+DOT_PATH =
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
# \dotfile command).
-DOTFILE_DIRS =
+DOTFILE_DIRS =
-# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
# \mscfile command).
-MSCFILE_DIRS =
+MSCFILE_DIRS =
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
DOT_GRAPH_MAX_NODES = 50
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
MAX_DOT_GRAPH_DEPTH = 0
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
# a graph (i.e. they become hard to read).
DOT_TRANSPARENT = NO
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
# support this, this feature is disabled by default.
DOT_MULTI_TARGETS = NO
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
# arrows in the dot generated graphs.
GENERATE_LEGEND = YES
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
# the various graphs.
DOT_CLEANUP = YES
diff --git a/lib/simpleini/ConvertUTF.c b/lib/simpleini/ConvertUTF.c
new file mode 100644
index 0000000..4351b15
--- /dev/null
+++ b/lib/simpleini/ConvertUTF.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Source code file.
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Sept 2001: fixed const & error conditions per
+ mods suggested by S. Parent & A. Lillich.
+ June 2002: Tim Dodd added detection and handling of incomplete
+ source sequences, enhanced error detection, added casts
+ to eliminate compiler warnings.
+ July 2003: slight mods to back out aggressive FFFE detection.
+ Jan 2004: updated switches in from-UTF8 conversions.
+ Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
+
+ See the header file "ConvertUTF.h" for complete documentation.
+
+------------------------------------------------------------------------ */
+
+
+#include "ConvertUTF.h"
+#ifdef CVTUTF_DEBUG
+#include <stdio.h>
+#endif
+
+static const int halfShift = 10; /* used for shifting by 10 bits */
+
+static const UTF32 halfBase = 0x0010000UL;
+static const UTF32 halfMask = 0x3FFUL;
+
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+#define false 0
+#define true 1
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF16 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF32* source = *sourceStart;
+ UTF16* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ if (target >= targetEnd) {
+ result = targetExhausted; break;
+ }
+ ch = *source++;
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_LEGAL_UTF32) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ --source; /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF32 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16* source = *sourceStart;
+ UTF32* target = *targetStart;
+ UTF32 ch, ch2;
+ while (source < sourceEnd) {
+ const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd) {
+ ch2 = *source;
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ } else if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ if (target >= targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ *target++ = ch;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+#ifdef CVTUTF_DEBUG
+if (result == sourceIllegal) {
+ fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
+ fflush(stderr);
+}
+#endif
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+
+/*
+ * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+ * into the first byte, depending on how many bytes follow. There are
+ * as many entries in this table as there are UTF-8 sequence types.
+ * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
+ * for *legal* UTF-8 will be 4 or fewer bytes total.
+ */
+static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+/* --------------------------------------------------------------------- */
+
+/* The interface converts a whole buffer to avoid function-call overhead.
+ * Constants have been gathered. Loops & conditionals have been removed as
+ * much as possible for efficiency, in favor of drop-through switches.
+ * (See "Note A" at the bottom of the file for equivalent code.)
+ * If your compiler supports it, the "isLegalUTF8" call can be turned
+ * into an inline function.
+ */
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF8 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16* source = *sourceStart;
+ UTF8* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd) {
+ UTF32 ch2 = *source;
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ } else if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ /* Figure out how many bytes the result will require */
+ if (ch < (UTF32)0x80) { bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
+ } else if (ch < (UTF32)0x110000) { bytesToWrite = 4;
+ } else { bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ target -= bytesToWrite; result = targetExhausted; break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns false. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+
+static Boolean isLegalUTF8(const UTF8 *source, int length) {
+ UTF8 a;
+ const UTF8 *srcptr = source+length;
+ switch (length) {
+ default: return false;
+ /* Everything else falls through when "true"... */
+ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 2: if ((a = (*--srcptr)) > 0xBF) return false;
+
+ switch (*source) {
+ /* no fall-through in this inner switch */
+ case 0xE0: if (a < 0xA0) return false; break;
+ case 0xED: if (a > 0x9F) return false; break;
+ case 0xF0: if (a < 0x90) return false; break;
+ case 0xF4: if (a > 0x8F) return false; break;
+ default: if (a < 0x80) return false;
+ }
+
+ case 1: if (*source >= 0x80 && *source < 0xC2) return false;
+ }
+ if (*source > 0xF4) return false;
+ return true;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Exported function to return whether a UTF-8 sequence is legal or not.
+ * This is not used here; it's just exported.
+ */
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
+ int length = trailingBytesForUTF8[*source]+1;
+ if (source+length > sourceEnd) {
+ return false;
+ }
+ return isLegalUTF8(source, length);
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF16 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8* source = *sourceStart;
+ UTF16* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ result = sourceExhausted; break;
+ }
+ /* Do this check whether lenient or strict */
+ if (! isLegalUTF8(source, extraBytesToRead+1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (target >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_UTF16) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ source -= (extraBytesToRead+1); /* return to the start */
+ break; /* Bail out; shouldn't continue */
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF8 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF32* source = *sourceStart;
+ UTF8* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ ch = *source++;
+ if (flags == strictConversion ) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ /*
+ * Figure out how many bytes the result will require. Turn any
+ * illegally large UTF32 things (> Plane 17) into replacement chars.
+ */
+ if (ch < (UTF32)0x80) { bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
+ } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4;
+ } else { bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ result = sourceIllegal;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ --source; /* Back up source pointer! */
+ target -= bytesToWrite; result = targetExhausted; break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF32 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8* source = *sourceStart;
+ UTF32* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ result = sourceExhausted; break;
+ }
+ /* Do this check whether lenient or strict */
+ if (! isLegalUTF8(source, extraBytesToRead+1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5: ch += *source++; ch <<= 6;
+ case 4: ch += *source++; ch <<= 6;
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (target >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up the source pointer! */
+ result = targetExhausted; break;
+ }
+ if (ch <= UNI_MAX_LEGAL_UTF32) {
+ /*
+ * UTF-16 surrogate values are illegal in UTF-32, and anything
+ * over Plane 17 (> 0x10FFFF) is illegal.
+ */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = ch;
+ }
+ } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
+ result = sourceIllegal;
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* ---------------------------------------------------------------------
+
+ Note A.
+ The fall-through switches in UTF-8 reading code save a
+ temp variable, some decrements & conditionals. The switches
+ are equivalent to the following loop:
+ {
+ int tmpBytesToRead = extraBytesToRead+1;
+ do {
+ ch += *source++;
+ --tmpBytesToRead;
+ if (tmpBytesToRead) ch <<= 6;
+ } while (tmpBytesToRead > 0);
+ }
+ In UTF-8 writing code, the switches on "bytesToWrite" are
+ similarly unrolled loops.
+
+ --------------------------------------------------------------------- */
diff --git a/lib/simpleini/ConvertUTF.h b/lib/simpleini/ConvertUTF.h
new file mode 100644
index 0000000..f1230ee
--- /dev/null
+++ b/lib/simpleini/ConvertUTF.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Header file.
+
+ Several funtions are included here, forming a complete set of
+ conversions between the three formats. UTF-7 is not included
+ here, but is handled in a separate source file.
+
+ Each of these routines takes pointers to input buffers and output
+ buffers. The input buffers are const.
+
+ Each routine converts the text between *sourceStart and sourceEnd,
+ putting the result into the buffer between *targetStart and
+ targetEnd. Note: the end pointers are *after* the last item: e.g.
+ *(sourceEnd - 1) is the last item.
+
+ The return result indicates whether the conversion was successful,
+ and if not, whether the problem was in the source or target buffers.
+ (Only the first encountered problem is indicated.)
+
+ After the conversion, *sourceStart and *targetStart are both
+ updated to point to the end of last text successfully converted in
+ the respective buffers.
+
+ Input parameters:
+ sourceStart - pointer to a pointer to the source buffer.
+ The contents of this are modified on return so that
+ it points at the next thing to be converted.
+ targetStart - similarly, pointer to pointer to the target buffer.
+ sourceEnd, targetEnd - respectively pointers to the ends of the
+ two buffers, for overflow checking only.
+
+ These conversion functions take a ConversionFlags argument. When this
+ flag is set to strict, both irregular sequences and isolated surrogates
+ will cause an error. When the flag is set to lenient, both irregular
+ sequences and isolated surrogates are converted.
+
+ Whether the flag is strict or lenient, all illegal sequences will cause
+ an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
+ or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
+ must check for illegal sequences.
+
+ When the flag is set to lenient, characters over 0x10FFFF are converted
+ to the replacement character; otherwise (when the flag is set to strict)
+ they constitute an error.
+
+ Output parameters:
+ The value "sourceIllegal" is returned from some routines if the input
+ sequence is malformed. When "sourceIllegal" is returned, the source
+ value will point to the illegal value that caused the problem. E.g.,
+ in UTF-8 when a sequence is malformed, it points to the start of the
+ malformed sequence.
+
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Fixes & updates, Sept 2001.
+
+------------------------------------------------------------------------ */
+
+/* ---------------------------------------------------------------------
+ The following 4 definitions are compiler-specific.
+ The C standard does not guarantee that wchar_t has at least
+ 16 bits, so wchar_t is no less portable than unsigned short!
+ All should be unsigned values to avoid sign extension during
+ bit mask & shift operations.
+------------------------------------------------------------------------ */
+
+typedef unsigned int UTF32; /* at least 32 bits */
+typedef unsigned short UTF16; /* at least 16 bits */
+typedef unsigned char UTF8; /* typically 8 bits */
+typedef unsigned char Boolean; /* 0 or 1 */
+
+/* Some fundamental constants */
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_MAX_BMP (UTF32)0x0000FFFF
+#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
+#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
+#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
+
+typedef enum {
+ conversionOK, /* conversion successful */
+ sourceExhausted, /* partial character in source, but hit end */
+ targetExhausted, /* insuff. room in target for conversion */
+ sourceIllegal /* source sequence is illegal/malformed */
+} ConversionResult;
+
+typedef enum {
+ strictConversion = 0,
+ lenientConversion
+} ConversionFlags;
+
+/* This is for C++ and does no harm in C */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ConversionResult ConvertUTF8toUTF16 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF16toUTF8 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF8toUTF32 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF32toUTF8 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF16toUTF32 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF32toUTF16 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
+
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* --------------------------------------------------------------------- */
diff --git a/lib/simpleini/Makefile b/lib/simpleini/Makefile
new file mode 100644
index 0000000..a04b5e3
--- /dev/null
+++ b/lib/simpleini/Makefile
@@ -0,0 +1,28 @@
+CC=g++
+CFLAGS=-Wall
+CPPFLAGS=-Wall
+
+OBJS=testsi.o test1.o snippets.o ConvertUTF.o
+
+help:
+ @echo This makefile is just for the test program \(use \"make clean all test\"\)
+ @echo Just include the SimpleIni.h header file to use it.
+
+all: $(OBJS)
+ $(CC) -o testsi $(OBJS)
+
+clean:
+ rm -f core *.o testsi
+
+data:
+ sed 's/\r\n$$/\n/g' < test1-expected.ini > unix.out
+ mv unix.out test1-expected.ini
+
+test: testsi
+ ./testsi -u -m -l test1-input.ini > test1-blah.ini
+ diff test1-output.ini test1-expected.ini
+
+install:
+ @echo No install required. Just include the SimpleIni.h header file to use it.
+
+testsi.o test1.o snippets.o : SimpleIni.h
diff --git a/lib/simpleini/SimpleIni.h b/lib/simpleini/SimpleIni.h
new file mode 100644
index 0000000..f9f36bb
--- /dev/null
+++ b/lib/simpleini/SimpleIni.h
@@ -0,0 +1,3370 @@
+/** @mainpage
+
+ <table>
+ <tr><th>Library <td>SimpleIni
+ <tr><th>File <td>SimpleIni.h
+ <tr><th>Author <td>Brodie Thiesfield [code at jellycan dot com]
+ <tr><th>Source <td>http://code.jellycan.com/simpleini/
+ <tr><th>Version <td>4.15
+ </table>
+
+ Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation.
+
+ @section intro INTRODUCTION
+
+ This component allows an INI-style configuration file to be used on both
+ Windows and Linux/Unix. It is fast, simple and source code using this
+ component will compile unchanged on either OS.
+
+
+ @section features FEATURES
+
+ - MIT Licence allows free use in all software (including GPL and commercial)
+ - multi-platform (Windows 95/98/ME/NT/2K/XP/2003, Windows CE, Linux, Unix)
+ - loading and saving of INI-style configuration files
+ - configuration files can have any newline format on all platforms
+ - liberal acceptance of file format
+ - key/values with no section
+ - removal of whitespace around sections, keys and values
+ - support for multi-line values (values with embedded newline characters)
+ - optional support for multiple keys with the same name
+ - optional case-insensitive sections and keys (for ASCII characters only)
+ - saves files with sections and keys in the same order as they were loaded
+ - preserves comments on the file, section and keys where possible.
+ - supports both char or wchar_t programming interfaces
+ - supports both MBCS (system locale) and UTF-8 file encodings
+ - system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file
+ - support for non-ASCII characters in section, keys, values and comments
+ - support for non-standard character types or file encodings
+ via user-written converter classes
+ - support for adding/modifying values programmatically
+ - compiles cleanly in the following compilers:
+ - Windows/VC6 (warning level 3)
+ - Windows/VC.NET 2003 (warning level 4)
+ - Windows/VC 2005 (warning level 4)
+ - Linux/gcc (-Wall)
+
+
+ @section usage USAGE SUMMARY
+
+ -# Define the appropriate symbol for the converter you wish to use and
+ include the SimpleIni.h header file. If no specific converter is defined
+ then the default converter is used. The default conversion mode uses
+ SI_CONVERT_WIN32 on Windows and SI_CONVERT_GENERIC on all other
+ platforms. If you are using ICU then SI_CONVERT_ICU is supported on all
+ platforms.
+ -# Declare an instance the appropriate class. Note that the following
+ definitions are just shortcuts for commonly used types. Other types
+ (PRUnichar, unsigned short, unsigned char) are also possible.
+ <table>
+ <tr><th>Interface <th>Case-sensitive <th>Load UTF-8 <th>Load MBCS <th>Typedef
+ <tr><th>SI_CONVERT_GENERIC
+ <tr><td>char <td>No <td>Yes <td>Yes #1 <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
+ <tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW
+ <tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
+ <tr><th>SI_CONVERT_WIN32
+ <tr><td>char <td>No <td>No #2 <td>Yes <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
+ <tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW
+ <tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
+ <tr><th>SI_CONVERT_ICU
+ <tr><td>char <td>No <td>Yes <td>Yes <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
+ <tr><td>UChar <td>No <td>Yes <td>Yes <td>CSimpleIniW
+ <tr><td>UChar <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
+ </table>
+ #1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.<br>
+ #2 Only affects Windows. On Windows this uses MBCS functions and
+ so may fold case incorrectly leading to uncertain results.
+ -# Call LoadData() or LoadFile() to load and parse the INI configuration file
+ -# Access and modify the data of the file using the following functions
+ <table>
+ <tr><td>GetAllSections <td>Return all section names
+ <tr><td>GetAllKeys <td>Return all key names within a section
+ <tr><td>GetAllValues <td>Return all values within a section & key
+ <tr><td>GetSection <td>Return all key names and values in a section
+ <tr><td>GetSectionSize <td>Return the number of keys in a section
+ <tr><td>GetValue <td>Return a value for a section & key
+ <tr><td>SetValue <td>Add or update a value for a section & key
+ <tr><td>Delete <td>Remove a section, or a key from a section
+ </table>
+ -# Call Save() or SaveFile() to save the INI configuration data
+
+ @section iostreams IO STREAMS
+
+ SimpleIni supports reading from and writing to STL IO streams. Enable this
+ by defining SI_SUPPORT_IOSTREAMS before including the SimpleIni.h header
+ file. Ensure that if the streams are backed by a file (e.g. ifstream or
+ ofstream) then the flag ios_base::binary has been used when the file was
+ opened.
+
+ @section multiline MULTI-LINE VALUES
+
+ Values that span multiple lines are created using the following format.
+
+ <pre>
+ key = <<<ENDTAG
+ .... multiline value ....
+ ENDTAG
+ </pre>
+
+ Note the following:
+ - The text used for ENDTAG can be anything and is used to find
+ where the multi-line text ends.
+ - The newline after ENDTAG in the start tag, and the newline
+ before ENDTAG in the end tag is not included in the data value.
+ - The ending tag must be on it's own line with no whitespace before
+ or after it.
+ - The multi-line value is modified at load so that each line in the value
+ is delimited by a single '\\n' character on all platforms. At save time
+ it will be converted into the newline format used by the current
+ platform.
+
+ @section comments COMMENTS
+
+ Comments are preserved in the file within the following restrictions:
+ - Every file may have a single "file comment". It must start with the
+ first character in the file, and will end with the first non-comment
+ line in the file.
+ - Every section may have a single "section comment". It will start
+ with the first comment line following the file comment, or the last
+ data entry. It ends at the beginning of the section.
+ - Every key may have a single "key comment". This comment will start
+ with the first comment line following the section start, or the file
+ comment if there is no section name.
+ - Comments are set at the time that the file, section or key is first
+ created. The only way to modify a comment on a section or a key is to
+ delete that entry and recreate it with the new comment. There is no
+ way to change the file comment.
+
+ @section save SAVE ORDER
+
+ The sections and keys are written out in the same order as they were
+ read in from the file. Sections and keys added to the data after the
+ file has been loaded will be added to the end of the file when it is
+ written. There is no way to specify the location of a section or key
+ other than in first-created, first-saved order.
+
+ @section notes NOTES
+
+ - To load UTF-8 data on Windows 95, you need to use Microsoft Layer for
+ Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU.
+ - When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked.
+ - When using SI_CONVERT_ICU, ICU header files must be on the include
+ path and icuuc.lib must be linked in.
+ - To load a UTF-8 file on Windows AND expose it with SI_CHAR == char,
+ you should use SI_CONVERT_GENERIC.
+ - The collation (sorting) order used for sections and keys returned from
+ iterators is NOT DEFINED. If collation order of the text is important
+ then it should be done yourself by either supplying a replacement
+ SI_STRLESS class, or by sorting the strings external to this library.
+ - Usage of the <mbstring.h> header on Windows can be disabled by defining
+ SI_NO_MBCS. This is defined automatically on Windows CE platforms.
+
+ @section contrib CONTRIBUTIONS
+
+ - 2010/05/03: Tobias Gehrig: added GetDoubleValue()
+
+ @section licence MIT LICENCE
+
+ The licence text below is the boilerplate "MIT Licence" used from:
+ http://www.opensource.org/licenses/mit-license.php
+
+ Copyright (c) 2006-2012, Brodie Thiesfield
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is furnished
+ to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef INCLUDED_SimpleIni_h
+#define INCLUDED_SimpleIni_h
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+// Disable these warnings in MSVC:
+// 4127 "conditional expression is constant" as the conversion classes trigger
+// it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
+// be optimized away in a release build.
+// 4503 'insert' : decorated name length exceeded, name was truncated
+// 4702 "unreachable code" as the MS STL header causes it in release mode.
+// Again, the code causing the warning will be cleaned up by the compiler.
+// 4786 "identifier truncated to 256 characters" as this is thrown hundreds
+// of times VC6 as soon as STL is used.
+#ifdef _MSC_VER
+# pragma warning (push)
+# pragma warning (disable: 4127 4503 4702 4786)
+#endif
+
+#include <cstring>
+#include <string>
+#include <map>
+#include <list>
+#include <algorithm>
+#include <stdio.h>
+
+#ifdef SI_SUPPORT_IOSTREAMS
+# include <iostream>
+#endif // SI_SUPPORT_IOSTREAMS
+
+#ifdef _DEBUG
+# ifndef assert
+# include <cassert>
+# endif
+# define SI_ASSERT(x) assert(x)
+#else
+# define SI_ASSERT(x)
+#endif
+
+enum SI_Error {
+ SI_OK = 0, //!< No error
+ SI_UPDATED = 1, //!< An existing value was updated
+ SI_INSERTED = 2, //!< A new value was inserted
+
+ // note: test for any error with (retval < 0)
+ SI_FAIL = -1, //!< Generic failure
+ SI_NOMEM = -2, //!< Out of memory error
+ SI_FILE = -3 //!< File error (see errno for detail error)
+};
+
+#define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
+
+#ifdef _WIN32
+# define SI_NEWLINE_A "\r\n"
+# define SI_NEWLINE_W L"\r\n"
+#else // !_WIN32
+# define SI_NEWLINE_A "\n"
+# define SI_NEWLINE_W L"\n"
+#endif // _WIN32
+
+#if defined(SI_CONVERT_ICU)
+# include <unicode/ustring.h>
+#endif
+
+#if defined(_WIN32)
+# define SI_HAS_WIDE_FILE
+# define SI_WCHAR_T wchar_t
+#elif defined(SI_CONVERT_ICU)
+# define SI_HAS_WIDE_FILE
+# define SI_WCHAR_T UChar
+#endif
+
+
+// ---------------------------------------------------------------------------
+// MAIN TEMPLATE CLASS
+// ---------------------------------------------------------------------------
+
+/** Simple INI file reader.
+
+ This can be instantiated with the choice of unicode or native characterset,
+ and case sensitive or insensitive comparisons of section and key names.
+ The supported combinations are pre-defined with the following typedefs:
+
+ <table>
+ <tr><th>Interface <th>Case-sensitive <th>Typedef
+ <tr><td>char <td>No <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>CSimpleIniCaseA
+ <tr><td>wchar_t <td>No <td>CSimpleIniW
+ <tr><td>wchar_t <td>Yes <td>CSimpleIniCaseW
+ </table>
+
+ Note that using other types for the SI_CHAR is supported. For instance,
+ unsigned char, unsigned short, etc. Note that where the alternative type
+ is a different size to char/wchar_t you may need to supply new helper
+ classes for SI_STRLESS and SI_CONVERTER.
+ */
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+class CSimpleIniTempl
+{
+public:
+ /** key entry */
+ struct Entry {
+ const SI_CHAR * pItem;
+ const SI_CHAR * pComment;
+ int nOrder;
+
+ Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0)
+ : pItem(a_pszItem)
+ , pComment(NULL)
+ , nOrder(a_nOrder)
+ { }
+ Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder)
+ : pItem(a_pszItem)
+ , pComment(a_pszComment)
+ , nOrder(a_nOrder)
+ { }
+ Entry(const Entry & rhs) { operator=(rhs); }
+ Entry & operator=(const Entry & rhs) {
+ pItem = rhs.pItem;
+ pComment = rhs.pComment;
+ nOrder = rhs.nOrder;
+ return *this;
+ }
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+ /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */
+ bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); }
+ bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); }
+#endif
+
+ /** Strict less ordering by name of key only */
+ struct KeyOrder : std::binary_function<Entry, Entry, bool> {
+ bool operator()(const Entry & lhs, const Entry & rhs) const {
+ const static SI_STRLESS isLess = SI_STRLESS();
+ return isLess(lhs.pItem, rhs.pItem);
+ }
+ };
+
+ /** Strict less ordering by order, and then name of key */
+ struct LoadOrder : std::binary_function<Entry, Entry, bool> {
+ bool operator()(const Entry & lhs, const Entry & rhs) const {
+ if (lhs.nOrder != rhs.nOrder) {
+ return lhs.nOrder < rhs.nOrder;
+ }
+ return KeyOrder()(lhs.pItem, rhs.pItem);
+ }
+ };
+ };
+
+ /** map keys to values */
+ typedef std::multimap<Entry,const SI_CHAR *,typename Entry::KeyOrder> TKeyVal;
+
+ /** map sections to key/value map */
+ typedef std::map<Entry,TKeyVal,typename Entry::KeyOrder> TSection;
+
+ /** set of dependent string pointers. Note that these pointers are
+ dependent on memory owned by CSimpleIni.
+ */
+ typedef std::list<Entry> TNamesDepend;
+
+ /** interface definition for the OutputWriter object to pass to Save()
+ in order to output the INI file data.
+ */
+ class OutputWriter {
+ public:
+ OutputWriter() { }
+ virtual ~OutputWriter() { }
+ virtual void Write(const char * a_pBuf) = 0;
+ private:
+ OutputWriter(const OutputWriter &); // disable
+ OutputWriter & operator=(const OutputWriter &); // disable
+ };
+
+ /** OutputWriter class to write the INI data to a file */
+ class FileWriter : public OutputWriter {
+ FILE * m_file;
+ public:
+ FileWriter(FILE * a_file) : m_file(a_file) { }
+ void Write(const char * a_pBuf) {
+ fputs(a_pBuf, m_file);
+ }
+ private:
+ FileWriter(const FileWriter &); // disable
+ FileWriter & operator=(const FileWriter &); // disable
+ };
+
+ /** OutputWriter class to write the INI data to a string */
+ class StringWriter : public OutputWriter {
+ std::string & m_string;
+ public:
+ StringWriter(std::string & a_string) : m_string(a_string) { }
+ void Write(const char * a_pBuf) {
+ m_string.append(a_pBuf);
+ }
+ private:
+ StringWriter(const StringWriter &); // disable
+ StringWriter & operator=(const StringWriter &); // disable
+ };
+
+#ifdef SI_SUPPORT_IOSTREAMS
+ /** OutputWriter class to write the INI data to an ostream */
+ class StreamWriter : public OutputWriter {
+ std::ostream & m_ostream;
+ public:
+ StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { }
+ void Write(const char * a_pBuf) {
+ m_ostream << a_pBuf;
+ }
+ private:
+ StreamWriter(const StreamWriter &); // disable
+ StreamWriter & operator=(const StreamWriter &); // disable
+ };
+#endif // SI_SUPPORT_IOSTREAMS
+
+ /** Characterset conversion utility class to convert strings to the
+ same format as is used for the storage.
+ */
+ class Converter : private SI_CONVERTER {
+ public:
+ Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) {
+ m_scratch.resize(1024);
+ }
+ Converter(const Converter & rhs) { operator=(rhs); }
+ Converter & operator=(const Converter & rhs) {
+ m_scratch = rhs.m_scratch;
+ return *this;
+ }
+ bool ConvertToStore(const SI_CHAR * a_pszString) {
+ size_t uLen = SizeToStore(a_pszString);
+ if (uLen == (size_t)(-1)) {
+ return false;
+ }
+ while (uLen > m_scratch.size()) {
+ m_scratch.resize(m_scratch.size() * 2);
+ }
+ return SI_CONVERTER::ConvertToStore(
+ a_pszString,
+ const_cast<char*>(m_scratch.data()),
+ m_scratch.size());
+ }
+ const char * Data() { return m_scratch.data(); }
+ private:
+ std::string m_scratch;
+ };
+
+public:
+ /*-----------------------------------------------------------------------*/
+
+ /** Default constructor.
+
+ @param a_bIsUtf8 See the method SetUnicode() for details.
+ @param a_bMultiKey See the method SetMultiKey() for details.
+ @param a_bMultiLine See the method SetMultiLine() for details.
+ */
+ CSimpleIniTempl(
+ bool a_bIsUtf8 = false,
+ bool a_bMultiKey = false,
+ bool a_bMultiLine = false
+ );
+
+ /** Destructor */
+ ~CSimpleIniTempl();
+
+ /** Deallocate all memory stored by this object */
+ void Reset();
+
+ /** Has any data been loaded */
+ bool IsEmpty() const { return m_data.empty(); }
+
+ /*-----------------------------------------------------------------------*/
+ /** @{ @name Settings */
+
+ /** Set the storage format of the INI data. This affects both the loading
+ and saving of the INI data using all of the Load/Save API functions.
+ This value cannot be changed after any INI data has been loaded.
+
+ If the file is not set to Unicode (UTF-8), then the data encoding is
+ assumed to be the OS native encoding. This encoding is the system
+ locale on Linux/Unix and the legacy MBCS encoding on Windows NT/2K/XP.
+ If the storage format is set to Unicode then the file will be loaded
+ as UTF-8 encoded data regardless of the native file encoding. If
+ SI_CHAR == char then all of the char* parameters take and return UTF-8
+ encoded data regardless of the system locale.
+
+ \param a_bIsUtf8 Assume UTF-8 encoding for the source?
+ */
+ void SetUnicode(bool a_bIsUtf8 = true) {
+ if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8;
+ }
+
+ /** Get the storage format of the INI data. */
+ bool IsUnicode() const { return m_bStoreIsUtf8; }
+
+ /** Should multiple identical keys be permitted in the file. If set to false
+ then the last value encountered will be used as the value of the key.
+ If set to true, then all values will be available to be queried. For
+ example, with the following input:
+
+ <pre>
+ [section]
+ test=value1
+ test=value2
+ </pre>
+
+ Then with SetMultiKey(true), both of the values "value1" and "value2"
+ will be returned for the key test. If SetMultiKey(false) is used, then
+ the value for "test" will only be "value2". This value may be changed
+ at any time.
+
+ \param a_bAllowMultiKey Allow multi-keys in the source?
+ */
+ void SetMultiKey(bool a_bAllowMultiKey = true) {
+ m_bAllowMultiKey = a_bAllowMultiKey;
+ }
+
+ /** Get the storage format of the INI data. */
+ bool IsMultiKey() const { return m_bAllowMultiKey; }
+
+ /** Should data values be permitted to span multiple lines in the file. If
+ set to false then the multi-line construct <<<TAG as a value will be
+ returned as is instead of loading the data. This value may be changed
+ at any time.
+
+ \param a_bAllowMultiLine Allow multi-line values in the source?
+ */
+ void SetMultiLine(bool a_bAllowMultiLine = true) {
+ m_bAllowMultiLine = a_bAllowMultiLine;
+ }
+
+ /** Query the status of multi-line data */
+ bool IsMultiLine() const { return m_bAllowMultiLine; }
+
+ /** Should spaces be added around the equals sign when writing key/value
+ pairs out. When true, the result will be "key = value". When false,
+ the result will be "key=value". This value may be changed at any time.
+
+ \param a_bSpaces Add spaces around the equals sign?
+ */
+ void SetSpaces(bool a_bSpaces = true) {
+ m_bSpaces = a_bSpaces;
+ }
+
+ /** Query the status of spaces output */
+ bool UsingSpaces() const { return m_bSpaces; }
+
+ /*-----------------------------------------------------------------------*/
+ /** @}
+ @{ @name Loading INI Data */
+
+ /** Load an INI file from disk into memory
+
+ @param a_pszFile Path of the file to be loaded. This will be passed
+ to fopen() and so must be a valid path for the
+ current platform.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadFile(
+ const char * a_pszFile
+ );
+
+#ifdef SI_HAS_WIDE_FILE
+ /** Load an INI file from disk into memory
+
+ @param a_pwszFile Path of the file to be loaded in UTF-16.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadFile(
+ const SI_WCHAR_T * a_pwszFile
+ );
+#endif // SI_HAS_WIDE_FILE
+
+ /** Load the file from a file pointer.
+
+ @param a_fpFile Valid file pointer to read the file data from. The
+ file will be read until end of file.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadFile(
+ FILE * a_fpFile
+ );
+
+#ifdef SI_SUPPORT_IOSTREAMS
+ /** Load INI file data from an istream.
+
+ @param a_istream Stream to read from
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadData(
+ std::istream & a_istream
+ );
+#endif // SI_SUPPORT_IOSTREAMS
+
+ /** Load INI file data direct from a std::string
+
+ @param a_strData Data to be loaded
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadData(const std::string & a_strData) {
+ return LoadData(a_strData.c_str(), a_strData.size());
+ }
+
+ /** Load INI file data direct from memory
+
+ @param a_pData Data to be loaded
+ @param a_uDataLen Length of the data in bytes
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadData(
+ const char * a_pData,
+ size_t a_uDataLen
+ );
+
+ /*-----------------------------------------------------------------------*/
+ /** @}
+ @{ @name Saving INI Data */
+
+ /** Save an INI file from memory to disk
+
+ @param a_pszFile Path of the file to be saved. This will be passed
+ to fopen() and so must be a valid path for the
+ current platform.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is
+ in UTF-8 format. If it is not UTF-8 then
+ this parameter is ignored.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error SaveFile(
+ const char * a_pszFile,
+ bool a_bAddSignature = true
+ ) const;
+
+#ifdef SI_HAS_WIDE_FILE
+ /** Save an INI file from memory to disk
+
+ @param a_pwszFile Path of the file to be saved in UTF-16.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is
+ in UTF-8 format. If it is not UTF-8 then
+ this parameter is ignored.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error SaveFile(
+ const SI_WCHAR_T * a_pwszFile,
+ bool a_bAddSignature = true
+ ) const;
+#endif // _WIN32
+
+ /** Save the INI data to a file. See Save() for details.
+
+ @param a_pFile Handle to a file. File should be opened for
+ binary output.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
+ UTF-8 format. If it is not UTF-8 then this value is
+ ignored. Do not set this to true if anything has
+ already been written to the file.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error SaveFile(
+ FILE * a_pFile,
+ bool a_bAddSignature = false
+ ) const;
+
+ /** Save the INI data. The data will be written to the output device
+ in a format appropriate to the current data, selected by:
+
+ <table>
+ <tr><th>SI_CHAR <th>FORMAT
+ <tr><td>char <td>same format as when loaded (MBCS or UTF-8)
+ <tr><td>wchar_t <td>UTF-8
+ <tr><td>other <td>UTF-8
+ </table>
+
+ Note that comments from the original data is preserved as per the
+ documentation on comments. The order of the sections and values
+ from the original file will be preserved.
+
+ Any data prepended or appended to the output device must use the the
+ same format (MBCS or UTF-8). You may use the GetConverter() method to
+ convert text to the correct format regardless of the output format
+ being used by SimpleIni.
+
+ To add a BOM to UTF-8 data, write it out manually at the very beginning
+ like is done in SaveFile when a_bUseBOM is true.
+
+ @param a_oOutput Output writer to write the data to.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
+ UTF-8 format. If it is not UTF-8 then this value is
+ ignored. Do not set this to true if anything has
+ already been written to the OutputWriter.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error Save(
+ OutputWriter & a_oOutput,
+ bool a_bAddSignature = false
+ ) const;
+
+#ifdef SI_SUPPORT_IOSTREAMS
+ /** Save the INI data to an ostream. See Save() for details.
+
+ @param a_ostream String to have the INI data appended to.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
+ UTF-8 format. If it is not UTF-8 then this value is
+ ignored. Do not set this to true if anything has
+ already been written to the stream.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error Save(
+ std::ostream & a_ostream,
+ bool a_bAddSignature = false
+ ) const
+ {
+ StreamWriter writer(a_ostream);
+ return Save(writer, a_bAddSignature);
+ }
+#endif // SI_SUPPORT_IOSTREAMS
+
+ /** Append the INI data to a string. See Save() for details.
+
+ @param a_sBuffer String to have the INI data appended to.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
+ UTF-8 format. If it is not UTF-8 then this value is
+ ignored. Do not set this to true if anything has
+ already been written to the string.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error Save(
+ std::string & a_sBuffer,
+ bool a_bAddSignature = false
+ ) const
+ {
+ StringWriter writer(a_sBuffer);
+ return Save(writer, a_bAddSignature);
+ }
+
+ /*-----------------------------------------------------------------------*/
+ /** @}
+ @{ @name Accessing INI Data */
+
+ /** Retrieve all section names. The list is returned as an STL vector of
+ names and can be iterated or searched as necessary. Note that the
+ sort order of the returned strings is NOT DEFINED. You can sort
+ the names into the load order if desired. Search this file for ".sort"
+ for an example.
+
+ NOTE! This structure contains only pointers to strings. The actual
+ string data is stored in memory owned by CSimpleIni. Ensure that the
+ CSimpleIni object is not destroyed or Reset() while these pointers
+ are in use!
+
+ @param a_names Vector that will receive all of the section
+ names. See note above!
+ */
+ void GetAllSections(
+ TNamesDepend & a_names
+ ) const;
+
+ /** Retrieve all unique key names in a section. The sort order of the
+ returned strings is NOT DEFINED. You can sort the names into the load
+ order if desired. Search this file for ".sort" for an example. Only
+ unique key names are returned.
+
+ NOTE! This structure contains only pointers to strings. The actual
+ string data is stored in memory owned by CSimpleIni. Ensure that the
+ CSimpleIni object is not destroyed or Reset() while these strings
+ are in use!
+
+ @param a_pSection Section to request data for
+ @param a_names List that will receive all of the key
+ names. See note above!
+
+ @return true Section was found.
+ @return false Matching section was not found.
+ */
+ bool GetAllKeys(
+ const SI_CHAR * a_pSection,
+ TNamesDepend & a_names
+ ) const;
+
+ /** Retrieve all values for a specific key. This method can be used when
+ multiple keys are both enabled and disabled. Note that the sort order
+ of the returned strings is NOT DEFINED. You can sort the names into
+ the load order if desired. Search this file for ".sort" for an example.
+
+ NOTE! The returned values are pointers to string data stored in memory
+ owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
+ or Reset while you are using this pointer!
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_values List to return if the key is not found
+
+ @return true Key was found.
+ @return false Matching section/key was not found.
+ */
+ bool GetAllValues(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ TNamesDepend & a_values
+ ) const;
+
+ /** Query the number of keys in a specific section. Note that if multiple
+ keys are enabled, then this value may be different to the number of
+ keys returned by GetAllKeys.
+
+ @param a_pSection Section to request data for
+
+ @return -1 Section does not exist in the file
+ @return >=0 Number of keys in the section
+ */
+ int GetSectionSize(
+ const SI_CHAR * a_pSection
+ ) const;
+
+ /** Retrieve all key and value pairs for a section. The data is returned
+ as a pointer to an STL map and can be iterated or searched as
+ desired. Note that multiple entries for the same key may exist when
+ multiple keys have been enabled.
+
+ NOTE! This structure contains only pointers to strings. The actual
+ string data is stored in memory owned by CSimpleIni. Ensure that the
+ CSimpleIni object is not destroyed or Reset() while these strings
+ are in use!
+
+ @param a_pSection Name of the section to return
+ @return boolean Was a section matching the supplied
+ name found.
+ */
+ const TKeyVal * GetSection(
+ const SI_CHAR * a_pSection
+ ) const;
+
+ /** Retrieve the value for a specific key. If multiple keys are enabled
+ (see SetMultiKey) then only the first value associated with that key
+ will be returned, see GetAllValues for getting all values with multikey.
+
+ NOTE! The returned value is a pointer to string data stored in memory
+ owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
+ or Reset while you are using this pointer!
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_pDefault Value to return if the key is not found
+ @param a_pHasMultiple Optionally receive notification of if there are
+ multiple entries for this key.
+
+ @return a_pDefault Key was not found in the section
+ @return other Value of the key
+ */
+ const SI_CHAR * GetValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pDefault = NULL,
+ bool * a_pHasMultiple = NULL
+ ) const;
+
+ /** Retrieve a numeric value for a specific key. If multiple keys are enabled
+ (see SetMultiKey) then only the first value associated with that key
+ will be returned, see GetAllValues for getting all values with multikey.
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_nDefault Value to return if the key is not found
+ @param a_pHasMultiple Optionally receive notification of if there are
+ multiple entries for this key.
+
+ @return a_nDefault Key was not found in the section
+ @return other Value of the key
+ */
+ long GetLongValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ long a_nDefault = 0,
+ bool * a_pHasMultiple = NULL
+ ) const;
+
+ /** Retrieve a numeric value for a specific key. If multiple keys are enabled
+ (see SetMultiKey) then only the first value associated with that key
+ will be returned, see GetAllValues for getting all values with multikey.
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_nDefault Value to return if the key is not found
+ @param a_pHasMultiple Optionally receive notification of if there are
+ multiple entries for this key.
+
+ @return a_nDefault Key was not found in the section
+ @return other Value of the key
+ */
+ double GetDoubleValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ double a_nDefault = 0,
+ bool * a_pHasMultiple = NULL
+ ) const;
+
+ /** Retrieve a boolean value for a specific key. If multiple keys are enabled
+ (see SetMultiKey) then only the first value associated with that key
+ will be returned, see GetAllValues for getting all values with multikey.
+
+ Strings starting with "t", "y", "on" or "1" are returned as logically true.
+ Strings starting with "f", "n", "of" or "0" are returned as logically false.
+ For all other values the default is returned. Character comparisons are
+ case-insensitive.
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_bDefault Value to return if the key is not found
+ @param a_pHasMultiple Optionally receive notification of if there are
+ multiple entries for this key.
+
+ @return a_nDefault Key was not found in the section
+ @return other Value of the key
+ */
+ bool GetBoolValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bDefault = false,
+ bool * a_pHasMultiple = NULL
+ ) const;
+
+ /** Add or update a section or value. This will always insert
+ when multiple keys are enabled.
+
+ @param a_pSection Section to add or update
+ @param a_pKey Key to add or update. Set to NULL to
+ create an empty section.
+ @param a_pValue Value to set. Set to NULL to create an
+ empty section.
+ @param a_pComment Comment to be associated with the section or the
+ key. If a_pKey is NULL then it will be associated
+ with the section, otherwise the key. Note that a
+ comment may be set ONLY when the section or key is
+ first created (i.e. when this function returns the
+ value SI_INSERTED). If you wish to create a section
+ with a comment then you need to create the section
+ separately to the key. The comment string must be
+ in full comment form already (have a comment
+ character starting every line).
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/SetValue and SetValue
+ with a_bForceReplace = true, is that the load
+ order and comment will be preserved this way.
+
+ @return SI_Error See error definitions
+ @return SI_UPDATED Value was updated
+ @return SI_INSERTED Value was inserted
+ */
+ SI_Error SetValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pValue,
+ const SI_CHAR * a_pComment = NULL,
+ bool a_bForceReplace = false
+ )
+ {
+ return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true);
+ }
+
+ /** Add or update a numeric value. This will always insert
+ when multiple keys are enabled.
+
+ @param a_pSection Section to add or update
+ @param a_pKey Key to add or update.
+ @param a_nValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
+ notes on SetValue() for comments.
+ @param a_bUseHex By default the value will be written to the file
+ in decimal format. Set this to true to write it
+ as hexadecimal.
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/SetLongValue and
+ SetLongValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
+ way.
+
+ @return SI_Error See error definitions
+ @return SI_UPDATED Value was updated
+ @return SI_INSERTED Value was inserted
+ */
+ SI_Error SetLongValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ long a_nValue,
+ const SI_CHAR * a_pComment = NULL,
+ bool a_bUseHex = false,
+ bool a_bForceReplace = false
+ );
+
+ /** Add or update a double value. This will always insert
+ when multiple keys are enabled.
+
+ @param a_pSection Section to add or update
+ @param a_pKey Key to add or update.
+ @param a_nValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
+ notes on SetValue() for comments.
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/SetDoubleValue and
+ SetDoubleValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
+ way.
+
+ @return SI_Error See error definitions
+ @return SI_UPDATED Value was updated
+ @return SI_INSERTED Value was inserted
+ */
+ SI_Error SetDoubleValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ double a_nValue,
+ const SI_CHAR * a_pComment = NULL,
+ bool a_bForceReplace = false
+ );
+
+ /** Add or update a boolean value. This will always insert
+ when multiple keys are enabled.
+
+ @param a_pSection Section to add or update
+ @param a_pKey Key to add or update.
+ @param a_bValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
+ notes on SetValue() for comments.
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/SetBoolValue and
+ SetBoolValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
+ way.
+
+ @return SI_Error See error definitions
+ @return SI_UPDATED Value was updated
+ @return SI_INSERTED Value was inserted
+ */
+ SI_Error SetBoolValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bValue,
+ const SI_CHAR * a_pComment = NULL,
+ bool a_bForceReplace = false
+ );
+
+ /** Delete an entire section, or a key from a section. Note that the
+ data returned by GetSection is invalid and must not be used after
+ anything has been deleted from that section using this method.
+ Note when multiple keys is enabled, this will delete all keys with
+ that name; there is no way to selectively delete individual key/values
+ in this situation.
+
+ @param a_pSection Section to delete key from, or if
+ a_pKey is NULL, the section to remove.
+ @param a_pKey Key to remove from the section. Set to
+ NULL to remove the entire section.
+ @param a_bRemoveEmpty If the section is empty after this key has
+ been deleted, should the empty section be
+ removed?
+
+ @return true Key or section was deleted.
+ @return false Key or section was not found.
+ */
+ bool Delete(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bRemoveEmpty = false
+ );
+
+ /*-----------------------------------------------------------------------*/
+ /** @}
+ @{ @name Converter */
+
+ /** Return a conversion object to convert text to the same encoding
+ as is used by the Save(), SaveFile() and SaveString() functions.
+ Use this to prepare the strings that you wish to append or prepend
+ to the output INI data.
+ */
+ Converter GetConverter() const {
+ return Converter(m_bStoreIsUtf8);
+ }
+
+ /*-----------------------------------------------------------------------*/
+ /** @} */
+
+private:
+ // copying is not permitted
+ CSimpleIniTempl(const CSimpleIniTempl &); // disabled
+ CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled
+
+ /** Parse the data looking for a file comment and store it if found.
+ */
+ SI_Error FindFileComment(
+ SI_CHAR *& a_pData,
+ bool a_bCopyStrings
+ );
+
+ /** Parse the data looking for the next valid entry. The memory pointed to
+ by a_pData is modified by inserting NULL characters. The pointer is
+ updated to the current location in the block of text.
+ */
+ bool FindEntry(
+ SI_CHAR *& a_pData,
+ const SI_CHAR *& a_pSection,
+ const SI_CHAR *& a_pKey,
+ const SI_CHAR *& a_pVal,
+ const SI_CHAR *& a_pComment
+ ) const;
+
+ /** Add the section/key/value to our data.
+
+ @param a_pSection Section name. Sections will be created if they
+ don't already exist.
+ @param a_pKey Key name. May be NULL to create an empty section.
+ Existing entries will be updated. New entries will
+ be created.
+ @param a_pValue Value for the key.
+ @param a_pComment Comment to be associated with the section or the
+ key. If a_pKey is NULL then it will be associated
+ with the section, otherwise the key. This must be
+ a string in full comment form already (have a
+ comment character starting every line).
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/AddEntry and AddEntry
+ with a_bForceReplace = true, is that the load
+ order and comment will be preserved this way.
+ @param a_bCopyStrings Should copies of the strings be made or not.
+ If false then the pointers will be used as is.
+ */
+ SI_Error AddEntry(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pValue,
+ const SI_CHAR * a_pComment,
+ bool a_bForceReplace,
+ bool a_bCopyStrings
+ );
+
+ /** Is the supplied character a whitespace character? */
+ inline bool IsSpace(SI_CHAR ch) const {
+ return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
+ }
+
+ /** Does the supplied character start a comment line? */
+ inline bool IsComment(SI_CHAR ch) const {
+ return (ch == ';' || ch == '#');
+ }
+
+
+ /** Skip over a newline character (or characters) for either DOS or UNIX */
+ inline void SkipNewLine(SI_CHAR *& a_pData) const {
+ a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1;
+ }
+
+ /** Make a copy of the supplied string, replacing the original pointer */
+ SI_Error CopyString(const SI_CHAR *& a_pString);
+
+ /** Delete a string from the copied strings buffer if necessary */
+ void DeleteString(const SI_CHAR * a_pString);
+
+ /** Internal use of our string comparison function */
+ bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const {
+ const static SI_STRLESS isLess = SI_STRLESS();
+ return isLess(a_pLeft, a_pRight);
+ }
+
+ bool IsMultiLineTag(const SI_CHAR * a_pData) const;
+ bool IsMultiLineData(const SI_CHAR * a_pData) const;
+ bool LoadMultiLineText(
+ SI_CHAR *& a_pData,
+ const SI_CHAR *& a_pVal,
+ const SI_CHAR * a_pTagName,
+ bool a_bAllowBlankLinesInComment = false
+ ) const;
+ bool IsNewLineChar(SI_CHAR a_c) const;
+
+ bool OutputMultiLineText(
+ OutputWriter & a_oOutput,
+ Converter & a_oConverter,
+ const SI_CHAR * a_pText
+ ) const;
+
+private:
+ /** Copy of the INI file data in our character format. This will be
+ modified when parsed to have NULL characters added after all
+ interesting string entries. All of the string pointers to sections,
+ keys and values point into this block of memory.
+ */
+ SI_CHAR * m_pData;
+
+ /** Length of the data that we have stored. Used when deleting strings
+ to determine if the string is stored here or in the allocated string
+ buffer.
+ */
+ size_t m_uDataLen;
+
+ /** File comment for this data, if one exists. */
+ const SI_CHAR * m_pFileComment;
+
+ /** Parsed INI data. Section -> (Key -> Value). */
+ TSection m_data;
+
+ /** This vector stores allocated memory for copies of strings that have
+ been supplied after the file load. It will be empty unless SetValue()
+ has been called.
+ */
+ TNamesDepend m_strings;
+
+ /** Is the format of our datafile UTF-8 or MBCS? */
+ bool m_bStoreIsUtf8;
+
+ /** Are multiple values permitted for the same key? */
+ bool m_bAllowMultiKey;
+
+ /** Are data values permitted to span multiple lines? */
+ bool m_bAllowMultiLine;
+
+ /** Should spaces be written out surrounding the equals sign? */
+ bool m_bSpaces;
+
+ /** Next order value, used to ensure sections and keys are output in the
+ same order that they are loaded/added.
+ */
+ int m_nOrder;
+};
+
+// ---------------------------------------------------------------------------
+// IMPLEMENTATION
+// ---------------------------------------------------------------------------
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl(
+ bool a_bIsUtf8,
+ bool a_bAllowMultiKey,
+ bool a_bAllowMultiLine
+ )
+ : m_pData(0)
+ , m_uDataLen(0)
+ , m_pFileComment(NULL)
+ , m_bStoreIsUtf8(a_bIsUtf8)
+ , m_bAllowMultiKey(a_bAllowMultiKey)
+ , m_bAllowMultiLine(a_bAllowMultiLine)
+ , m_bSpaces(true)
+ , m_nOrder(0)
+{ }
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::~CSimpleIniTempl()
+{
+ Reset();
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+void
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Reset()
+{
+ // remove all data
+ delete[] m_pData;
+ m_pData = NULL;
+ m_uDataLen = 0;
+ m_pFileComment = NULL;
+ if (!m_data.empty()) {
+ m_data.erase(m_data.begin(), m_data.end());
+ }
+
+ // remove all strings
+ if (!m_strings.empty()) {
+ typename TNamesDepend::iterator i = m_strings.begin();
+ for (; i != m_strings.end(); ++i) {
+ delete[] const_cast<SI_CHAR*>(i->pItem);
+ }
+ m_strings.erase(m_strings.begin(), m_strings.end());
+ }
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
+ const char * a_pszFile
+ )
+{
+ FILE * fp = NULL;
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ fopen_s(&fp, a_pszFile, "rb");
+#else // !__STDC_WANT_SECURE_LIB__
+ fp = fopen(a_pszFile, "rb");
+#endif // __STDC_WANT_SECURE_LIB__
+ if (!fp) {
+ return SI_FILE;
+ }
+ SI_Error rc = LoadFile(fp);
+ fclose(fp);
+ return rc;
+}
+
+#ifdef SI_HAS_WIDE_FILE
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
+ const SI_WCHAR_T * a_pwszFile
+ )
+{
+#ifdef _WIN32
+ FILE * fp = NULL;
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ _wfopen_s(&fp, a_pwszFile, L"rb");
+#else // !__STDC_WANT_SECURE_LIB__
+ fp = _wfopen(a_pwszFile, L"rb");
+#endif // __STDC_WANT_SECURE_LIB__
+ if (!fp) return SI_FILE;
+ SI_Error rc = LoadFile(fp);
+ fclose(fp);
+ return rc;
+#else // !_WIN32 (therefore SI_CONVERT_ICU)
+ char szFile[256];
+ u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
+ return LoadFile(szFile);
+#endif // _WIN32
+}
+#endif // SI_HAS_WIDE_FILE
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
+ FILE * a_fpFile
+ )
+{
+ // load the raw file data
+ int retval = fseek(a_fpFile, 0, SEEK_END);
+ if (retval != 0) {
+ return SI_FILE;
+ }
+ long lSize = ftell(a_fpFile);
+ if (lSize < 0) {
+ return SI_FILE;
+ }
+ if (lSize == 0) {
+ return SI_OK;
+ }
+ char * pData = new char[lSize];
+ if (!pData) {
+ return SI_NOMEM;
+ }
+ fseek(a_fpFile, 0, SEEK_SET);
+ size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
+ if (uRead != (size_t) lSize) {
+ delete[] pData;
+ return SI_FILE;
+ }
+
+ // convert the raw data to unicode
+ SI_Error rc = LoadData(pData, uRead);
+ delete[] pData;
+ return rc;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(
+ const char * a_pData,
+ size_t a_uDataLen
+ )
+{
+ SI_CONVERTER converter(m_bStoreIsUtf8);
+
+ if (a_uDataLen == 0) {
+ return SI_OK;
+ }
+
+ // consume the UTF-8 BOM if it exists
+ if (m_bStoreIsUtf8 && a_uDataLen >= 3) {
+ if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
+ a_pData += 3;
+ a_uDataLen -= 3;
+ }
+ }
+
+ // determine the length of the converted data
+ size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
+ if (uLen == (size_t)(-1)) {
+ return SI_FAIL;
+ }
+
+ // allocate memory for the data, ensure that there is a NULL
+ // terminator wherever the converted data ends
+ SI_CHAR * pData = new SI_CHAR[uLen+1];
+ if (!pData) {
+ return SI_NOMEM;
+ }
+ memset(pData, 0, sizeof(SI_CHAR)*(uLen+1));
+
+ // convert the data
+ if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
+ delete[] pData;
+ return SI_FAIL;
+ }
+
+ // parse it
+ const static SI_CHAR empty = 0;
+ SI_CHAR * pWork = pData;
+ const SI_CHAR * pSection = &empty;
+ const SI_CHAR * pItem = NULL;
+ const SI_CHAR * pVal = NULL;
+ const SI_CHAR * pComment = NULL;
+
+ // We copy the strings if we are loading data into this class when we
+ // already have stored some.
+ bool bCopyStrings = (m_pData != NULL);
+
+ // find a file comment if it exists, this is a comment that starts at the
+ // beginning of the file and continues until the first blank line.
+ SI_Error rc = FindFileComment(pWork, bCopyStrings);
+ if (rc < 0) return rc;
+
+ // add every entry in the file to the data table
+ while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
+ rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings);
+ if (rc < 0) return rc;
+ }
+
+ // store these strings if we didn't copy them
+ if (bCopyStrings) {
+ delete[] pData;
+ }
+ else {
+ m_pData = pData;
+ m_uDataLen = uLen+1;
+ }
+
+ return SI_OK;
+}
+
+#ifdef SI_SUPPORT_IOSTREAMS
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(
+ std::istream & a_istream
+ )
+{
+ std::string strData;
+ char szBuf[512];
+ do {
+ a_istream.get(szBuf, sizeof(szBuf), '\0');
+ strData.append(szBuf);
+ }
+ while (a_istream.good());
+ return LoadData(strData);
+}
+#endif // SI_SUPPORT_IOSTREAMS
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindFileComment(
+ SI_CHAR *& a_pData,
+ bool a_bCopyStrings
+ )
+{
+ // there can only be a single file comment
+ if (m_pFileComment) {
+ return SI_OK;
+ }
+
+ // Load the file comment as multi-line text, this will modify all of
+ // the newline characters to be single \n chars
+ if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
+ return SI_OK;
+ }
+
+ // copy the string if necessary
+ if (a_bCopyStrings) {
+ SI_Error rc = CopyString(m_pFileComment);
+ if (rc < 0) return rc;
+ }
+
+ return SI_OK;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
+ SI_CHAR *& a_pData,
+ const SI_CHAR *& a_pSection,
+ const SI_CHAR *& a_pKey,
+ const SI_CHAR *& a_pVal,
+ const SI_CHAR *& a_pComment
+ ) const
+{
+ a_pComment = NULL;
+
+ SI_CHAR * pTrail = NULL;
+ while (*a_pData) {
+ // skip spaces and empty lines
+ while (*a_pData && IsSpace(*a_pData)) {
+ ++a_pData;
+ }
+ if (!*a_pData) {
+ break;
+ }
+
+ // skip processing of comment lines but keep a pointer to
+ // the start of the comment.
+ if (IsComment(*a_pData)) {
+ LoadMultiLineText(a_pData, a_pComment, NULL, true);
+ continue;
+ }
+
+ // process section names
+ if (*a_pData == '[') {
+ // skip leading spaces
+ ++a_pData;
+ while (*a_pData && IsSpace(*a_pData)) {
+ ++a_pData;
+ }
+
+ // find the end of the section name (it may contain spaces)
+ // and convert it to lowercase as necessary
+ a_pSection = a_pData;
+ while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ // if it's an invalid line, just skip it
+ if (*a_pData != ']') {
+ continue;
+ }
+
+ // remove trailing spaces from the section
+ pTrail = a_pData - 1;
+ while (pTrail >= a_pSection && IsSpace(*pTrail)) {
+ --pTrail;
+ }
+ ++pTrail;
+ *pTrail = 0;
+
+ // skip to the end of the line
+ ++a_pData; // safe as checked that it == ']' above
+ while (*a_pData && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ a_pKey = NULL;
+ a_pVal = NULL;
+ return true;
+ }
+
+ // find the end of the key name (it may contain spaces)
+ // and convert it to lowercase as necessary
+ a_pKey = a_pData;
+ while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ // if it's an invalid line, just skip it
+ if (*a_pData != '=') {
+ continue;
+ }
+
+ // empty keys are invalid
+ if (a_pKey == a_pData) {
+ while (*a_pData && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+ continue;
+ }
+
+ // remove trailing spaces from the key
+ pTrail = a_pData - 1;
+ while (pTrail >= a_pKey && IsSpace(*pTrail)) {
+ --pTrail;
+ }
+ ++pTrail;
+ *pTrail = 0;
+
+ // skip leading whitespace on the value
+ ++a_pData; // safe as checked that it == '=' above
+ while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
+ ++a_pData;
+ }
+
+ // find the end of the value which is the end of this line
+ a_pVal = a_pData;
+ while (*a_pData && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ // remove trailing spaces from the value
+ pTrail = a_pData - 1;
+ if (*a_pData) { // prepare for the next round
+ SkipNewLine(a_pData);
+ }
+ while (pTrail >= a_pVal && IsSpace(*pTrail)) {
+ --pTrail;
+ }
+ ++pTrail;
+ *pTrail = 0;
+
+ // check for multi-line entries
+ if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
+ // skip the "<<<" to get the tag that will end the multiline
+ const SI_CHAR * pTagName = a_pVal + 3;
+ return LoadMultiLineText(a_pData, a_pVal, pTagName);
+ }
+
+ // return the standard entry
+ return true;
+ }
+
+ return false;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineTag(
+ const SI_CHAR * a_pVal
+ ) const
+{
+ // check for the "<<<" prefix for a multi-line entry
+ if (*a_pVal++ != '<') return false;
+ if (*a_pVal++ != '<') return false;
+ if (*a_pVal++ != '<') return false;
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData(
+ const SI_CHAR * a_pData
+ ) const
+{
+ // data is multi-line if it has any of the following features:
+ // * whitespace prefix
+ // * embedded newlines
+ // * whitespace suffix
+
+ // empty string
+ if (!*a_pData) {
+ return false;
+ }
+
+ // check for prefix
+ if (IsSpace(*a_pData)) {
+ return true;
+ }
+
+ // embedded newlines
+ while (*a_pData) {
+ if (IsNewLineChar(*a_pData)) {
+ return true;
+ }
+ ++a_pData;
+ }
+
+ // check for suffix
+ if (IsSpace(*--a_pData)) {
+ return true;
+ }
+
+ return false;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar(
+ SI_CHAR a_c
+ ) const
+{
+ return (a_c == '\n' || a_c == '\r');
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
+ SI_CHAR *& a_pData,
+ const SI_CHAR *& a_pVal,
+ const SI_CHAR * a_pTagName,
+ bool a_bAllowBlankLinesInComment
+ ) const
+{
+ // we modify this data to strip all newlines down to a single '\n'
+ // character. This means that on Windows we need to strip out some
+ // characters which will make the data shorter.
+ // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
+ // LINE1-LINE1\nLINE2-LINE2\0
+ // The pDataLine entry is the pointer to the location in memory that
+ // the current line needs to start to run following the existing one.
+ // This may be the same as pCurrLine in which case no move is needed.
+ SI_CHAR * pDataLine = a_pData;
+ SI_CHAR * pCurrLine;
+
+ // value starts at the current line
+ a_pVal = a_pData;
+
+ // find the end tag. This tag must start in column 1 and be
+ // followed by a newline. No whitespace removal is done while
+ // searching for this tag.
+ SI_CHAR cEndOfLineChar = *a_pData;
+ for(;;) {
+ // if we are loading comments then we need a comment character as
+ // the first character on every line
+ if (!a_pTagName && !IsComment(*a_pData)) {
+ // if we aren't allowing blank lines then we're done
+ if (!a_bAllowBlankLinesInComment) {
+ break;
+ }
+
+ // if we are allowing blank lines then we only include them
+ // in this comment if another comment follows, so read ahead
+ // to find out.
+ SI_CHAR * pCurr = a_pData;
+ int nNewLines = 0;
+ while (IsSpace(*pCurr)) {
+ if (IsNewLineChar(*pCurr)) {
+ ++nNewLines;
+ SkipNewLine(pCurr);
+ }
+ else {
+ ++pCurr;
+ }
+ }
+
+ // we have a comment, add the blank lines to the output
+ // and continue processing from here
+ if (IsComment(*pCurr)) {
+ for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n';
+ a_pData = pCurr;
+ continue;
+ }
+
+ // the comment ends here
+ break;
+ }
+
+ // find the end of this line
+ pCurrLine = a_pData;
+ while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
+
+ // move this line down to the location that it should be if necessary
+ if (pDataLine < pCurrLine) {
+ size_t nLen = (size_t) (a_pData - pCurrLine);
+ memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
+ pDataLine[nLen] = '\0';
+ }
+
+ // end the line with a NULL
+ cEndOfLineChar = *a_pData;
+ *a_pData = 0;
+
+ // if are looking for a tag then do the check now. This is done before
+ // checking for end of the data, so that if we have the tag at the end
+ // of the data then the tag is removed correctly.
+ if (a_pTagName &&
+ (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)))
+ {
+ break;
+ }
+
+ // if we are at the end of the data then we just automatically end
+ // this entry and return the current data.
+ if (!cEndOfLineChar) {
+ return true;
+ }
+
+ // otherwise we need to process this newline to ensure that it consists
+ // of just a single \n character.
+ pDataLine += (a_pData - pCurrLine);
+ *a_pData = cEndOfLineChar;
+ SkipNewLine(a_pData);
+ *pDataLine++ = '\n';
+ }
+
+ // if we didn't find a comment at all then return false
+ if (a_pVal == a_pData) {
+ a_pVal = NULL;
+ return false;
+ }
+
+ // the data (which ends at the end of the last line) needs to be
+ // null-terminated BEFORE before the newline character(s). If the
+ // user wants a new line in the multi-line data then they need to
+ // add an empty line before the tag.
+ *--pDataLine = '\0';
+
+ // if looking for a tag and if we aren't at the end of the data,
+ // then move a_pData to the start of the next line.
+ if (a_pTagName && cEndOfLineChar) {
+ SI_ASSERT(IsNewLineChar(cEndOfLineChar));
+ *a_pData = cEndOfLineChar;
+ SkipNewLine(a_pData);
+ }
+
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString(
+ const SI_CHAR *& a_pString
+ )
+{
+ size_t uLen = 0;
+ if (sizeof(SI_CHAR) == sizeof(char)) {
+ uLen = strlen((const char *)a_pString);
+ }
+ else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
+ uLen = wcslen((const wchar_t *)a_pString);
+ }
+ else {
+ for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
+ }
+ ++uLen; // NULL character
+ SI_CHAR * pCopy = new SI_CHAR[uLen];
+ if (!pCopy) {
+ return SI_NOMEM;
+ }
+ memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
+ m_strings.push_back(pCopy);
+ a_pString = pCopy;
+ return SI_OK;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pValue,
+ const SI_CHAR * a_pComment,
+ bool a_bForceReplace,
+ bool a_bCopyStrings
+ )
+{
+ SI_Error rc;
+ bool bInserted = false;
+
+ SI_ASSERT(!a_pComment || IsComment(*a_pComment));
+
+ // if we are copying strings then make a copy of the comment now
+ // because we will need it when we add the entry.
+ if (a_bCopyStrings && a_pComment) {
+ rc = CopyString(a_pComment);
+ if (rc < 0) return rc;
+ }
+
+ // create the section entry if necessary
+ typename TSection::iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ // if the section doesn't exist then we need a copy as the
+ // string needs to last beyond the end of this function
+ if (a_bCopyStrings) {
+ rc = CopyString(a_pSection);
+ if (rc < 0) return rc;
+ }
+
+ // only set the comment if this is a section only entry
+ Entry oSection(a_pSection, ++m_nOrder);
+ if (a_pComment && (!a_pKey || !a_pValue)) {
+ oSection.pComment = a_pComment;
+ }
+
+ typename TSection::value_type oEntry(oSection, TKeyVal());
+ typedef typename TSection::iterator SectionIterator;
+ std::pair<SectionIterator,bool> i = m_data.insert(oEntry);
+ iSection = i.first;
+ bInserted = true;
+ }
+ if (!a_pKey || !a_pValue) {
+ // section only entries are specified with pItem and pVal as NULL
+ return bInserted ? SI_INSERTED : SI_UPDATED;
+ }
+
+ // check for existence of the key
+ TKeyVal & keyval = iSection->second;
+ typename TKeyVal::iterator iKey = keyval.find(a_pKey);
+
+ // remove all existing entries but save the load order and
+ // comment of the first entry
+ int nLoadOrder = ++m_nOrder;
+ if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) {
+ const SI_CHAR * pComment = NULL;
+ while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) {
+ if (iKey->first.nOrder < nLoadOrder) {
+ nLoadOrder = iKey->first.nOrder;
+ pComment = iKey->first.pComment;
+ }
+ ++iKey;
+ }
+ if (pComment) {
+ DeleteString(a_pComment);
+ a_pComment = pComment;
+ CopyString(a_pComment);
+ }
+ Delete(a_pSection, a_pKey);
+ iKey = keyval.end();
+ }
+
+ // make string copies if necessary
+ bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
+ if (a_bCopyStrings) {
+ if (bForceCreateNewKey || iKey == keyval.end()) {
+ // if the key doesn't exist then we need a copy as the
+ // string needs to last beyond the end of this function
+ // because we will be inserting the key next
+ rc = CopyString(a_pKey);
+ if (rc < 0) return rc;
+ }
+
+ // we always need a copy of the value
+ rc = CopyString(a_pValue);
+ if (rc < 0) return rc;
+ }
+
+ // create the key entry
+ if (iKey == keyval.end() || bForceCreateNewKey) {
+ Entry oKey(a_pKey, nLoadOrder);
+ if (a_pComment) {
+ oKey.pComment = a_pComment;
+ }
+ typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL));
+ iKey = keyval.insert(oEntry);
+ bInserted = true;
+ }
+ iKey->second = a_pValue;
+ return bInserted ? SI_INSERTED : SI_UPDATED;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+const SI_CHAR *
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pDefault,
+ bool * a_pHasMultiple
+ ) const
+{
+ if (a_pHasMultiple) {
+ *a_pHasMultiple = false;
+ }
+ if (!a_pSection || !a_pKey) {
+ return a_pDefault;
+ }
+ typename TSection::const_iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return a_pDefault;
+ }
+ typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
+ if (iKeyVal == iSection->second.end()) {
+ return a_pDefault;
+ }
+
+ // check for multiple entries with the same key
+ if (m_bAllowMultiKey && a_pHasMultiple) {
+ typename TKeyVal::const_iterator iTemp = iKeyVal;
+ if (++iTemp != iSection->second.end()) {
+ if (!IsLess(a_pKey, iTemp->first.pItem)) {
+ *a_pHasMultiple = true;
+ }
+ }
+ }
+
+ return iKeyVal->second;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+long
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetLongValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ long a_nDefault,
+ bool * a_pHasMultiple
+ ) const
+{
+ // return the default if we don't have a value
+ const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
+ if (!pszValue || !*pszValue) return a_nDefault;
+
+ // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
+ char szValue[64] = { 0 };
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
+ return a_nDefault;
+ }
+
+ // handle the value as hex if prefaced with "0x"
+ long nValue = a_nDefault;
+ char * pszSuffix = szValue;
+ if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
+ if (!szValue[2]) return a_nDefault;
+ nValue = strtol(&szValue[2], &pszSuffix, 16);
+ }
+ else {
+ nValue = strtol(szValue, &pszSuffix, 10);
+ }
+
+ // any invalid strings will return the default value
+ if (*pszSuffix) {
+ return a_nDefault;
+ }
+
+ return nValue;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ long a_nValue,
+ const SI_CHAR * a_pComment,
+ bool a_bUseHex,
+ bool a_bForceReplace
+ )
+{
+ // use SetValue to create sections
+ if (!a_pSection || !a_pKey) return SI_FAIL;
+
+ // convert to an ASCII string
+ char szInput[64];
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
+#else // !__STDC_WANT_SECURE_LIB__
+ sprintf(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
+#endif // __STDC_WANT_SECURE_LIB__
+
+ // convert to output text
+ SI_CHAR szOutput[64];
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ c.ConvertFromStore(szInput, strlen(szInput) + 1,
+ szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
+
+ // actually add it
+ return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+double
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetDoubleValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ double a_nDefault,
+ bool * a_pHasMultiple
+ ) const
+{
+ // return the default if we don't have a value
+ const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
+ if (!pszValue || !*pszValue) return a_nDefault;
+
+ // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
+ char szValue[64] = { 0 };
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
+ return a_nDefault;
+ }
+
+ char * pszSuffix = NULL;
+ double nValue = strtod(szValue, &pszSuffix);
+
+ // any invalid strings will return the default value
+ if (!pszSuffix || *pszSuffix) {
+ return a_nDefault;
+ }
+
+ return nValue;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetDoubleValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ double a_nValue,
+ const SI_CHAR * a_pComment,
+ bool a_bForceReplace
+ )
+{
+ // use SetValue to create sections
+ if (!a_pSection || !a_pKey) return SI_FAIL;
+
+ // convert to an ASCII string
+ char szInput[64];
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ sprintf_s(szInput, "%f", a_nValue);
+#else // !__STDC_WANT_SECURE_LIB__
+ sprintf(szInput, "%f", a_nValue);
+#endif // __STDC_WANT_SECURE_LIB__
+
+ // convert to output text
+ SI_CHAR szOutput[64];
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ c.ConvertFromStore(szInput, strlen(szInput) + 1,
+ szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
+
+ // actually add it
+ return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetBoolValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bDefault,
+ bool * a_pHasMultiple
+ ) const
+{
+ // return the default if we don't have a value
+ const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
+ if (!pszValue || !*pszValue) return a_bDefault;
+
+ // we only look at the minimum number of characters
+ switch (pszValue[0]) {
+ case 't': case 'T': // true
+ case 'y': case 'Y': // yes
+ case '1': // 1 (one)
+ return true;
+
+ case 'f': case 'F': // false
+ case 'n': case 'N': // no
+ case '0': // 0 (zero)
+ return false;
+
+ case 'o': case 'O':
+ if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on
+ if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off
+ break;
+ }
+
+ // no recognized value, return the default
+ return a_bDefault;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetBoolValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bValue,
+ const SI_CHAR * a_pComment,
+ bool a_bForceReplace
+ )
+{
+ // use SetValue to create sections
+ if (!a_pSection || !a_pKey) return SI_FAIL;
+
+ // convert to an ASCII string
+ const char * pszInput = a_bValue ? "true" : "false";
+
+ // convert to output text
+ SI_CHAR szOutput[64];
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ c.ConvertFromStore(pszInput, strlen(pszInput) + 1,
+ szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
+
+ // actually add it
+ return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ TNamesDepend & a_values
+ ) const
+{
+ a_values.clear();
+
+ if (!a_pSection || !a_pKey) {
+ return false;
+ }
+ typename TSection::const_iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return false;
+ }
+ typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
+ if (iKeyVal == iSection->second.end()) {
+ return false;
+ }
+
+ // insert all values for this key
+ a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
+ if (m_bAllowMultiKey) {
+ ++iKeyVal;
+ while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {
+ a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
+ ++iKeyVal;
+ }
+ }
+
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+int
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSectionSize(
+ const SI_CHAR * a_pSection
+ ) const
+{
+ if (!a_pSection) {
+ return -1;
+ }
+
+ typename TSection::const_iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return -1;
+ }
+ const TKeyVal & section = iSection->second;
+
+ // if multi-key isn't permitted then the section size is
+ // the number of keys that we have.
+ if (!m_bAllowMultiKey || section.empty()) {
+ return (int) section.size();
+ }
+
+ // otherwise we need to count them
+ int nCount = 0;
+ const SI_CHAR * pLastKey = NULL;
+ typename TKeyVal::const_iterator iKeyVal = section.begin();
+ for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
+ if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
+ ++nCount;
+ pLastKey = iKeyVal->first.pItem;
+ }
+ }
+ return nCount;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+const typename CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::TKeyVal *
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSection(
+ const SI_CHAR * a_pSection
+ ) const
+{
+ if (a_pSection) {
+ typename TSection::const_iterator i = m_data.find(a_pSection);
+ if (i != m_data.end()) {
+ return &(i->second);
+ }
+ }
+ return 0;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+void
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections(
+ TNamesDepend & a_names
+ ) const
+{
+ a_names.clear();
+ typename TSection::const_iterator i = m_data.begin();
+ for (int n = 0; i != m_data.end(); ++i, ++n ) {
+ a_names.push_back(i->first);
+ }
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllKeys(
+ const SI_CHAR * a_pSection,
+ TNamesDepend & a_names
+ ) const
+{
+ a_names.clear();
+
+ if (!a_pSection) {
+ return false;
+ }
+
+ typename TSection::const_iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return false;
+ }
+
+ const TKeyVal & section = iSection->second;
+ const SI_CHAR * pLastKey = NULL;
+ typename TKeyVal::const_iterator iKeyVal = section.begin();
+ for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) {
+ if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
+ a_names.push_back(iKeyVal->first);
+ pLastKey = iKeyVal->first.pItem;
+ }
+ }
+
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
+ const char * a_pszFile,
+ bool a_bAddSignature
+ ) const
+{
+ FILE * fp = NULL;
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ fopen_s(&fp, a_pszFile, "wb");
+#else // !__STDC_WANT_SECURE_LIB__
+ fp = fopen(a_pszFile, "wb");
+#endif // __STDC_WANT_SECURE_LIB__
+ if (!fp) return SI_FILE;
+ SI_Error rc = SaveFile(fp, a_bAddSignature);
+ fclose(fp);
+ return rc;
+}
+
+#ifdef SI_HAS_WIDE_FILE
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
+ const SI_WCHAR_T * a_pwszFile,
+ bool a_bAddSignature
+ ) const
+{
+#ifdef _WIN32
+ FILE * fp = NULL;
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ _wfopen_s(&fp, a_pwszFile, L"wb");
+#else // !__STDC_WANT_SECURE_LIB__
+ fp = _wfopen(a_pwszFile, L"wb");
+#endif // __STDC_WANT_SECURE_LIB__
+ if (!fp) return SI_FILE;
+ SI_Error rc = SaveFile(fp, a_bAddSignature);
+ fclose(fp);
+ return rc;
+#else // !_WIN32 (therefore SI_CONVERT_ICU)
+ char szFile[256];
+ u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
+ return SaveFile(szFile, a_bAddSignature);
+#endif // _WIN32
+}
+#endif // SI_HAS_WIDE_FILE
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
+ FILE * a_pFile,
+ bool a_bAddSignature
+ ) const
+{
+ FileWriter writer(a_pFile);
+ return Save(writer, a_bAddSignature);
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
+ OutputWriter & a_oOutput,
+ bool a_bAddSignature
+ ) const
+{
+ Converter convert(m_bStoreIsUtf8);
+
+ // add the UTF-8 signature if it is desired
+ if (m_bStoreIsUtf8 && a_bAddSignature) {
+ a_oOutput.Write(SI_UTF8_SIGNATURE);
+ }
+
+ // get all of the sections sorted in load order
+ TNamesDepend oSections;
+ GetAllSections(oSections);
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+ oSections.sort();
+#elif defined(__BORLANDC__)
+ oSections.sort(Entry::LoadOrder());
+#else
+ oSections.sort(typename Entry::LoadOrder());
+#endif
+
+ // write the file comment if we have one
+ bool bNeedNewLine = false;
+ if (m_pFileComment) {
+ if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
+ return SI_FAIL;
+ }
+ bNeedNewLine = true;
+ }
+
+ // iterate through our sections and output the data
+ typename TNamesDepend::const_iterator iSection = oSections.begin();
+ for ( ; iSection != oSections.end(); ++iSection ) {
+ // write out the comment if there is one
+ if (iSection->pComment) {
+ if (bNeedNewLine) {
+ a_oOutput.Write(SI_NEWLINE_A);
+ a_oOutput.Write(SI_NEWLINE_A);
+ }
+ if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) {
+ return SI_FAIL;
+ }
+ bNeedNewLine = false;
+ }
+
+ if (bNeedNewLine) {
+ a_oOutput.Write(SI_NEWLINE_A);
+ a_oOutput.Write(SI_NEWLINE_A);
+ bNeedNewLine = false;
+ }
+
+ // write the section (unless there is no section name)
+ if (*iSection->pItem) {
+ if (!convert.ConvertToStore(iSection->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write("[");
+ a_oOutput.Write(convert.Data());
+ a_oOutput.Write("]");
+ a_oOutput.Write(SI_NEWLINE_A);
+ }
+
+ // get all of the keys sorted in load order
+ TNamesDepend oKeys;
+ GetAllKeys(iSection->pItem, oKeys);
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+ oKeys.sort();
+#elif defined(__BORLANDC__)
+ oKeys.sort(Entry::LoadOrder());
+#else
+ oKeys.sort(typename Entry::LoadOrder());
+#endif
+
+ // write all keys and values
+ typename TNamesDepend::const_iterator iKey = oKeys.begin();
+ for ( ; iKey != oKeys.end(); ++iKey) {
+ // get all values for this key
+ TNamesDepend oValues;
+ GetAllValues(iSection->pItem, iKey->pItem, oValues);
+
+ typename TNamesDepend::const_iterator iValue = oValues.begin();
+ for ( ; iValue != oValues.end(); ++iValue) {
+ // write out the comment if there is one
+ if (iValue->pComment) {
+ a_oOutput.Write(SI_NEWLINE_A);
+ if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) {
+ return SI_FAIL;
+ }
+ }
+
+ // write the key
+ if (!convert.ConvertToStore(iKey->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write(convert.Data());
+
+ // write the value
+ if (!convert.ConvertToStore(iValue->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write(m_bSpaces ? " = " : "=");
+ if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
+ // multi-line data needs to be processed specially to ensure
+ // that we use the correct newline format for the current system
+ a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
+ if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write("END_OF_TEXT");
+ }
+ else {
+ a_oOutput.Write(convert.Data());
+ }
+ a_oOutput.Write(SI_NEWLINE_A);
+ }
+ }
+
+ bNeedNewLine = true;
+ }
+
+ return SI_OK;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::OutputMultiLineText(
+ OutputWriter & a_oOutput,
+ Converter & a_oConverter,
+ const SI_CHAR * a_pText
+ ) const
+{
+ const SI_CHAR * pEndOfLine;
+ SI_CHAR cEndOfLineChar = *a_pText;
+ while (cEndOfLineChar) {
+ // find the end of this line
+ pEndOfLine = a_pText;
+ for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;
+ cEndOfLineChar = *pEndOfLine;
+
+ // temporarily null terminate, convert and output the line
+ *const_cast<SI_CHAR*>(pEndOfLine) = 0;
+ if (!a_oConverter.ConvertToStore(a_pText)) {
+ return false;
+ }
+ *const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar;
+ a_pText += (pEndOfLine - a_pText) + 1;
+ a_oOutput.Write(a_oConverter.Data());
+ a_oOutput.Write(SI_NEWLINE_A);
+ }
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bRemoveEmpty
+ )
+{
+ if (!a_pSection) {
+ return false;
+ }
+
+ typename TSection::iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return false;
+ }
+
+ // remove a single key if we have a keyname
+ if (a_pKey) {
+ typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
+ if (iKeyVal == iSection->second.end()) {
+ return false;
+ }
+
+ // remove any copied strings and then the key
+ typename TKeyVal::iterator iDelete;
+ do {
+ iDelete = iKeyVal++;
+
+ DeleteString(iDelete->first.pItem);
+ DeleteString(iDelete->second);
+ iSection->second.erase(iDelete);
+ }
+ while (iKeyVal != iSection->second.end()
+ && !IsLess(a_pKey, iKeyVal->first.pItem));
+
+ // done now if the section is not empty or we are not pruning away
+ // the empty sections. Otherwise let it fall through into the section
+ // deletion code
+ if (!a_bRemoveEmpty || !iSection->second.empty()) {
+ return true;
+ }
+ }
+ else {
+ // delete all copied strings from this section. The actual
+ // entries will be removed when the section is removed.
+ typename TKeyVal::iterator iKeyVal = iSection->second.begin();
+ for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
+ DeleteString(iKeyVal->first.pItem);
+ DeleteString(iKeyVal->second);
+ }
+ }
+
+ // delete the section itself
+ DeleteString(iSection->first.pItem);
+ m_data.erase(iSection);
+
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+void
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString(
+ const SI_CHAR * a_pString
+ )
+{
+ // strings may exist either inside the data block, or they will be
+ // individually allocated and stored in m_strings. We only physically
+ // delete those stored in m_strings.
+ if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
+ typename TNamesDepend::iterator i = m_strings.begin();
+ for (;i != m_strings.end(); ++i) {
+ if (a_pString == i->pItem) {
+ delete[] const_cast<SI_CHAR*>(i->pItem);
+ m_strings.erase(i);
+ break;
+ }
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+// CONVERSION FUNCTIONS
+// ---------------------------------------------------------------------------
+
+// Defines the conversion classes for different libraries. Before including
+// SimpleIni.h, set the converter that you wish you use by defining one of the
+// following symbols.
+//
+// SI_CONVERT_GENERIC Use the Unicode reference conversion library in
+// the accompanying files ConvertUTF.h/c
+// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
+// ICU headers on include path and icuuc.lib
+// SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
+
+#if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
+# ifdef _WIN32
+# define SI_CONVERT_WIN32
+# else
+# define SI_CONVERT_GENERIC
+# endif
+#endif
+
+/**
+ * Generic case-sensitive less than comparison. This class returns numerically
+ * ordered ASCII case-sensitive text for all possible sizes and types of
+ * SI_CHAR.
+ */
+template<class SI_CHAR>
+struct SI_GenericCase {
+ bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
+ long cmp;
+ for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
+ cmp = (long) *pLeft - (long) *pRight;
+ if (cmp != 0) {
+ return cmp < 0;
+ }
+ }
+ return *pRight != 0;
+ }
+};
+
+/**
+ * Generic ASCII case-insensitive less than comparison. This class returns
+ * numerically ordered ASCII case-insensitive text for all possible sizes
+ * and types of SI_CHAR. It is not safe for MBCS text comparison where
+ * ASCII A-Z characters are used in the encoding of multi-byte characters.
+ */
+template<class SI_CHAR>
+struct SI_GenericNoCase {
+ inline SI_CHAR locase(SI_CHAR ch) const {
+ return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
+ }
+ bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
+ long cmp;
+ for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
+ cmp = (long) locase(*pLeft) - (long) locase(*pRight);
+ if (cmp != 0) {
+ return cmp < 0;
+ }
+ }
+ return *pRight != 0;
+ }
+};
+
+/**
+ * Null conversion class for MBCS/UTF-8 to char (or equivalent).
+ */
+template<class SI_CHAR>
+class SI_ConvertA {
+ bool m_bStoreIsUtf8;
+protected:
+ SI_ConvertA() { }
+public:
+ SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
+
+ /* copy and assignment */
+ SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }
+ SI_ConvertA & operator=(const SI_ConvertA & rhs) {
+ m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
+ return *this;
+ }
+
+ /** Calculate the number of SI_CHAR required for converting the input
+ * from the storage format. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @return Number of SI_CHAR required by the string when
+ * converted. If there are embedded NULL bytes in the
+ * input data, only the string up and not including
+ * the NULL byte will be converted.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen)
+ {
+ (void)a_pInputData;
+ SI_ASSERT(a_uInputDataLen != (size_t) -1);
+
+ // ASCII/MBCS/UTF-8 needs no conversion
+ return a_uInputDataLen;
+ }
+
+ /** Convert the input string from the storage format to SI_CHAR.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @param a_pOutputData Pointer to the output buffer to received the
+ * converted data.
+ * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
+ * @return true if all of the input data was successfully
+ * converted.
+ */
+ bool ConvertFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen,
+ SI_CHAR * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ // ASCII/MBCS/UTF-8 needs no conversion
+ if (a_uInputDataLen > a_uOutputDataSize) {
+ return false;
+ }
+ memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
+ return true;
+ }
+
+ /** Calculate the number of char required by the storage format of this
+ * data. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated string to calculate the number of
+ * bytes required to be converted to storage format.
+ * @return Number of bytes required by the string when
+ * converted to storage format. This size always
+ * includes space for the terminating NULL character.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeToStore(
+ const SI_CHAR * a_pInputData)
+ {
+ // ASCII/MBCS/UTF-8 needs no conversion
+ return strlen((const char *)a_pInputData) + 1;
+ }
+
+ /** Convert the input string to the storage format of this data.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated source string to convert. All of
+ * the data will be converted including the
+ * terminating NULL character.
+ * @param a_pOutputData Pointer to the buffer to receive the converted
+ * string.
+ * @param a_uOutputDataSize Size of the output buffer in char.
+ * @return true if all of the input data, including the
+ * terminating NULL character was successfully
+ * converted.
+ */
+ bool ConvertToStore(
+ const SI_CHAR * a_pInputData,
+ char * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ // calc input string length (SI_CHAR type and size independent)
+ size_t uInputLen = strlen((const char *)a_pInputData) + 1;
+ if (uInputLen > a_uOutputDataSize) {
+ return false;
+ }
+
+ // ascii/UTF-8 needs no conversion
+ memcpy(a_pOutputData, a_pInputData, uInputLen);
+ return true;
+ }
+};
+
+
+// ---------------------------------------------------------------------------
+// SI_CONVERT_GENERIC
+// ---------------------------------------------------------------------------
+#ifdef SI_CONVERT_GENERIC
+
+#define SI_Case SI_GenericCase
+#define SI_NoCase SI_GenericNoCase
+
+#include <wchar.h>
+#include "ConvertUTF.h"
+
+/**
+ * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference
+ * library functions. This can be used on all platforms.
+ */
+template<class SI_CHAR>
+class SI_ConvertW {
+ bool m_bStoreIsUtf8;
+protected:
+ SI_ConvertW() { }
+public:
+ SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
+
+ /* copy and assignment */
+ SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
+ SI_ConvertW & operator=(const SI_ConvertW & rhs) {
+ m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
+ return *this;
+ }
+
+ /** Calculate the number of SI_CHAR required for converting the input
+ * from the storage format. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @return Number of SI_CHAR required by the string when
+ * converted. If there are embedded NULL bytes in the
+ * input data, only the string up and not including
+ * the NULL byte will be converted.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen)
+ {
+ SI_ASSERT(a_uInputDataLen != (size_t) -1);
+
+ if (m_bStoreIsUtf8) {
+ // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
+ // so we just return the same number of characters required as for
+ // the source text.
+ return a_uInputDataLen;
+ }
+ else {
+ return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
+ }
+ }
+
+ /** Convert the input string from the storage format to SI_CHAR.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @param a_pOutputData Pointer to the output buffer to received the
+ * converted data.
+ * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
+ * @return true if all of the input data was successfully
+ * converted.
+ */
+ bool ConvertFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen,
+ SI_CHAR * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ if (m_bStoreIsUtf8) {
+ // This uses the Unicode reference implementation to do the
+ // conversion from UTF-8 to wchar_t. The required files are
+ // ConvertUTF.h and ConvertUTF.c which should be included in
+ // the distribution but are publically available from unicode.org
+ // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
+ ConversionResult retval;
+ const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
+ if (sizeof(wchar_t) == sizeof(UTF32)) {
+ UTF32 * pUtf32 = (UTF32 *) a_pOutputData;
+ retval = ConvertUTF8toUTF32(
+ &pUtf8, pUtf8 + a_uInputDataLen,
+ &pUtf32, pUtf32 + a_uOutputDataSize,
+ lenientConversion);
+ }
+ else if (sizeof(wchar_t) == sizeof(UTF16)) {
+ UTF16 * pUtf16 = (UTF16 *) a_pOutputData;
+ retval = ConvertUTF8toUTF16(
+ &pUtf8, pUtf8 + a_uInputDataLen,
+ &pUtf16, pUtf16 + a_uOutputDataSize,
+ lenientConversion);
+ }
+ return retval == conversionOK;
+ }
+ else {
+ size_t retval = mbstowcs(a_pOutputData,
+ a_pInputData, a_uOutputDataSize);
+ return retval != (size_t)(-1);
+ }
+ }
+
+ /** Calculate the number of char required by the storage format of this
+ * data. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated string to calculate the number of
+ * bytes required to be converted to storage format.
+ * @return Number of bytes required by the string when
+ * converted to storage format. This size always
+ * includes space for the terminating NULL character.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeToStore(
+ const SI_CHAR * a_pInputData)
+ {
+ if (m_bStoreIsUtf8) {
+ // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
+ size_t uLen = 0;
+ while (a_pInputData[uLen]) {
+ ++uLen;
+ }
+ return (6 * uLen) + 1;
+ }
+ else {
+ size_t uLen = wcstombs(NULL, a_pInputData, 0);
+ if (uLen == (size_t)(-1)) {
+ return uLen;
+ }
+ return uLen + 1; // include NULL terminator
+ }
+ }
+
+ /** Convert the input string to the storage format of this data.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated source string to convert. All of
+ * the data will be converted including the
+ * terminating NULL character.
+ * @param a_pOutputData Pointer to the buffer to receive the converted
+ * string.
+ * @param a_uOutputDataSize Size of the output buffer in char.
+ * @return true if all of the input data, including the
+ * terminating NULL character was successfully
+ * converted.
+ */
+ bool ConvertToStore(
+ const SI_CHAR * a_pInputData,
+ char * a_pOutputData,
+ size_t a_uOutputDataSize
+ )
+ {
+ if (m_bStoreIsUtf8) {
+ // calc input string length (SI_CHAR type and size independent)
+ size_t uInputLen = 0;
+ while (a_pInputData[uInputLen]) {
+ ++uInputLen;
+ }
+ ++uInputLen; // include the NULL char
+
+ // This uses the Unicode reference implementation to do the
+ // conversion from wchar_t to UTF-8. The required files are
+ // ConvertUTF.h and ConvertUTF.c which should be included in
+ // the distribution but are publically available from unicode.org
+ // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
+ ConversionResult retval;
+ UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
+ if (sizeof(wchar_t) == sizeof(UTF32)) {
+ const UTF32 * pUtf32 = (const UTF32 *) a_pInputData;
+ retval = ConvertUTF32toUTF8(
+ &pUtf32, pUtf32 + uInputLen,
+ &pUtf8, pUtf8 + a_uOutputDataSize,
+ lenientConversion);
+ }
+ else if (sizeof(wchar_t) == sizeof(UTF16)) {
+ const UTF16 * pUtf16 = (const UTF16 *) a_pInputData;
+ retval = ConvertUTF16toUTF8(
+ &pUtf16, pUtf16 + uInputLen,
+ &pUtf8, pUtf8 + a_uOutputDataSize,
+ lenientConversion);
+ }
+ return retval == conversionOK;
+ }
+ else {
+ size_t retval = wcstombs(a_pOutputData,
+ a_pInputData, a_uOutputDataSize);
+ return retval != (size_t) -1;
+ }
+ }
+};
+
+#endif // SI_CONVERT_GENERIC
+
+
+// ---------------------------------------------------------------------------
+// SI_CONVERT_ICU
+// ---------------------------------------------------------------------------
+#ifdef SI_CONVERT_ICU
+
+#define SI_Case SI_GenericCase
+#define SI_NoCase SI_GenericNoCase
+
+#include <unicode/ucnv.h>
+
+/**
+ * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms.
+ */
+template<class SI_CHAR>
+class SI_ConvertW {
+ const char * m_pEncoding;
+ UConverter * m_pConverter;
+protected:
+ SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
+public:
+ SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
+ m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
+ }
+
+ /* copy and assignment */
+ SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
+ SI_ConvertW & operator=(const SI_ConvertW & rhs) {
+ m_pEncoding = rhs.m_pEncoding;
+ m_pConverter = NULL;
+ return *this;
+ }
+ ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); }
+
+ /** Calculate the number of UChar required for converting the input
+ * from the storage format. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to UChar.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @return Number of UChar required by the string when
+ * converted. If there are embedded NULL bytes in the
+ * input data, only the string up and not including
+ * the NULL byte will be converted.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen)
+ {
+ SI_ASSERT(a_uInputDataLen != (size_t) -1);
+
+ UErrorCode nError;
+
+ if (!m_pConverter) {
+ nError = U_ZERO_ERROR;
+ m_pConverter = ucnv_open(m_pEncoding, &nError);
+ if (U_FAILURE(nError)) {
+ return (size_t) -1;
+ }
+ }
+
+ nError = U_ZERO_ERROR;
+ int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,
+ a_pInputData, (int32_t) a_uInputDataLen, &nError);
+ if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
+ return (size_t) -1;
+ }
+
+ return (size_t) nLen;
+ }
+
+ /** Convert the input string from the storage format to UChar.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to UChar.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @param a_pOutputData Pointer to the output buffer to received the
+ * converted data.
+ * @param a_uOutputDataSize Size of the output buffer in UChar.
+ * @return true if all of the input data was successfully
+ * converted.
+ */
+ bool ConvertFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen,
+ UChar * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ UErrorCode nError;
+
+ if (!m_pConverter) {
+ nError = U_ZERO_ERROR;
+ m_pConverter = ucnv_open(m_pEncoding, &nError);
+ if (U_FAILURE(nError)) {
+ return false;
+ }
+ }
+
+ nError = U_ZERO_ERROR;
+ ucnv_toUChars(m_pConverter,
+ a_pOutputData, (int32_t) a_uOutputDataSize,
+ a_pInputData, (int32_t) a_uInputDataLen, &nError);
+ if (U_FAILURE(nError)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /** Calculate the number of char required by the storage format of this
+ * data. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated string to calculate the number of
+ * bytes required to be converted to storage format.
+ * @return Number of bytes required by the string when
+ * converted to storage format. This size always
+ * includes space for the terminating NULL character.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeToStore(
+ const UChar * a_pInputData)
+ {
+ UErrorCode nError;
+
+ if (!m_pConverter) {
+ nError = U_ZERO_ERROR;
+ m_pConverter = ucnv_open(m_pEncoding, &nError);
+ if (U_FAILURE(nError)) {
+ return (size_t) -1;
+ }
+ }
+
+ nError = U_ZERO_ERROR;
+ int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,
+ a_pInputData, -1, &nError);
+ if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
+ return (size_t) -1;
+ }
+
+ return (size_t) nLen + 1;
+ }
+
+ /** Convert the input string to the storage format of this data.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated source string to convert. All of
+ * the data will be converted including the
+ * terminating NULL character.
+ * @param a_pOutputData Pointer to the buffer to receive the converted
+ * string.
+ * @param a_pOutputDataSize Size of the output buffer in char.
+ * @return true if all of the input data, including the
+ * terminating NULL character was successfully
+ * converted.
+ */
+ bool ConvertToStore(
+ const UChar * a_pInputData,
+ char * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ UErrorCode nError;
+
+ if (!m_pConverter) {
+ nError = U_ZERO_ERROR;
+ m_pConverter = ucnv_open(m_pEncoding, &nError);
+ if (U_FAILURE(nError)) {
+ return false;
+ }
+ }
+
+ nError = U_ZERO_ERROR;
+ ucnv_fromUChars(m_pConverter,
+ a_pOutputData, (int32_t) a_uOutputDataSize,
+ a_pInputData, -1, &nError);
+ if (U_FAILURE(nError)) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+#endif // SI_CONVERT_ICU
+
+
+// ---------------------------------------------------------------------------
+// SI_CONVERT_WIN32
+// ---------------------------------------------------------------------------
+#ifdef SI_CONVERT_WIN32
+
+#define SI_Case SI_GenericCase
+
+// Windows CE doesn't have errno or MBCS libraries
+#ifdef _WIN32_WCE
+# ifndef SI_NO_MBCS
+# define SI_NO_MBCS
+# endif
+#endif
+
+#include <windows.h>
+#ifdef SI_NO_MBCS
+# define SI_NoCase SI_GenericNoCase
+#else // !SI_NO_MBCS
+/**
+ * Case-insensitive comparison class using Win32 MBCS functions. This class
+ * returns a case-insensitive semi-collation order for MBCS text. It may not
+ * be safe for UTF-8 text returned in char format as we don't know what
+ * characters will be folded by the function! Therefore, if you are using
+ * SI_CHAR == char and SetUnicode(true), then you need to use the generic
+ * SI_NoCase class instead.
+ */
+#include <mbstring.h>
+template<class SI_CHAR>
+struct SI_NoCase {
+ bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
+ if (sizeof(SI_CHAR) == sizeof(char)) {
+ return _mbsicmp((const unsigned char *)pLeft,
+ (const unsigned char *)pRight) < 0;
+ }
+ if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
+ return _wcsicmp((const wchar_t *)pLeft,
+ (const wchar_t *)pRight) < 0;
+ }
+ return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight);
+ }
+};
+#endif // SI_NO_MBCS
+
+/**
+ * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses
+ * only the Win32 functions and doesn't require the external Unicode UTF-8
+ * conversion library. It will not work on Windows 95 without using Microsoft
+ * Layer for Unicode in your application.
+ */
+template<class SI_CHAR>
+class SI_ConvertW {
+ UINT m_uCodePage;
+protected:
+ SI_ConvertW() { }
+public:
+ SI_ConvertW(bool a_bStoreIsUtf8) {
+ m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP;
+ }
+
+ /* copy and assignment */
+ SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
+ SI_ConvertW & operator=(const SI_ConvertW & rhs) {
+ m_uCodePage = rhs.m_uCodePage;
+ return *this;
+ }
+
+ /** Calculate the number of SI_CHAR required for converting the input
+ * from the storage format. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @return Number of SI_CHAR required by the string when
+ * converted. If there are embedded NULL bytes in the
+ * input data, only the string up and not including
+ * the NULL byte will be converted.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen)
+ {
+ SI_ASSERT(a_uInputDataLen != (size_t) -1);
+
+ int retval = MultiByteToWideChar(
+ m_uCodePage, 0,
+ a_pInputData, (int) a_uInputDataLen,
+ 0, 0);
+ return (size_t)(retval > 0 ? retval : -1);
+ }
+
+ /** Convert the input string from the storage format to SI_CHAR.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @param a_pOutputData Pointer to the output buffer to received the
+ * converted data.
+ * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
+ * @return true if all of the input data was successfully
+ * converted.
+ */
+ bool ConvertFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen,
+ SI_CHAR * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ int nSize = MultiByteToWideChar(
+ m_uCodePage, 0,
+ a_pInputData, (int) a_uInputDataLen,
+ (wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
+ return (nSize > 0);
+ }
+
+ /** Calculate the number of char required by the storage format of this
+ * data. The storage format is always UTF-8.
+ *
+ * @param a_pInputData NULL terminated string to calculate the number of
+ * bytes required to be converted to storage format.
+ * @return Number of bytes required by the string when
+ * converted to storage format. This size always
+ * includes space for the terminating NULL character.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeToStore(
+ const SI_CHAR * a_pInputData)
+ {
+ int retval = WideCharToMultiByte(
+ m_uCodePage, 0,
+ (const wchar_t *) a_pInputData, -1,
+ 0, 0, 0, 0);
+ return (size_t) (retval > 0 ? retval : -1);
+ }
+
+ /** Convert the input string to the storage format of this data.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated source string to convert. All of
+ * the data will be converted including the
+ * terminating NULL character.
+ * @param a_pOutputData Pointer to the buffer to receive the converted
+ * string.
+ * @param a_pOutputDataSize Size of the output buffer in char.
+ * @return true if all of the input data, including the
+ * terminating NULL character was successfully
+ * converted.
+ */
+ bool ConvertToStore(
+ const SI_CHAR * a_pInputData,
+ char * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ int retval = WideCharToMultiByte(
+ m_uCodePage, 0,
+ (const wchar_t *) a_pInputData, -1,
+ a_pOutputData, (int) a_uOutputDataSize, 0, 0);
+ return retval > 0;
+ }
+};
+
+#endif // SI_CONVERT_WIN32
+
+
+// ---------------------------------------------------------------------------
+// TYPE DEFINITIONS
+// ---------------------------------------------------------------------------
+
+typedef CSimpleIniTempl<char,
+ SI_NoCase<char>,SI_ConvertA<char> > CSimpleIniA;
+typedef CSimpleIniTempl<char,
+ SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA;
+
+#if defined(SI_CONVERT_ICU)
+typedef CSimpleIniTempl<UChar,
+ SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;
+typedef CSimpleIniTempl<UChar,
+ SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;
+#else
+typedef CSimpleIniTempl<wchar_t,
+ SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;
+typedef CSimpleIniTempl<wchar_t,
+ SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;
+#endif
+
+#ifdef _UNICODE
+# define CSimpleIni CSimpleIniW
+# define CSimpleIniCase CSimpleIniCaseW
+# define SI_NEWLINE SI_NEWLINE_W
+#else // !_UNICODE
+# define CSimpleIni CSimpleIniA
+# define CSimpleIniCase CSimpleIniCaseA
+# define SI_NEWLINE SI_NEWLINE_A
+#endif // _UNICODE
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif // INCLUDED_SimpleIni_h
+
diff --git a/lib/simpleini/SimpleIni.sln b/lib/simpleini/SimpleIni.sln
new file mode 100644
index 0000000..6fd8a73
--- /dev/null
+++ b/lib/simpleini/SimpleIni.sln
@@ -0,0 +1,29 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleIni", "SimpleIni.vcproj", "{C23240A6-AA9D-4827-AF06-C98E97CA6DFB}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ Debug = Debug
+ Debug Unicode = Debug Unicode
+ Release = Release
+ Release Unicode = Release Unicode
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Debug.ActiveCfg = Debug|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Debug.Build.0 = Debug|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Debug Unicode.ActiveCfg = Debug Unicode|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Debug Unicode.Build.0 = Debug Unicode|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Release.ActiveCfg = Release|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Release.Build.0 = Release|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Release Unicode.ActiveCfg = Release Unicode|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Release Unicode.Build.0 = Release Unicode|Win32
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/lib/simpleini/SimpleIni.vcproj b/lib/simpleini/SimpleIni.vcproj
new file mode 100644
index 0000000..e5ee0db
--- /dev/null
+++ b/lib/simpleini/SimpleIni.vcproj
@@ -0,0 +1,291 @@
+<?xml version="1.0" encoding="shift_jis"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="SimpleIni"
+ ProjectGUID="{C23240A6-AA9D-4827-AF06-C98E97CA6DFB}"
+ RootNamespace="SimpleIni"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Debug"
+ IntermediateDirectory="Debug"
+ ConfigurationType="1"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="5"
+ UsePrecompiledHeader="0"
+ WarningLevel="4"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/testsi.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/SimpleIni.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Release"
+ IntermediateDirectory="Release"
+ ConfigurationType="1"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="4"
+ UsePrecompiledHeader="0"
+ WarningLevel="4"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/testsi.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="TRUE"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Debug Unicode|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="5"
+ UsePrecompiledHeader="0"
+ WarningLevel="4"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/testsi.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/SimpleIni.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release Unicode|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1">
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="4"
+ UsePrecompiledHeader="0"
+ WarningLevel="4"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/testsi.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="TRUE"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+ <File
+ RelativePath=".\snippets.cpp">
+ </File>
+ <File
+ RelativePath=".\test1.cpp">
+ </File>
+ <File
+ RelativePath=".\testsi.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Library Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+ <File
+ RelativePath=".\SimpleIni.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
+ <File
+ RelativePath=".\Makefile">
+ </File>
+ <File
+ RelativePath=".\testsi-EUCJP.ini">
+ </File>
+ <File
+ RelativePath=".\testsi-SJIS.ini">
+ </File>
+ <File
+ RelativePath=".\testsi-UTF8.ini">
+ </File>
+ </Filter>
+ <Filter
+ Name="Conversion Files"
+ Filter="">
+ <File
+ RelativePath=".\ConvertUTF.c">
+ </File>
+ <File
+ RelativePath=".\ConvertUTF.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Documentation"
+ Filter="">
+ <File
+ RelativePath=".\simpleini.doxy">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="if not exist &quot;C:\Program Files\doxygen\bin\doxygen.exe&quot; goto done
+
+echo Generating documentation...
+&quot;C:\Program Files\doxygen\bin\doxygen.exe&quot; $(InputDir)simpleini.doxy
+
+:done
+"
+ Outputs="d:\src\simpleini-doc\html\index.html"/>
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Tests"
+ Filter="">
+ <File
+ RelativePath=".\test1-expected.ini">
+ </File>
+ <File
+ RelativePath=".\test1-input.ini">
+ </File>
+ <File
+ RelativePath=".\test1-output.ini">
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/lib/simpleini/ini.syn b/lib/simpleini/ini.syn
new file mode 100644
index 0000000..718c560
--- /dev/null
+++ b/lib/simpleini/ini.syn
@@ -0,0 +1,36 @@
+; Syntax file for ini files - contributed by Brodie Thiesfield
+;
+; Suggested Colors:
+; Comments (;#) Comments, Comments 2 Green
+; Sections Characters Red
+; Values Strings Blue
+
+C=1
+
+[Syntax]
+Namespace1 = 6
+IgnoreCase = Yes
+KeyWordLength = 1
+BracketChars =
+OperatorChars =
+PreprocStart =
+SyntaxStart =
+SyntaxEnd =
+HexPrefix =
+CommentStart =
+CommentEnd =
+CommentStartAlt =
+CommentEndAlt =
+SingleComment = #
+SingleCommentCol =
+SingleCommentAlt = ;
+SingleCommentColAlt =
+SingleCommentEsc =
+StringsSpanLines = No
+StringStart =
+StringEnd =
+StringAlt = =
+StringEsc =
+CharStart = [
+CharEnd = ]
+CharEsc =
diff --git a/lib/simpleini/package.cmd b/lib/simpleini/package.cmd
new file mode 100644
index 0000000..2ae29db
--- /dev/null
+++ b/lib/simpleini/package.cmd
@@ -0,0 +1,26 @@
+set VERSION=4.15
+
+set SEVENZIP="C:\Program Files\7-Zip\7z.exe"
+
+FOR /F "tokens=*" %%G IN ('DIR /AD /B /S Debug*') DO (
+ DEL /S /Q "%%G"
+ RD "%%G"
+)
+FOR /F "tokens=*" %%G IN ('DIR /AD /B /S Release*') DO (
+ DEL /S /Q "%%G"
+ RD "%%G"
+)
+DEL /Q "SimpleIni.ncb"
+ATTRIB -H "SimpleIni.suo"
+DEL /Q "SimpleIni.suo"
+DEL /Q "SimpleIni.opt"
+DEL /Q testsi-out*.ini
+DEL /Q test1-blah.ini
+DEL /Q test1-output.ini
+START "Generate documentation" /WAIT "C:\Program Files (x86)\doxygen\bin\doxygen.exe" SimpleIni.doxy
+cd ..
+del simpleini-%VERSION%.zip
+%SEVENZIP% a -tzip -r- -x!simpleini\.svn simpleini-%VERSION%.zip simpleini\*
+del simpleini-doc.zip
+%SEVENZIP% a -tzip -r simpleini-doc.zip simpleini-doc\*
+cd simpleini
diff --git a/lib/simpleini/simpleini.doxy b/lib/simpleini/simpleini.doxy
new file mode 100644
index 0000000..6a27221
--- /dev/null
+++ b/lib/simpleini/simpleini.doxy
@@ -0,0 +1,1321 @@
+# Doxyfile 1.5.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file that
+# follow. The default is UTF-8 which is also the encoding used for all text before
+# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into
+# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of
+# possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = SimpleIni
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = D:/src/simpleini-doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
+# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
+# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
+# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF = "The $name class " \
+ "The $name widget " \
+ "The $name file " \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH = "D:/src/simpleini/ "
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
+# include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct (or union) is
+# documented as struct with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code where the coding convention is that all structs are
+# typedef'ed and only the typedef is referenced never the struct's name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be extracted
+# and appear in the documentation as a namespace called 'anonymous_namespace{file}',
+# where file will be replaced with the base name of the file that contains the anonymous
+# namespace. By default anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file($line) : $text "
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = D:/src/simpleini/SimpleIni.h
+
+# This tag can be used to specify the character encoding of the source files that
+# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default
+# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding.
+# See http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the output.
+# The symbol name can be a fully qualified name, a word, or if the wildcard * is used,
+# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH
+# then you must also enable this option. If you don't then doxygen will produce
+# a warning and turn it on anyway
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = SI_HAS_WIDE_FILE \
+ SI_SUPPORT_IOSTREAMS
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to
+# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to
+# specify the directory where the mscgen tool resides. If left empty the tool is assumed to
+# be found in the default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will
+# generate a caller dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the number
+# of direct children of the root node in a graph is already larger than
+# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 1000
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/lib/simpleini/simpleini.dsp b/lib/simpleini/simpleini.dsp
new file mode 100644
index 0000000..97c386a
--- /dev/null
+++ b/lib/simpleini/simpleini.dsp
@@ -0,0 +1,178 @@
+# Microsoft Developer Studio Project File - Name="simpleini" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=simpleini - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "simpleini.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "simpleini.mak" CFG="simpleini - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "simpleini - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "simpleini - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "simpleini - Win32 Debug Unicode" (based on "Win32 (x86) Console Application")
+!MESSAGE "simpleini - Win32 Release Unicode" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "simpleini - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0xc09 /d "NDEBUG"
+# ADD RSC /l 0xc09 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"Release/testsi.exe"
+
+!ELSEIF "$(CFG)" == "simpleini - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "simpleini___Win32_Debug"
+# PROP BASE Intermediate_Dir "simpleini___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c
+# SUBTRACT CPP /Fr /YX
+# ADD BASE RSC /l 0xc09 /d "_DEBUG"
+# ADD RSC /l 0xc09 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug/testsi.exe" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "simpleini - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug Unicode"
+# PROP BASE Intermediate_Dir "Debug Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug Unicode"
+# PROP Intermediate_Dir "Debug Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /D "SI_USE_GENERIC_CONVERSION" /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0xc09 /d "_DEBUG"
+# ADD RSC /l 0xc09 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug Unicode/testsi.exe" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "simpleini - Win32 Release Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release Unicode"
+# PROP BASE Intermediate_Dir "Release Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release Unicode"
+# PROP Intermediate_Dir "Release Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /D "SI_USE_GENERIC_CONVERSION" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0xc09 /d "NDEBUG"
+# ADD RSC /l 0xc09 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"Release Unicode/testsi.exe"
+
+!ENDIF
+
+# Begin Target
+
+# Name "simpleini - Win32 Release"
+# Name "simpleini - Win32 Debug"
+# Name "simpleini - Win32 Debug Unicode"
+# Name "simpleini - Win32 Release Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\snippets.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\test1.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\testsi.cpp
+# End Source File
+# End Group
+# Begin Group "Library Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\SimpleIni.h
+# End Source File
+# End Group
+# Begin Group "Generic Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\ConvertUTF.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConvertUTF.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/lib/simpleini/simpleini.dsw b/lib/simpleini/simpleini.dsw
new file mode 100644
index 0000000..2d593ed
--- /dev/null
+++ b/lib/simpleini/simpleini.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "simpleini"=.\simpleini.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/lib/simpleini/snippets.cpp b/lib/simpleini/snippets.cpp
new file mode 100644
index 0000000..646f4ff
--- /dev/null
+++ b/lib/simpleini/snippets.cpp
@@ -0,0 +1,123 @@
+// File: snippets.cpp
+// Library: SimpleIni
+// Author: Brodie Thiesfield <code@jellycan.com>
+// Source: http://code.jellycan.com/simpleini/
+//
+// Snippets that are used on the website
+
+#ifdef _WIN32
+# pragma warning(disable: 4786)
+#endif
+
+#ifndef _WIN32
+# include <unistd.h>
+#endif
+#include <fstream>
+
+#define SI_SUPPORT_IOSTREAMS
+#include "SimpleIni.h"
+
+bool
+snippets(
+ const char * a_pszFile,
+ bool a_bIsUtf8,
+ bool a_bUseMultiKey,
+ bool a_bUseMultiLine
+ )
+{
+ // LOADING DATA
+
+ // load from a data file
+ CSimpleIniA ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine);
+ SI_Error rc = ini.LoadFile(a_pszFile);
+ if (rc < 0) return false;
+
+ // load from a string
+ std::string strData;
+ rc = ini.LoadData(strData.c_str(), strData.size());
+ if (rc < 0) return false;
+
+ // GETTING SECTIONS AND KEYS
+
+ // get all sections
+ CSimpleIniA::TNamesDepend sections;
+ ini.GetAllSections(sections);
+
+ // get all keys in a section
+ CSimpleIniA::TNamesDepend keys;
+ ini.GetAllKeys("section-name", keys);
+
+ // GETTING VALUES
+
+ // get the value of a key
+ const char * pszValue = ini.GetValue("section-name",
+ "key-name", NULL /*default*/);
+
+ // get the value of a key which may have multiple
+ // values. If bHasMultipleValues is true, then just
+ // one value has been returned
+ bool bHasMultipleValues;
+ pszValue = ini.GetValue("section-name", "key-name",
+ NULL /*default*/, &bHasMultipleValues);
+
+ // get all values of a key with multiple values
+ CSimpleIniA::TNamesDepend values;
+ ini.GetAllValues("section-name", "key-name", values);
+
+ // sort the values into the original load order
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+ /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */
+ values.sort();
+#else
+ values.sort(CSimpleIniA::Entry::LoadOrder());
+#endif
+
+ // output all of the items
+ CSimpleIniA::TNamesDepend::const_iterator i;
+ for (i = values.begin(); i != values.end(); ++i) {
+ printf("key-name = '%s'\n", i->pItem);
+ }
+
+ // MODIFYING DATA
+
+ // adding a new section
+ rc = ini.SetValue("new-section", NULL, NULL);
+ if (rc < 0) return false;
+ printf("section: %s\n", rc == SI_INSERTED ?
+ "inserted" : "updated");
+
+ // adding a new key ("new-section" will be added
+ // automatically if it doesn't already exist.
+ rc = ini.SetValue("new-section", "new-key", "value");
+ if (rc < 0) return false;
+ printf("key: %s\n", rc == SI_INSERTED ?
+ "inserted" : "updated");
+
+ // changing the value of a key
+ rc = ini.SetValue("section", "key", "updated-value");
+ if (rc < 0) return false;
+ printf("key: %s\n", rc == SI_INSERTED ?
+ "inserted" : "updated");
+
+ // DELETING DATA
+
+ // deleting a key from a section. Optionally the entire
+ // section may be deleted if it is now empty.
+ ini.Delete("section-name", "key-name",
+ true /*delete the section if empty*/);
+
+ // deleting an entire section and all keys in it
+ ini.Delete("section-name", NULL);
+
+ // SAVING DATA
+
+ // save the data to a string
+ rc = ini.Save(strData);
+ if (rc < 0) return false;
+
+ // save the data back to the file
+ rc = ini.SaveFile(a_pszFile);
+ if (rc < 0) return false;
+
+ return true;
+}
diff --git a/lib/simpleini/test.cmd b/lib/simpleini/test.cmd
new file mode 100644
index 0000000..f5d64ea
--- /dev/null
+++ b/lib/simpleini/test.cmd
@@ -0,0 +1,24 @@
+@echo off
+
+Debug\testsi.exe -u -m -l test1-input.ini > test1-blah.ini
+fc test1-expected.ini test1-output.ini
+if errorlevel 1 goto error
+
+"Debug Unicode\testsi.exe" -u -m -l test1-input.ini > test1-blah.ini
+fc test1-expected.ini test1-output.ini
+if errorlevel 1 goto error
+
+Release\testsi.exe -u -m -l test1-input.ini > test1-blah.ini
+fc test1-expected.ini test1-output.ini
+if errorlevel 1 goto error
+
+"Release Unicode\testsi.exe" -u -m -l test1-input.ini > test1-blah.ini
+fc test1-expected.ini test1-output.ini
+if errorlevel 1 goto error
+
+exit /b 0
+
+:error
+echo Failed during test run. Output file doesn't match expected file.
+pause
+exit /b 1
diff --git a/lib/simpleini/test1-expected.ini b/lib/simpleini/test1-expected.ini
new file mode 100644
index 0000000..6b26c6c
--- /dev/null
+++ b/lib/simpleini/test1-expected.ini
@@ -0,0 +1,85 @@
+; testsi-UTF8-std.ini : standard UTF-8 test file for SimpleIni automated testing
+;
+; The number after a section or key is the order that it is defined in this file
+; to make it easier to see if it has been written out correctly. This file should
+; be loaded with Unicode / MultiKey / MultiLine turned on.
+
+
+
+; This comment should be joined on to the one below it about the key
+; with no section.
+
+; Key with no section
+lonely-key = nosection
+another = nosection either
+
+; This key has no value
+empty =
+
+
+; This should be joined with the comment below about japanese.
+; Another line which will be un-indented.
+
+; This is a section of keys showing the word Japanese in different syllabies.
+[ordered-1]
+a-1 = blah
+
+; this is in kanji
+japanese-2 = 日本語
+
+; this is in hiragana
+japanese-3 = にほんご
+
+; this is in katakana
+japanese-4 = ニホンゴ
+
+; this is in romaji
+japanese-5 = nihongo
+
+; kanji as the key
+日本語-6 = japanese
+
+
+[multi-2]
+
+; value a
+test = a
+
+; value b
+test = b
+
+; value c
+test = c
+
+; value d
+test = d
+
+
+[multiline-3]
+
+; This is obviously a multi-line entry
+multiline-1 = <<<END_OF_TEXT
+
+This is a multi-line comment. It
+will continue until we have the word MULTI
+on a line by itself.
+
+日本語も。
+
+END_OF_TEXT
+
+; This looks like multi-line, but because the newline following the last
+; line is discarded, it will be converted into a single line entry.
+another-2 = This is not a multiline entry.
+
+; If you wanted a multiline entry with a single line, you need to add
+; an extra line to it.
+another-3 = <<<END_OF_TEXT
+This is a multiline entry.
+
+END_OF_TEXT
+
+
+[integer]
+dec = 42
+hex = 0x2a
diff --git a/lib/simpleini/test1-input.ini b/lib/simpleini/test1-input.ini
new file mode 100644
index 0000000..76d16dd
--- /dev/null
+++ b/lib/simpleini/test1-input.ini
@@ -0,0 +1,76 @@
+; testsi-UTF8-std.ini : standard UTF-8 test file for SimpleIni automated testing
+;
+; The number after a section or key is the order that it is defined in this file
+; to make it easier to see if it has been written out correctly. This file should
+; be loaded with Unicode / MultiKey / MultiLine turned on.
+
+; This comment should be joined on to the one below it about the key
+; with no section.
+
+; Key with no section
+lonely-key = nosection
+another = nosection either
+
+; This key has no value
+empty =
+
+; This should be joined with the comment below about japanese.
+ ; Another line which will be un-indented.
+
+; This is a section of keys showing the word Japanese in different syllabies.
+[ordered-1]
+a-1 = blah
+
+; this is in kanji
+japanese-2 = 日本語
+
+; this is in hiragana
+japanese-3 = にほんご
+
+; this is in katakana
+japanese-4 = ニホンゴ
+
+; this is in romaji
+japanese-5 = nihongo
+
+; kanji as the key
+日本語-6 = japanese
+
+[multi-2]
+; value a
+test = a
+; value b
+test = b
+; value c
+test = c
+; value d
+test = d
+
+[multiline-3]
+; This is obviously a multi-line entry
+multiline-1 = <<<MULTI
+
+This is a multi-line comment. It
+will continue until we have the word MULTI
+on a line by itself.
+
+日本語も。
+
+MULTI
+
+; This looks like multi-line, but because the newline following the last
+; line is discarded, it will be converted into a single line entry.
+another-2 = <<<MULTI
+This is not a multiline entry.
+MULTI
+
+; If you wanted a multiline entry with a single line, you need to add
+; an extra line to it.
+another-3 = <<<MULTI
+This is a multiline entry.
+
+MULTI
+
+[integer]
+dec = 42
+hex = 0x2a
diff --git a/lib/simpleini/test1.cpp b/lib/simpleini/test1.cpp
new file mode 100644
index 0000000..b00d38c
--- /dev/null
+++ b/lib/simpleini/test1.cpp
@@ -0,0 +1,166 @@
+// File: test1.cpp
+// Library: SimpleIni
+// Author: Brodie Thiesfield <code@jellycan.com>
+// Source: http://code.jellycan.com/simpleini/
+//
+// Automated testing for SimpleIni streams
+
+#ifdef _WIN32
+# pragma warning(disable: 4786)
+#endif
+
+#ifdef _WIN32
+# include <windows.h>
+# define DELETE_FILE DeleteFileA
+#else
+# include <unistd.h>
+# define DELETE_FILE unlink
+#endif
+#include <fstream>
+
+#define SI_SUPPORT_IOSTREAMS
+#include "SimpleIni.h"
+
+class Test
+{
+ std::string m_strTest;
+
+public:
+ Test(const char * a_pszName)
+ : m_strTest(a_pszName)
+ {
+ printf("%s: test starting\n", m_strTest.c_str());
+ }
+
+ bool Success()
+ {
+ printf("%s: test succeeded\n", m_strTest.c_str());
+ return false;
+ }
+
+ bool Failure(const char * pszReason)
+ {
+ printf("%s: test FAILED (%s)\n", m_strTest.c_str(), pszReason);
+ return false;
+ }
+};
+
+bool FileComparisonTest(const char * a_pszFile1, const char * a_pszFile2) {
+ // ensure that the two files are the same
+ try {
+ std::string strFile1, strFile2;
+
+ char szBuf[1024];
+ FILE * fp = NULL;
+
+#if __STDC_WANT_SECURE_LIB__
+ fopen_s(&fp, a_pszFile1, "rb");
+#else
+ fp = fopen(a_pszFile1, "rb");
+#endif
+ if (!fp) throw false;
+ while (!feof(fp)) {
+ size_t n = fread(szBuf, 1, sizeof(szBuf), fp);
+ strFile1.append(szBuf, n);
+ }
+ fclose(fp);
+
+ fp = NULL;
+#if __STDC_WANT_SECURE_LIB__
+ fopen_s(&fp, a_pszFile2, "rb");
+#else
+ fp = fopen(a_pszFile2, "rb");
+#endif
+ if (!fp) throw false;
+ while (!feof(fp)) {
+ size_t n = fread(szBuf, 1, sizeof(szBuf), fp);
+ strFile2.append(szBuf, n);
+ }
+ fclose(fp);
+
+ if (strFile1 != strFile2) throw false;
+ }
+ catch (...) {
+ return false;
+ }
+
+ return true;
+}
+
+bool FileLoadTest(const char * a_pszFile1, const char * a_pszFile2) {
+ // ensure that the two files load into simpleini the same
+ CSimpleIniA ini(true, true, true);
+ bool b;
+ try {
+ ini.Reset();
+ if (ini.LoadFile(a_pszFile1) < 0) throw "Load failed for file 1";
+ if (ini.SaveFile("test1.ini") < 0) throw "Save failed for file 1";
+
+ ini.Reset();
+ if (ini.LoadFile(a_pszFile2) < 0) throw "Load failed for file 2";
+ if (ini.SaveFile("test2.ini") < 0) throw "Save failed for file 2";
+
+ b = FileComparisonTest("test1.ini", "test2.ini");
+ DELETE_FILE("test1.ini");
+ DELETE_FILE("test2.ini");
+ if (!b) throw "File comparison failed in FileLoadTest";
+ }
+ catch (...) {
+ return false;
+ }
+
+ return true;
+}
+
+bool TestStreams()
+{
+ const char * rgszTestFile[3] = {
+ "test1-input.ini",
+ "test1-output.ini",
+ "test1-expected.ini"
+ };
+
+ Test oTest("TestStreams");
+
+ CSimpleIniW ini;
+ ini.SetUnicode(true);
+ ini.SetMultiKey(true);
+ ini.SetMultiLine(true);
+
+ // load the file
+ try {
+ std::ifstream instream;
+ instream.open(rgszTestFile[0], std::ifstream::in | std::ifstream::binary);
+ if (ini.LoadData(instream) < 0) throw false;
+ instream.close();
+ }
+ catch (...) {
+ return oTest.Failure("Failed to load file");
+ }
+
+ // standard contents test
+ //if (!StandardContentsTest(ini, oTest)) {
+ // return false;
+ //}
+
+ // save the file
+ try {
+ std::ofstream outfile;
+ outfile.open(rgszTestFile[1], std::ofstream::out | std::ofstream::binary);
+ if (ini.Save(outfile, true) < 0) throw false;
+ outfile.close();
+ }
+ catch (...) {
+ return oTest.Failure("Failed to save file");
+ }
+
+ // file comparison test
+ if (!FileComparisonTest(rgszTestFile[1], rgszTestFile[2])) {
+ return oTest.Failure("Failed file comparison");
+ }
+ if (!FileLoadTest(rgszTestFile[1], rgszTestFile[2])) {
+ return oTest.Failure("Failed file load comparison");
+ }
+
+ return oTest.Success();
+}
diff --git a/lib/simpleini/testsi-EUCJP.ini b/lib/simpleini/testsi-EUCJP.ini
new file mode 100644
index 0000000..ee9e987
--- /dev/null
+++ b/lib/simpleini/testsi-EUCJP.ini
@@ -0,0 +1,52 @@
+; test file for SimpleIni
+
+nosection=ok
+NOSECTION=still ok
+ whitespace = ok
+
+[standard]
+foo=foo1
+standard-1=foo
+ܸ=ok1
+
+[Standard]
+Foo=foo2
+standard-2=foo
+ܸ=ok2
+
+ [ Whitespace ]
+
+a=
+
+[ whitespace in section name ]
+ whitespace in key name = whitespace in value name
+
+; comments
+ ; more comments
+
+invalid
+=invalid
+====invalid
+
+[Japanese]
+nihongo = ܸ
+ܸ = ܸ
+
+[ܸ]
+nihongo = ܸ
+ܸ = ܸ
+
+[]
+more=no section name
+
+
+
+[MultiLine]
+single = This is a single line.
+multi = <<<MULTI
+
+This is a multi-line value. It continues until the MULTI tag is found
+on a line by itself with no whitespace before or after it. This value
+will be returned to the user with all newlines and whitespace.
+
+MULTI
diff --git a/lib/simpleini/testsi-SJIS.ini b/lib/simpleini/testsi-SJIS.ini
new file mode 100644
index 0000000..f723ac6
--- /dev/null
+++ b/lib/simpleini/testsi-SJIS.ini
@@ -0,0 +1,51 @@
+; test file for SimpleIni
+
+nosection=ok
+NOSECTION=still ok
+ whitespace = ok
+
+[standard]
+foo=foo1
+standard-1=foo
+{=ok1
+
+[Standard]
+Foo=foo2
+standard-2=foo
+{=ok2
+
+ [ Whitespace ]
+
+a=
+
+[ whitespace in section name ]
+ whitespace in key name = whitespace in value name
+
+; comments
+ ; more comments
+
+invalid
+=invalid
+====invalid
+
+[Japanese]
+nihongo = {
+{ = {
+
+[{]
+nihongo = {
+{ = {
+
+[]
+more=no section name
+
+
+[MultiLine]
+single = This is a single line.
+multi = <<<MULTI
+
+This is a multi-line value. It continues until the MULTI tag is found
+on a line by itself with no whitespace before or after it. This value
+will be returned to the user with all newlines and whitespace.
+
+MULTI
diff --git a/lib/simpleini/testsi-UTF8.ini b/lib/simpleini/testsi-UTF8.ini
new file mode 100644
index 0000000..37d8e6d
--- /dev/null
+++ b/lib/simpleini/testsi-UTF8.ini
@@ -0,0 +1,50 @@
+; test file for SimpleIni
+
+ whitespace = ok
+nosection=ok
+NOSECTION=still ok
+
+[standard]
+foo=foo1
+standard-1=foo
+日本語=ok1
+
+[Standard]
+Foo=foo2
+standard-2=foo
+日本語=ok2
+
+ [ Whitespace ]
+
+a=
+
+[ whitespace in section name ]
+ whitespace in key name = whitespace in value name
+
+; comments
+ ; more comments
+
+invalid
+=invalid
+====invalid
+
+[Japanese]
+nihongo = 日本語
+日本語 = 日本語
+
+[日本語]
+nihongo = 日本語
+日本語 = 日本語
+
+[]
+more=no section name
+
+[MultiLine]
+single = This is a single line.
+multi = <<<MULTI
+
+This is a multi-line value. It continues until the MULTI tag is found
+on a line by itself with no whitespace before or after it. This value
+will be returned to the user with all newlines and whitespace.
+
+MULTI
diff --git a/lib/simpleini/testsi.cpp b/lib/simpleini/testsi.cpp
new file mode 100644
index 0000000..4d6dd9d
--- /dev/null
+++ b/lib/simpleini/testsi.cpp
@@ -0,0 +1,309 @@
+// File: testsi.cpp
+// Library: SimpleIni
+// Author: Brodie Thiesfield <code@jellycan.com>
+// Source: http://code.jellycan.com/simpleini/
+//
+// Demo of usage
+
+#ifdef _WIN32
+# pragma warning(disable: 4786)
+#endif
+
+#include <locale.h>
+#include <stdio.h>
+#include <cassert>
+
+#define SI_SUPPORT_IOSTREAMS
+#if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE)
+# include <fstream>
+#endif
+
+//#define SI_CONVERT_GENERIC
+//#define SI_CONVERT_ICU
+//#define SI_CONVERT_WIN32
+#include "SimpleIni.h"
+
+#ifdef SI_CONVERT_ICU
+// if converting using ICU then we need the ICU library
+# pragma comment(lib, "icuuc.lib")
+#endif
+
+#ifdef _WIN32
+# include <tchar.h>
+#else // !_WIN32
+# define TCHAR char
+# define _T(x) x
+# define _tprintf printf
+# define _tmain main
+#endif // _WIN32
+
+static void
+Test(
+ CSimpleIni & ini
+ )
+{
+ const TCHAR *pszSection = 0;
+ const TCHAR *pItem = 0;
+ const TCHAR *pszVal = 0;
+
+ // get the value of the key "foo" in section "standard"
+ bool bHasMulti;
+ pszVal = ini.GetValue(_T("standard"), _T("foo"), 0, &bHasMulti);
+ _tprintf(_T("\n-- Value of standard::foo is '%s' (hasMulti = %d)\n"),
+ pszVal ? pszVal : _T("(null)"), bHasMulti);
+
+ // set the value of the key "foo" in section "standard"
+ ini.SetValue(_T("standard"), _T("foo"), _T("wibble"));
+ pszVal = ini.GetValue(_T("standard"), _T("foo"), 0, &bHasMulti);
+ _tprintf(_T("\n-- Value of standard::foo is '%s' (hasMulti = %d)\n"),
+ pszVal ? pszVal : _T("(null)"), bHasMulti);
+
+ // get all values of the key "foo" in section "standard"
+ CSimpleIni::TNamesDepend values;
+ if (ini.GetAllValues(_T("standard"), _T("foo"), values)) {
+ _tprintf(_T("\n-- Values of standard::foo are:\n"));
+ CSimpleIni::TNamesDepend::const_iterator i = values.begin();
+ for (; i != values.end(); ++i) {
+ pszVal = i->pItem;
+ _tprintf(_T(" -> '%s'\n"), pszVal);
+ }
+ }
+
+ // get the size of the section [standard]
+ _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"),
+ ini.GetSectionSize(_T("standard")));
+
+ // delete the key "foo" in section "standard"
+ ini.Delete(_T("standard"), _T("foo"));
+ pszVal = ini.GetValue(_T("standard"), _T("foo"), 0);
+ _tprintf(_T("\n-- Value of standard::foo is now '%s'\n"),
+ pszVal ? pszVal : _T("(null)"));
+
+ // get the size of the section [standard]
+ _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"),
+ ini.GetSectionSize(_T("standard")));
+
+ // get the list of all key names for the section "standard"
+ _tprintf(_T("\n-- Dumping keys of section: [standard]\n"));
+ CSimpleIni::TNamesDepend keys;
+ ini.GetAllKeys(_T("standard"), keys);
+
+ // dump all of the key names
+ CSimpleIni::TNamesDepend::const_iterator iKey = keys.begin();
+ for ( ; iKey != keys.end(); ++iKey ) {
+ pItem = iKey->pItem;
+ _tprintf(_T("Key: %s\n"), pItem);
+ }
+
+ // add a decimal value
+ ini.SetLongValue(_T("integer"), _T("dec"), 42, NULL, false);
+ ini.SetLongValue(_T("integer"), _T("hex"), 42, NULL, true);
+
+ // add some bool values
+ ini.SetBoolValue(_T("bool"), _T("t"), true);
+ ini.SetBoolValue(_T("bool"), _T("f"), false);
+
+ // get the values back
+ assert(42 == ini.GetLongValue(_T("integer"), _T("dec")));
+ assert(42 == ini.GetLongValue(_T("integer"), _T("hex")));
+ assert(true == ini.GetBoolValue(_T("bool"), _T("t")));
+ assert(false == ini.GetBoolValue(_T("bool"), _T("f")));
+
+ // delete the section "standard"
+ ini.Delete(_T("standard"), NULL);
+ _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"),
+ ini.GetSectionSize(_T("standard")));
+
+ // iterate through every section in the file
+ _tprintf(_T("\n-- Dumping all sections\n"));
+ CSimpleIni::TNamesDepend sections;
+ ini.GetAllSections(sections);
+ CSimpleIni::TNamesDepend::const_iterator iSection = sections.begin();
+ for ( ; iSection != sections.end(); ++iSection ) {
+ pszSection = iSection->pItem;
+
+ // print the section name
+ printf("\n");
+ if (*pszSection) {
+ _tprintf(_T("[%s]\n"), pszSection);
+ }
+
+ // if there are keys and values...
+ const CSimpleIni::TKeyVal * pSectionData = ini.GetSection(pszSection);
+ if (pSectionData) {
+ // iterate over all keys and dump the key name and value
+ CSimpleIni::TKeyVal::const_iterator iKeyVal = pSectionData->begin();
+ for ( ;iKeyVal != pSectionData->end(); ++iKeyVal) {
+ pItem = iKeyVal->first.pItem;
+ pszVal = iKeyVal->second;
+ _tprintf(_T("%s=%s\n"), pItem, pszVal);
+ }
+ }
+ }
+}
+
+#if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE)
+static bool
+TestStreams(
+ const TCHAR * a_pszFile,
+ bool a_bIsUtf8,
+ bool a_bUseMultiKey,
+ bool a_bUseMultiLine
+ )
+{
+ // load the file
+ CSimpleIni ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine);
+ _tprintf(_T("Loading file: %s\n"), a_pszFile);
+ std::ifstream instream;
+ instream.open(a_pszFile, std::ifstream::in | std::ifstream::binary);
+ SI_Error rc = ini.LoadData(instream);
+ instream.close();
+ if (rc < 0) {
+ printf("Failed to open file.\n");
+ return false;
+ }
+
+ Test(ini);
+
+ // save the file (simple)
+ _tprintf(_T("\n-- Saving file to: testsi-out-streams.ini\n"));
+ std::ofstream outstream;
+ outstream.open("testsi-out-streams.ini", std::ofstream::out | std::ofstream::binary);
+ ini.Save(outstream);
+ outstream.close();
+
+ return true;
+}
+#endif // SI_SUPPORT_IOSTREAMS
+
+static bool
+TestFile(
+ const TCHAR * a_pszFile,
+ bool a_bIsUtf8,
+ bool a_bUseMultiKey,
+ bool a_bUseMultiLine
+ )
+{
+ // load the file
+ CSimpleIni ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine);
+ _tprintf(_T("Loading file: %s\n"), a_pszFile);
+ SI_Error rc = ini.LoadFile(a_pszFile);
+ if (rc < 0) {
+ printf("Failed to open file.\n");
+ return false;
+ }
+
+ // run the tests
+ Test(ini);
+
+ // save the file (simple)
+ _tprintf(_T("\n-- Saving file to: testsi-out.ini\n"));
+ ini.SaveFile("testsi-out.ini");
+
+ // save the file (with comments)
+ // Note: to save the file and add a comment to the beginning, use
+ // code such as the following.
+ _tprintf(_T("\n-- Saving file to: testsi-out-comment.ini\n"));
+ FILE * fp = NULL;
+#if __STDC_WANT_SECURE_LIB__
+ fopen_s(&fp, "testsi-out-comment.ini", "wb");
+#else
+ fp = fopen("testsi-out-comment.ini", "wb");
+#endif
+ if (fp) {
+ CSimpleIni::FileWriter writer(fp);
+ if (a_bIsUtf8) {
+ writer.Write(SI_UTF8_SIGNATURE);
+ }
+
+ // add a string to the file in the correct text format
+ CSimpleIni::Converter convert = ini.GetConverter();
+ convert.ConvertToStore(_T("; output from testsi.cpp test program")
+ SI_NEWLINE SI_NEWLINE);
+ writer.Write(convert.Data());
+
+ ini.Save(writer, false);
+ fclose(fp);
+ }
+
+ return true;
+}
+
+static bool
+ParseCommandLine(
+ int argc,
+ TCHAR * argv[],
+ const TCHAR * & a_pszFile,
+ bool & a_bIsUtf8,
+ bool & a_bUseMultiKey,
+ bool & a_bUseMultiLine
+ )
+{
+ a_pszFile = 0;
+ a_bIsUtf8 = false;
+ a_bUseMultiKey = false;
+ a_bUseMultiLine = false;
+ for (--argc; argc > 0; --argc) {
+ if (argv[argc][0] == '-') {
+ switch (argv[argc][1]) {
+ case TCHAR('u'):
+ a_bIsUtf8 = true;
+ break;
+ case TCHAR('m'):
+ a_bUseMultiKey = true;
+ break;
+ case TCHAR('l'):
+ a_bUseMultiLine = true;
+ break;
+ }
+ }
+ else {
+ a_pszFile = argv[argc];
+ }
+ }
+ if (!a_pszFile) {
+ _tprintf(
+ _T("Usage: testsi [-u] [-m] [-l] iniFile\n")
+ _T(" -u Load file as UTF-8 (Default is to use system locale)\n")
+ _T(" -m Enable multiple keys\n")
+ _T(" -l Enable multiple line values\n")
+ );
+ return false;
+ }
+
+ return true;
+}
+
+extern bool TestStreams();
+
+int
+_tmain(
+ int argc,
+ TCHAR * argv[]
+ )
+{
+ setlocale(LC_ALL, "");
+
+ // start of automated testing...
+ TestStreams();
+
+ // parse the command line
+ const TCHAR * pszFile;
+ bool bIsUtf8, bUseMultiKey, bUseMultiLine;
+ if (!ParseCommandLine(argc, argv, pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) {
+ return 1;
+ }
+
+ // run the test
+ if (!TestFile(pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) {
+ return 1;
+ }
+#if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE)
+ if (!TestStreams(pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) {
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9bcd288..b998d19 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -67,7 +67,7 @@ common/iman.cpp
# common/restext.cpp
common/stringutils.cpp
graphics/core/color.cpp
-# graphics/engine/camera.cpp # new code but depends on other modules
+graphics/engine/camera.cpp
graphics/engine/cloud.cpp
graphics/engine/engine.cpp
graphics/engine/lightman.cpp
@@ -176,6 +176,7 @@ graphics/opengl/gldevice.cpp
set(LIBS
${SDL_LIBRARY}
${SDLIMAGE_LIBRARY}
+${SDLTTF_LIBRARY}
${OPENGL_LIBRARY}
${PNG_LIBRARIES}
${OPTIONAL_LIBS}
diff --git a/src/app/README.txt b/src/app/README.txt
index 1df1fcc..e4f69ec 100644
--- a/src/app/README.txt
+++ b/src/app/README.txt
@@ -1,3 +1,4 @@
-src/app
-
-Contains the main class of the application.
+/**
+ * \dir app
+ * Main class of the application and system functions
+ */
diff --git a/src/app/app.cpp b/src/app/app.cpp
index d20232d..a0518db 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -122,6 +122,7 @@ bool CApplication::ParseArguments(int argc, char *argv[])
{
waitDataDir = false;
m_dataPath = arg;
+ continue;
}
if (arg == "-debug")
@@ -153,10 +154,6 @@ bool CApplication::Create()
// Temporarily -- only in windowed mode
m_deviceConfig.fullScreen = false;
- // Create the 3D engine
- m_engine = new Gfx::CEngine(m_iMan, this);
-
-
/* // Create the sound instance.
m_sound = new CSound(m_iMan);
@@ -218,27 +215,20 @@ bool CApplication::Create()
if (! m_device->Create() )
{
SystemDialog( SDT_ERROR, "COLOBT - Fatal Error",
- std::string("Error in CDevice::Create() :\n") +
- std::string(m_device->GetError()) );
+ std::string("Error in CDevice::Create()") );
m_exitCode = 1;
return false;
}
+ // Create the 3D engine
+ m_engine = new Gfx::CEngine(m_iMan, this);
+
m_engine->SetDevice(m_device);
- if (! m_engine->Create() )
- {
- SystemDialog( SDT_ERROR, "COLOBT - Fatal Error",
- std::string("Error in CEngine::Create() :\n") +
- std::string(m_engine->GetError()) );
- m_exitCode = 1;
- return false;
- }
- if (! m_engine->AfterDeviceSetInit() )
+ if (! m_engine->Create() )
{
SystemDialog( SDT_ERROR, "COLOBT - Fatal Error",
- std::string("Error in CEngine::AfterDeviceSetInit() :\n") +
- std::string(m_engine->GetError()) );
+ std::string("Error in CEngine::Init()") );
m_exitCode = 1;
return false;
}
@@ -293,7 +283,7 @@ bool CApplication::CreateVideoSurface()
if (m_deviceConfig.hardwareAccel)
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
- m_private->surface = SDL_SetVideoMode(m_deviceConfig.size.w, m_deviceConfig.size.h,
+ m_private->surface = SDL_SetVideoMode(m_deviceConfig.size.x, m_deviceConfig.size.y,
m_deviceConfig.bpp, videoFlags);
return true;
@@ -315,8 +305,7 @@ void CApplication::Destroy()
if (m_engine != NULL)
{
- if (m_engine->GetWasInit())
- m_engine->Destroy();
+ m_engine->Destroy();
delete m_engine;
m_engine = NULL;
@@ -324,8 +313,7 @@ void CApplication::Destroy()
if (m_device != NULL)
{
- if (m_device->GetWasInit())
- m_device->Destroy();
+ m_device->Destroy();
delete m_device;
m_device = NULL;
@@ -508,6 +496,14 @@ void CApplication::UpdateJoystick()
}
}
+void CApplication::UpdateMouse()
+{
+ Math::IntPoint pos;
+ SDL_GetMouseState(&pos.x, &pos.y);
+ m_systemMousePos = m_engine->WindowToInterfaceCoords(pos);
+ m_engine->SetMousePos(m_systemMousePos);
+}
+
int CApplication::Run()
{
m_active = true;
@@ -582,17 +578,12 @@ int CApplication::Run()
m_robotMain->ProcessEvent(event); */
}
- // Update game and render a frame during idle time (no messages are waiting)
- bool ok = Render();
+ /* Update mouse position explicitly right before rendering
+ * because mouse events are usually way behind */
+ UpdateMouse();
- // If an error occurs, push quit event to the queue
- if (! ok)
- {
- SDL_Event quitEvent;
- memset(&quitEvent, 0, sizeof(SDL_Event));
- quitEvent.type = SDL_QUIT;
- SDL_PushEvent(&quitEvent);
- }
+ // Update game and render a frame during idle time (no messages are waiting)
+ Render();
}
}
@@ -616,21 +607,6 @@ PressState TranslatePressState(unsigned char state)
return STATE_RELEASED;
}
-/** Conversion of the position of the mouse from window coords to interface coords:
- - x: 0=left, 1=right
- - y: 0=down, 1=up */
-Math::Point CApplication::WindowToInterfaceCoords(Math::IntPoint pos)
-{
- return Math::Point( static_cast<float>(pos.x) / static_cast<float>(m_deviceConfig.size.w),
- 1.0f - static_cast<float>(pos.y) / static_cast<float>(m_deviceConfig.size.h) );
-}
-
-Math::IntPoint CApplication::InterfaceToWindowCoords(Math::Point pos)
-{
- return Math::IntPoint(static_cast<int>(pos.x * m_deviceConfig.size.w),
- static_cast<int>((1.0f - pos.y) * m_deviceConfig.size.h));
-}
-
/** The SDL event parsed is stored internally.
If event is not available or is not understood, returned event is of type EVENT_NULL. */
Event CApplication::ParseEvent()
@@ -666,14 +642,16 @@ Event CApplication::ParseEvent()
event.mouseButton.button = m_private->currentEvent.button.button;
event.mouseButton.state = TranslatePressState(m_private->currentEvent.button.state);
- event.mouseButton.pos = WindowToInterfaceCoords(Math::IntPoint(m_private->currentEvent.button.x, m_private->currentEvent.button.y));
+ event.mouseButton.pos = m_engine->WindowToInterfaceCoords(
+ Math::IntPoint(m_private->currentEvent.button.x, m_private->currentEvent.button.y));
}
else if (m_private->currentEvent.type == SDL_MOUSEMOTION)
{
event.type = EVENT_MOUSE_MOVE;
event.mouseMove.state = TranslatePressState(m_private->currentEvent.button.state);
- event.mouseMove.pos = WindowToInterfaceCoords(Math::IntPoint(m_private->currentEvent.button.x, m_private->currentEvent.button.y));
+ event.mouseMove.pos = m_engine->WindowToInterfaceCoords(
+ Math::IntPoint(m_private->currentEvent.button.x, m_private->currentEvent.button.y));
}
else if (m_private->currentEvent.type == SDL_JOYAXISMOTION)
{
@@ -774,17 +752,13 @@ bool CApplication::ProcessEvent(const Event &event)
return true;
}
-/** Renders the frame and swaps buffers as necessary. Returns \c false on error. */
-bool CApplication::Render()
+/** Renders the frame and swaps buffers as necessary */
+void CApplication::Render()
{
- bool result = m_engine->Render();
- if (! result)
- return false;
+ m_engine->Render();
if (m_deviceConfig.doubleBuf)
SDL_GL_SwapBuffers();
-
- return true;
}
void CApplication::StepSimulation(float rTime)
@@ -792,7 +766,12 @@ void CApplication::StepSimulation(float rTime)
// TODO
}
-VideoQueryResult CApplication::GetVideoResolutionList(std::vector<Math::IntSize> &resolutions,
+Gfx::GLDeviceConfig CApplication::GetVideoConfig()
+{
+ return m_deviceConfig;
+}
+
+VideoQueryResult CApplication::GetVideoResolutionList(std::vector<Math::IntPoint> &resolutions,
bool fullScreen, bool resizeable)
{
resolutions.clear();
@@ -830,7 +809,7 @@ VideoQueryResult CApplication::GetVideoResolutionList(std::vector<Math::IntSize>
for (int i = 0; modes[i] != NULL; ++i)
- resolutions.push_back(Math::IntSize(modes[i]->w, modes[i]->h));
+ resolutions.push_back(Math::IntPoint(modes[i]->w, modes[i]->h));
return VIDEO_QUERY_OK;
}
@@ -888,10 +867,9 @@ bool CApplication::GetSystemMouseVisibile()
return result == SDL_ENABLE;
}
-
void CApplication::SetSystemMousePos(Math::Point pos)
{
- Math::IntPoint windowPos = InterfaceToWindowCoords(pos);
+ Math::IntPoint windowPos = m_engine->InterfaceToWindowCoords(pos);
SDL_WarpMouse(windowPos.x, windowPos.y);
m_systemMousePos = pos;
}
diff --git a/src/app/app.h b/src/app/app.h
index 483aa55..0cfaad2 100644
--- a/src/app/app.h
+++ b/src/app/app.h
@@ -15,7 +15,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// app.h
+/**
+ * \file app/app.h
+ * \brief CApplication class
+ */
#pragma once
@@ -25,7 +28,6 @@
#include "graphics/core/device.h"
#include "graphics/engine/engine.h"
#include "graphics/opengl/gldevice.h"
-#include "math/intsize.h"
#include <string>
#include <vector>
@@ -133,7 +135,7 @@ public:
void Destroy();
//! Returns a list of possible video modes
- VideoQueryResult GetVideoResolutionList(std::vector<Math::IntSize> &resolutions,
+ VideoQueryResult GetVideoResolutionList(std::vector<Math::IntPoint> &resolutions,
bool fullScreen, bool resizeable);
//! Returns the current video mode
@@ -162,6 +164,9 @@ public:
//! Polls the state of joystick axes and buttons
void UpdateJoystick();
+ //! Updates the mouse position explicitly
+ void UpdateMouse();
+
void FlushPressKey();
void ResetKey();
void SetKey(int keyRank, int option, int key);
@@ -199,18 +204,13 @@ protected:
//! Handles some incoming events
bool ProcessEvent(const Event &event);
//! Renders the image in window
- bool Render();
+ void Render();
//! Opens the joystick device
bool OpenJoystick();
//! Closes the joystick device
void CloseJoystick();
- //! Converts window coords to interface coords
- Math::Point WindowToInterfaceCoords(Math::IntPoint pos);
- //! Converts the interface coords to window coords
- Math::IntPoint InterfaceToWindowCoords(Math::Point pos);
-
protected:
//! Instance manager
CInstanceManager* m_iMan;
diff --git a/src/app/main.cpp b/src/app/main.cpp
index dce13da..619043e 100644
--- a/src/app/main.cpp
+++ b/src/app/main.cpp
@@ -15,7 +15,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// main.cpp
+/**
+ * \file app/main.cpp
+ * \brief Entry point of application - main() function
+ */
#include "app/app.h"
#include "app/system.h"
diff --git a/src/app/system.h b/src/app/system.h
index 3c04760..e216842 100644
--- a/src/app/system.h
+++ b/src/app/system.h
@@ -15,7 +15,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// system.h
+/**
+ * \file app/system.h
+ * \brief System functions: time stamps, info dialogs, etc.
+ */
#pragma once
@@ -26,7 +29,7 @@
/* Dialog utils */
/**
- * \enum SysDialogType
+ * \enum SystemDialogType
* \brief Type of system dialog
*/
enum SystemDialogType
@@ -44,7 +47,7 @@ enum SystemDialogType
};
/**
- * \enum SysDialogResult
+ * \enum SystemDialogResult
* \brief Result of system dialog
*
* Means which button was pressed.
diff --git a/src/app/system_linux.h b/src/app/system_linux.h
index f58c9a1..69893de 100644
--- a/src/app/system_linux.h
+++ b/src/app/system_linux.h
@@ -15,10 +15,13 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// system_linux.h
+/**
+ * \file app/system_linux.h
+ * \brief Linux-specific implementation of system functions
+ */
-/* This header contains Linux-specific code for system utils
- from system.h. There is no separate .cpp module for simplicity.*/
+/* NOTE: code is contained in this header;
+ * there is no separate .cpp module for simplicity */
#include <sys/time.h>
#include <time.h>
diff --git a/src/app/system_other.h b/src/app/system_other.h
index 9f13ffa..eff0c8a 100644
--- a/src/app/system_other.h
+++ b/src/app/system_other.h
@@ -15,10 +15,13 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// system_other.h
+/**
+ * \file app/system_other.h
+ * \brief Fallback code for other systems
+ */
-/* This header contains fallback code for other platforms for system utils
- from system.h. There is no separate .cpp module for simplicity.*/
+/* NOTE: code is contained in this header;
+ * there is no separate .cpp module for simplicity */
#include <SDL/SDL.h>
diff --git a/src/app/system_windows.h b/src/app/system_windows.h
index eb6beec..72d9f88 100644
--- a/src/app/system_windows.h
+++ b/src/app/system_windows.h
@@ -15,10 +15,13 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// system_windows.h
+/**
+ * \file app/system_windows.h
+ * \brief Windows-specific implementation of system functions
+ */
-/* This header contains Windows-specific code for system utils
- from system.h. There is no separate .cpp module for simplicity.*/
+/* NOTE: code is contained in this header;
+ * there is no separate .cpp module for simplicity */
#include <windows.h>
diff --git a/src/common/README.txt b/src/common/README.txt
index 36653cc..73d65b7 100644
--- a/src/common/README.txt
+++ b/src/common/README.txt
@@ -1,3 +1,4 @@
-src/common
-
-Contains headers and modules with common structs and enums.
+/**
+ * \dir common
+ * \brief Structs and utils shared throughout the application
+ */
diff --git a/src/common/event.h b/src/common/event.h
index 0d9aa7c..1cfab35 100644
--- a/src/common/event.h
+++ b/src/common/event.h
@@ -19,8 +19,8 @@
#pragma once
-#include "common/key.h"
-#include "math/point.h"
+#include <common/key.h>
+#include <math/point.h>
#include <string.h>
@@ -40,9 +40,10 @@ enum EventType
EVENT_NULL = 0,
//! Event sent on user or system quit request
- EVENT_QUIT = 1,
+ EVENT_QUIT = 1,
- //? EVENT_FRAME = 2,
+ //! Frame update event
+ EVENT_FRAME = 2,
//! Event sent after pressing a mouse button
EVENT_MOUSE_BUTTON_DOWN = 3,
@@ -669,29 +670,41 @@ struct Event
//! If true, the event was produced by system (SDL); else, it has come from user interface
bool systemEvent;
- //! Additional data for EVENT_KEY_DOWN and EVENT_KEY_UP
- KeyEventData key;
- //! Additional data for EVENT_MOUSE_BUTTON_DOWN and EVENT_MOUSE_BUTTON_UP
- MouseButtonEventData mouseButton;
- //! Additional data for EVENT_MOUSE_MOVE
- MouseMoveEventData mouseMove;
- //! Additional data for EVENT_JOY
- JoyAxisEventData joyAxis;
- //! Additional data for EVENT_JOY_AXIS
- JoyButtonEventData joyButton;
- //! Additional data for EVENT_ACTIVE
- ActiveEventData active;
-
- //? long param; // parameter
- //? Math::Point pos; // mouse position (0 .. 1)
- //? float axeX; // control the X axis (-1 .. 1)
- //? float axeY; // control of the Y axis (-1 .. 1)
- //? float axeZ; // control the Z axis (-1 .. 1)
- //? short keyState; // state of the keyboard (KS_ *)
- //? float rTime; // relative time
+ union
+ {
+ //! Additional data for EVENT_KEY_DOWN and EVENT_KEY_UP
+ KeyEventData key;
+ //! Additional data for EVENT_MOUSE_BUTTON_DOWN and EVENT_MOUSE_BUTTON_UP
+ MouseButtonEventData mouseButton;
+ //! Additional data for EVENT_MOUSE_MOVE
+ MouseMoveEventData mouseMove;
+ //! Additional data for EVENT_JOY
+ JoyAxisEventData joyAxis;
+ //! Additional data for EVENT_JOY_AXIS
+ JoyButtonEventData joyButton;
+ //! Additional data for EVENT_ACTIVE
+ ActiveEventData active;
+ };
+
+ // TODO: refactor/rewrite
+ long param; // parameter
+ Math::Point pos; // mouse position (0 .. 1)
+ float axeX; // control the X axis (-1 .. 1)
+ float axeY; // control of the Y axis (-1 .. 1)
+ float axeZ; // control the Z axis (-1 .. 1)
+ short keyState; // state of the keyboard (KS_ *)
+ float rTime; // relative time
Event(EventType aType = EVENT_NULL)
- : type(aType), systemEvent(false) {}
+ {
+ type = aType;
+ systemEvent = false;
+
+ param = 0;
+ axeX = axeY = axeZ = 0.0f;
+ keyState = 0;
+ rTime = 0.0f;
+ }
};
diff --git a/src/common/iman.cpp b/src/common/iman.cpp
index 90b0c1e..4b89ecf 100644
--- a/src/common/iman.cpp
+++ b/src/common/iman.cpp
@@ -23,6 +23,21 @@
#include "common/iman.h"
+template<> CInstanceManager* CSingleton<CInstanceManager>::mInstance = nullptr;
+
+
+CInstanceManager& CInstanceManager::GetInstance()
+{
+ assert(mInstance);
+ return *mInstance;
+}
+
+
+CInstanceManager* CInstanceManager::GetInstancePointer()
+{
+ assert(mInstance);
+ return mInstance;
+}
// Object's constructor.
diff --git a/src/common/iman.h b/src/common/iman.h
index 7a7b499..89b5206 100644
--- a/src/common/iman.h
+++ b/src/common/iman.h
@@ -18,8 +18,8 @@
#pragma once
-
-#include "common/misc.h"
+#include <common/singleton.h>
+#include <common/misc.h>
@@ -32,7 +32,7 @@ struct BaseClass
-class CInstanceManager
+class CInstanceManager : public CSingleton<CInstanceManager>
{
public:
CInstanceManager();
@@ -44,6 +44,8 @@ public:
bool DeleteInstance(ClassType classType, void* pointer);
void* SearchInstance(ClassType classType, int rank=0);
+ static CInstanceManager& GetInstance();
+ static CInstanceManager* GetInstancePointer();
protected:
void Compress(ClassType classType);
diff --git a/src/common/logger.cpp b/src/common/logger.cpp
index f24726e..be73ec7 100644
--- a/src/common/logger.cpp
+++ b/src/common/logger.cpp
@@ -21,21 +21,7 @@
#include <stdio.h>
-template<> CLogger* CSingleton<CLogger>::mInstance = 0;
-
-
-CLogger& CLogger::GetInstance()
-{
- assert(mInstance);
- return *mInstance;
-}
-
-
-CLogger* CLogger::GetInstancePointer()
-{
- assert(mInstance);
- return mInstance;
-}
+template<> CLogger* CSingleton<CLogger>::mInstance = nullptr;
CLogger::CLogger()
diff --git a/src/common/logger.h b/src/common/logger.h
index a67aefe..f126e52 100644
--- a/src/common/logger.h
+++ b/src/common/logger.h
@@ -21,6 +21,7 @@
#include <string>
#include <cstdarg>
+#include <cstdio>
#include <common/singleton.h>
@@ -57,42 +58,39 @@ class CLogger : public CSingleton<CLogger>
~CLogger();
/** Write message to console or file
- * @param const char str - message to write
+ * @param str - message to write
* @param ... - additional arguments
*/
void Message(const char *str, ...);
/** Write message to console or file with LOG_INFO level
- * @param const char str - message to write
+ * @param str - message to write
* @param ... - additional arguments
*/
void Info(const char *str, ...);
/** Write message to console or file with LOG_WARN level
- * @param const char str - message to write
+ * @param str - message to write
* @param ... - additional arguments
*/
void Warn(const char *str, ...);
/** Write message to console or file with LOG_ERROR level
- * @param const char str - message to write
+ * @param str - message to write
* @param ... - additional arguments
*/
void Error(const char *str, ...);
/** Set output file to write logs to
- * @param std::string filename - output file to write to
+ * @param filename - output file to write to
*/
void SetOutputFile(std::string filename);
/** Set log level. Logs with level below will not be shown
- * @param LogType level - minimum log level to write
+ * @param level - minimum log level to write
*/
void SetLogLevel(LogType level);
- static CLogger& GetInstance();
- static CLogger* GetInstancePointer();
-
private:
std::string mFilename;
FILE *mFile;
diff --git a/src/common/profile.cpp b/src/common/profile.cpp
index d921d34..29a68e1 100644
--- a/src/common/profile.cpp
+++ b/src/common/profile.cpp
@@ -17,98 +17,89 @@
// profile.cpp
-#include <stdio.h>
-#include <d3d.h>
-#include <stdlib.h>
+#include <common/profile.h>
-#include "common/language.h"
-#include "common/struct.h"
-#include "common/profile.h"
+template<> CProfile* CSingleton<CProfile>::mInstance = nullptr;
-static char g_filename[100];
-
+CProfile::CProfile()
+{
+ m_ini = new CSimpleIniA();
+ m_ini->SetUnicode();
+ m_ini->SetMultiKey();
+}
-bool InitCurrentDirectory()
+CProfile::~CProfile()
{
-#if _SCHOOL
- _fullpath(g_filename, "ceebot.ini", 100);
-#else
- _fullpath(g_filename, "colobot.ini", 100);
-#endif
- return true;
+ m_ini->Reset();
+ delete m_ini;
}
-bool SetLocalProfileString(char* section, char* key, char* string)
+bool CProfile::InitCurrentDirectory()
{
- WritePrivateProfileString(section, key, string, g_filename);
- return true;
+ bool result = m_ini->LoadFile("colobot.ini") == SI_OK;
+ return result;
}
-bool GetLocalProfileString(char* section, char* key, char* buffer, int max)
+
+bool CProfile::SetLocalProfileString(std::string section, std::string key, std::string value)
{
- int nb;
+ return (m_ini->SetValue(section.c_str(), key.c_str(), value.c_str()) == SI_OK);
+}
- nb = GetPrivateProfileString(section, key, "", buffer, max, g_filename);
- if ( nb == 0 )
- {
- buffer[0] = 0;
- return false;
+
+bool CProfile::GetLocalProfileString(std::string section, std::string key, std::string &buffer)
+{
+ const char* value = m_ini->GetValue(section.c_str(), key.c_str(), nullptr);
+ if (strlen(value) > 0) {
+ buffer = std::string(value);
+ return true;
}
- return true;
+
+ return false;
}
-bool SetLocalProfileInt(char* section, char* key, int value)
+bool CProfile::SetLocalProfileInt(std::string section, std::string key, int value)
{
- char s[20];
-
- sprintf(s, "%d", value);
- WritePrivateProfileString(section, key, s, g_filename);
- return true;
+ return (m_ini->SetLongValue(section.c_str(), key.c_str(), value) == SI_OK);
}
-bool GetLocalProfileInt(char* section, char* key, int &value)
+
+bool CProfile::GetLocalProfileInt(std::string section, std::string key, int &value)
{
- char s[20];
- int nb;
-
- nb = GetPrivateProfileString(section, key, "", s, 20, g_filename);
- if ( nb == 0 )
- {
- value = 0;
- return false;
- }
- sscanf(s, "%d", &value);
+ value = m_ini->GetLongValue(section.c_str(), key.c_str(), 0L);
return true;
}
-bool SetLocalProfileFloat(char* section, char* key, float value)
+bool CProfile::SetLocalProfileFloat(std::string section, std::string key, float value)
{
- char s[20];
-
- sprintf(s, "%.2f", value);
- WritePrivateProfileString(section, key, s, g_filename);
- return true;
+ return (m_ini->SetDoubleValue(section.c_str(), key.c_str(), value) == SI_OK);
}
-bool GetLocalProfileFloat(char* section, char* key, float &value)
+
+bool CProfile::GetLocalProfileFloat(std::string section, std::string key, float &value)
{
- char s[20];
- int nb;
-
- nb = GetPrivateProfileString(section, key, "", s, 20, g_filename);
- if ( nb == 0 )
- {
- value = 0.0f;
- return false;
- }
- sscanf(s, "%f", &value);
+ value = m_ini->GetDoubleValue(section.c_str(), key.c_str(), 0.0d);
return true;
}
+std::vector< std::string > CProfile::GetLocalProfileSection(std::string section, std::string key)
+{
+ std::vector< std::string > ret_list;
+
+ CSimpleIniA::TNamesDepend values;
+ m_ini->GetAllValues(section.c_str(), key.c_str(), values);
+ values.sort(CSimpleIniA::Entry::LoadOrder());
+
+ for (auto item : values) {
+ ret_list.push_back(item.pItem);
+ }
+
+ return ret_list;
+}
diff --git a/src/common/profile.h b/src/common/profile.h
index 2c76a0b..0886522 100644
--- a/src/common/profile.h
+++ b/src/common/profile.h
@@ -18,13 +18,97 @@
#pragma once
+#include <cstdlib>
+#include <vector>
+#include <utility>
-extern bool InitCurrentDirectory();
-extern bool SetLocalProfileString(char* section, char* key, char* string);
-extern bool GetLocalProfileString(char* section, char* key, char* buffer, int max);
-extern bool SetLocalProfileInt(char* section, char* key, int value);
-extern bool GetLocalProfileInt(char* section, char* key, int &value);
-extern bool SetLocalProfileFloat(char* section, char* key, float value);
-extern bool GetLocalProfileFloat(char* section, char* key, float &value);
+#include <lib/simpleini/SimpleIni.h>
+#include <common/singleton.h>
+/**
+ * @file common/profile.h
+ * @brief Class for loading profile (currently for loading ini config file)
+ */
+
+
+/**
+* @class CProfile
+*
+* @brief Class for loading profile (currently for loading ini config file)
+*
+*/
+class CProfile : public CSingleton<CProfile>
+{
+ public:
+ CProfile();
+ ~CProfile();
+
+ /** Loads colobot.ini from current directory
+ * @return return true on success
+ */
+ bool InitCurrentDirectory();
+
+ /** Sets string value in section under specified key
+ * @param std::string section
+ * @param std::string key
+ * @param std::string value
+ * @return return true on success
+ */
+ bool SetLocalProfileString(std::string section, std::string key, std::string value);
+
+ /** Gets string value in section under specified key
+ * @param std::string section
+ * @param std::string key
+ * @param std::string& buffer
+ * @return return true on success
+ */
+ bool GetLocalProfileString(std::string section, std::string key, std::string& buffer);
+
+ /** Sets int value in section under specified key
+ * @param std::string section
+ * @param std::string key
+ * @param int value
+ * @return return true on success
+ */
+ bool SetLocalProfileInt(std::string section, std::string key, int value);
+
+ /** Gets int value in section under specified key
+ * @param std::string section
+ * @param std::string key
+ * @param int& value
+ * @return return true on success
+ */
+ bool GetLocalProfileInt(std::string section, std::string key, int &value);
+
+ /** Sets float value in section under specified key
+ * @param std::string section
+ * @param std::string key
+ * @param float value
+ * @return return true on success
+ */
+ bool SetLocalProfileFloat(std::string section, std::string key, float value);
+
+ /** Gets float value in section under specified key
+ * @param std::string section
+ * @param std::string key
+ * @param float& value
+ * @return return true on success
+ */
+ bool GetLocalProfileFloat(std::string section, std::string key, float &value);
+
+ /** Gets all values in section under specified key
+ * @param std::string section
+ * @param std::string key
+ * @return vector of values
+ */
+ std::vector< std::string > GetLocalProfileSection(std::string section, std::string key);
+
+ private:
+ CSimpleIniA *m_ini;
+};
+
+//! Global function to get profile instance
+inline CProfile* GetProfile() {
+ return CProfile::GetInstancePointer();
+}
diff --git a/src/common/stringutils.cpp b/src/common/stringutils.cpp
index 585bb46..12a3179 100644
--- a/src/common/stringutils.cpp
+++ b/src/common/stringutils.cpp
@@ -135,15 +135,11 @@ int StrUtils::Utf8CharSizeAt(const std::string &str, unsigned int pos)
size_t StrUtils::Utf8StringLength(const std::string &str)
{
size_t result = 0;
- for (unsigned int i = 0; i < str.size(); ++i)
+ unsigned int i = 0;
+ while (i < str.size())
{
- char ch = str[i];
- if ((ch & 0x80) == 0)
+ i += Utf8CharSizeAt(str, i);
++result;
- else if ((ch & 0xC0) == 0xC0)
- result += 2;
- else
- result += 3;
}
return result;
}
diff --git a/src/common/test/CMakeLists.txt b/src/common/test/CMakeLists.txt
index 680116c..d81acab 100644
--- a/src/common/test/CMakeLists.txt
+++ b/src/common/test/CMakeLists.txt
@@ -1,6 +1,12 @@
cmake_minimum_required(VERSION 2.8)
set(CMAKE_BUILD_TYPE debug)
-set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g -O0")
+set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g -O0 -std=c++11")
+
+include_directories("../../")
+include_directories("../../../")
add_executable(image_test ../image.cpp image_test.cpp)
+add_executable(profile_test ../profile.cpp profile_test.cpp)
+
+add_test(profile_test ./profile_test)
diff --git a/src/common/test/colobot.ini b/src/common/test/colobot.ini
new file mode 100644
index 0000000..f6a5f96
--- /dev/null
+++ b/src/common/test/colobot.ini
@@ -0,0 +1,15 @@
+[test_float]
+float_value=1.5
+
+[test_string]
+string_value=Hello world
+
+[test_int]
+int_value=42
+
+[test_multi]
+entry=1
+entry=2
+entry=3
+entry=4
+entry=5
diff --git a/src/common/test/profile_test.cpp b/src/common/test/profile_test.cpp
new file mode 100644
index 0000000..65e20c5
--- /dev/null
+++ b/src/common/test/profile_test.cpp
@@ -0,0 +1,43 @@
+#include "../profile.h"
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+int main()
+{
+ CProfile profile;
+ profile.InitCurrentDirectory(); // load colobot.ini file
+
+ string result;
+ profile.GetLocalProfileString("test_string", "string_value", result);
+ if (result != "Hello world") {
+ cout << "GetLocalProfileString failed!" << endl;
+ return 1;
+ }
+
+ int int_value;
+ profile.GetLocalProfileInt("test_int", "int_value", int_value);
+ if (int_value != 42) {
+ cout << "GetLocalProfileInt failed!" << endl;
+ return 1;
+ }
+
+ float float_value;
+ profile.GetLocalProfileFloat("test_float", "float_value", float_value);
+ if (float_value != 1.5) {
+ cout << "GetLocalProfileFloat failed!" << endl;
+ return 1;
+ }
+
+ vector<string> list;
+ list = profile.GetLocalProfileSection("test_multi", "entry");
+ if (list.size() != 5) {
+ cout << "GetLocalProfileSection failed!" << endl;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/graphics/README.txt b/src/graphics/README.txt
index 3ec3871..479747b 100644
--- a/src/graphics/README.txt
+++ b/src/graphics/README.txt
@@ -1,3 +1,12 @@
-src/graphics
+/**
+ * \dir graphics
+ * \brief Graphics engine
+ */
-Graphics engine
+/**
+ * \namespace Gfx
+ * \brief Namespace for (new) graphics code
+ *
+ * This namespace was created to avoid clashing with old code, but now it still serves,
+ * defining a border between pure graphics engine and other parts of application.
+ */ \ No newline at end of file
diff --git a/src/graphics/core/README.txt b/src/graphics/core/README.txt
index 12beef9..ca3768c 100644
--- a/src/graphics/core/README.txt
+++ b/src/graphics/core/README.txt
@@ -1,6 +1,7 @@
-src/graphics/core
-
-Abstract core of graphics engine
-
-Core types, enums, structs and CDevice abstract class that define
-the abstract graphics device used in graphics engine
+/**
+ * \dir graphics/core
+ * \brief Abstract core of graphics engine
+ *
+ * Core types, enums, structs and CDevice abstract class that define
+ * the abstract graphics device used in graphics engine
+ */ \ No newline at end of file
diff --git a/src/graphics/core/color.h b/src/graphics/core/color.h
index 6973644..ff8a2eb 100644
--- a/src/graphics/core/color.h
+++ b/src/graphics/core/color.h
@@ -14,11 +14,13 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// color.h
+/**
+ * \file graphics/core/color.h
+ * \brief Color structs and related functions
+ */
#pragma once
-
#include <sstream>
@@ -66,6 +68,11 @@ struct Color
{
return r == other.r && g == other.g && b == other.b && a == other.a;
}
+
+ inline bool operator!=(const Gfx::Color &other) const
+ {
+ return ! this->operator==(other);
+ }
};
/**
diff --git a/src/graphics/core/device.h b/src/graphics/core/device.h
index c10b853..a3d0208 100644
--- a/src/graphics/core/device.h
+++ b/src/graphics/core/device.h
@@ -15,7 +15,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// device.h
+/**
+ * \file graphics/core/device.h
+ * \brief Abstract graphics device - Gfx::CDevice class and related structs/enums
+ */
#pragma once
@@ -25,13 +28,14 @@
#include "graphics/core/material.h"
#include "graphics/core/texture.h"
#include "graphics/core/vertex.h"
-#include "math/intsize.h"
+#include "math/intpoint.h"
#include "math/matrix.h"
#include <string>
class CImage;
+struct ImageData;
namespace Gfx {
@@ -45,7 +49,7 @@ namespace Gfx {
struct DeviceConfig
{
//! Screen size
- Math::IntSize size;
+ Math::IntPoint size;
//! Bits per pixel
int bpp;
//! Full screen
@@ -63,7 +67,7 @@ struct DeviceConfig
//! Loads the default values
inline void LoadDefault()
{
- size = Math::IntSize(800, 600);
+ size = Math::IntPoint(800, 600);
bpp = 32;
fullScreen = false;
resizeable = false;
@@ -149,9 +153,9 @@ enum FogMode
\brief Culling mode for polygons */
enum CullMode
{
- //! Cull clockwise side
+ //! Cull clockwise faces
CULL_CW,
- //! Cull counter-clockwise side
+ //! Cull counter-clockwise faces
CULL_CCW
};
@@ -274,13 +278,14 @@ class CDevice
public:
virtual ~CDevice() {}
+ //! Provides a hook to debug graphics code (implementation-specific)
+ virtual void DebugHook() = 0;
+
//! Initializes the device, setting the initial state
virtual bool Create() = 0;
//! Destroys the device, releasing every acquired resource
virtual void Destroy() = 0;
- //! Returns whether the device has been initialized
- virtual bool GetWasInit() = 0;
//! Returns the last encountered error
virtual std::string GetError() = 0;
@@ -317,6 +322,8 @@ public:
//! Creates a texture from image; the image can be safely removed after that
virtual Gfx::Texture CreateTexture(CImage *image, const Gfx::TextureCreateParams &params) = 0;
+ //! Creates a texture from raw image data; image data can be freed after that
+ virtual Gfx::Texture CreateTexture(ImageData *data, const Gfx::TextureCreateParams &params) = 0;
//! Deletes a given texture, freeing it from video memory
virtual void DestroyTexture(const Gfx::Texture &texture) = 0;
//! Deletes all textures created so far
@@ -324,8 +331,10 @@ public:
//! Returns the maximum number of multitexture stages
virtual int GetMaxTextureCount() = 0;
- //! Sets the (multi)texture at given index
+ //! Sets the texture at given texture stage
virtual void SetTexture(int index, const Gfx::Texture &texture) = 0;
+ //! Sets the texture image by ID at given texture stage
+ virtual void SetTexture(int index, unsigned int textureId) = 0;
//! Returns the (multi)texture at given index
virtual Gfx::Texture GetTexture(int index) = 0;
//! Enables/disables the given texture stage
diff --git a/src/graphics/core/light.h b/src/graphics/core/light.h
index b787cb2..a39d1f5 100644
--- a/src/graphics/core/light.h
+++ b/src/graphics/core/light.h
@@ -15,7 +15,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// light.h
+/**
+ * \file graphics/core/light.h
+ * \brief Light struct and related enums
+ */
#pragma once
diff --git a/src/graphics/core/material.h b/src/graphics/core/material.h
index 31b42f3..eb73c50 100644
--- a/src/graphics/core/material.h
+++ b/src/graphics/core/material.h
@@ -14,7 +14,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// material.h
+/**
+ * \file graphics/core/material.h
+ * \brief Material struct
+ */
#pragma once
diff --git a/src/graphics/core/texture.h b/src/graphics/core/texture.h
index 8d6b082..c36b6c6 100644
--- a/src/graphics/core/texture.h
+++ b/src/graphics/core/texture.h
@@ -14,11 +14,14 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// texture.h
+/**
+ * \file graphics/core/texture.h
+ * \brief Texture struct and related enums
+ */
#pragma once
-#include "math/intsize.h"
+#include "math/intpoint.h"
namespace Gfx {
@@ -194,7 +197,7 @@ struct Texture
//! ID of the texture in graphics engine
unsigned int id;
//! Size of texture
- Math::IntSize size;
+ Math::IntPoint size;
//! Whether the texture has alpha channel
bool alpha;
diff --git a/src/graphics/core/vertex.h b/src/graphics/core/vertex.h
index b7fab1c..53dd642 100644
--- a/src/graphics/core/vertex.h
+++ b/src/graphics/core/vertex.h
@@ -14,7 +14,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// vertex.h
+/**
+ * \file graphics/core/vertex.h
+ * \brief Vertex structs
+ */
#pragma once
diff --git a/src/graphics/d3d/README.txt b/src/graphics/d3d/README.txt
index 8388120..524ae7b 100644
--- a/src/graphics/d3d/README.txt
+++ b/src/graphics/d3d/README.txt
@@ -1,3 +1,4 @@
-src/graphics/d3d
-
-Possible future DirectX implementation of graphics engine
+/**
+ * \dir graphics/d3d
+ * \brief Possible future DirectX implementation of graphics engine
+ */ \ No newline at end of file
diff --git a/src/graphics/engine/README.txt b/src/graphics/engine/README.txt
index 308b601..f64d3dd 100644
--- a/src/graphics/engine/README.txt
+++ b/src/graphics/engine/README.txt
@@ -1,8 +1,9 @@
-src/graphics/engine
-
-Graphics engine
-
-CEngine class and various other classes implementing the main features
-of graphics engine from model loading to decorative particles
-
-Graphics operations are done on abstract interface from src/graphics/core
+/**
+ * \dir graphics/engine
+ * \brief Graphics engine
+ *
+ * CEngine class and various other classes implementing the main features
+ * of graphics engine from model loading to decorative particles
+ *
+ * Graphics operations are done on abstract interface from src/graphics/core
+ */ \ No newline at end of file
diff --git a/src/graphics/engine/camera.cpp b/src/graphics/engine/camera.cpp
index c7ca503..2db6398 100644
--- a/src/graphics/engine/camera.cpp
+++ b/src/graphics/engine/camera.cpp
@@ -29,6 +29,66 @@
#include "physics/physics.h"
+// TODO temporary stubs for CObject and CPhysics
+
+void CObject::SetTransparency(float)
+{
+}
+
+CObject* CObject::GetFret()
+{
+ return nullptr;
+}
+
+CObject* CObject::GetPower()
+{
+ return nullptr;
+}
+
+CObject* CObject::GetTruck()
+{
+ return nullptr;
+}
+
+ObjectType CObject::GetType()
+{
+ return OBJECT_NULL;
+}
+
+void CObject::SetGunGoalH(float)
+{
+}
+
+void CObject::GetGlobalSphere(Math::Vector &pos, float &radius)
+{
+}
+
+float CObject::GetAngleY(int)
+{
+ return 0.0f;
+}
+
+Math::Vector CObject::GetPosition(int)
+{
+ return Math::Vector();
+}
+
+void CObject::SetViewFromHere(Math::Vector &eye, float &dirH, float &dirV,
+ Math::Vector &lookat, Math::Vector &upVec,
+ Gfx::CameraType type)
+{
+}
+
+CPhysics* CObject::GetPhysics()
+{
+ return nullptr;
+}
+
+bool CPhysics::GetLand()
+{
+ return false;
+}
+
//! Changes the level of transparency of an object and objects transported (battery & cargo)
void SetTransparency(CObject* obj, float value)
{
@@ -332,7 +392,7 @@ void Gfx::CCamera::SetType(CameraType type)
SetSmooth(Gfx::CAM_SMOOTH_NORM);
}
-CameraType Gfx::CCamera::GetType()
+Gfx::CameraType Gfx::CCamera::GetType()
{
return m_type;
}
@@ -342,7 +402,7 @@ void Gfx::CCamera::SetSmooth(CameraSmooth type)
m_smooth = type;
}
-CameraSmooth Gfx::CCamera::GetSmoth()
+Gfx::CameraSmooth Gfx::CCamera::GetSmoth()
{
return m_smooth;
}
@@ -692,7 +752,7 @@ void Gfx::CCamera::OverFrame(const Event &event)
}
else
{
- color = Gfx::Color(0.0f. 0.0f, 0.0f);
+ color = Gfx::Color(0.0f, 0.0f, 0.0f);
}
color.a = 0.0f;
m_engine->SetOverColor(color, m_overMode);
@@ -873,7 +933,7 @@ bool Gfx::CCamera::IsCollisionBack(Math::Vector &eye, Math::Vector lookat)
for (int i = 0 ;i < 1000000; i++)
{
- CObject *obj = (CObject*)m_iMan->SearchInstance(CLASS_OBJECT, i);
+ CObject *obj = static_cast<CObject*>( m_iMan->SearchInstance(CLASS_OBJECT, i) );
if (obj == NULL) break;
if (obj->GetTruck()) continue; // battery or cargo?
@@ -899,7 +959,7 @@ bool Gfx::CCamera::IsCollisionBack(Math::Vector &eye, Math::Vector lookat)
iType == OBJECT_SAFE ||
iType == OBJECT_HUSTON ) continue;
- ObjType oType = obj->GetType();
+ ObjectType oType = obj->GetType();
if ( oType == OBJECT_HUMAN ||
oType == OBJECT_TECH ||
oType == OBJECT_TOTO ||
@@ -995,7 +1055,6 @@ bool Gfx::CCamera::EventProcess(const Event &event)
{
switch (event.type)
{
- // TODO: frame update event
case EVENT_FRAME:
EventFrame(event);
break;
@@ -1004,11 +1063,11 @@ bool Gfx::CCamera::EventProcess(const Event &event)
EventMouseMove(event);
break;
- case EVENT_KEY_DOWN:
- // TODO: mouse wheel event
+ // TODO: mouse wheel event
+ /*case EVENT_KEY_DOWN:
if ( event.param == VK_WHEELUP ) EventMouseWheel(+1);
if ( event.param == VK_WHEELDOWN ) EventMouseWheel(-1);
- break;
+ break;*/
default:
break;
@@ -1489,8 +1548,6 @@ bool Gfx::CCamera::EventFrameFix(const Event &event)
bool Gfx::CCamera::EventFrameExplo(const Event &event)
{
- float factor = m_heightEye * 0.5f + 30.0f;
-
if (m_mouseDirH != 0.0f)
m_directionH -= m_mouseDirH * event.rTime * 0.7f * m_speed;
@@ -1526,7 +1583,7 @@ bool Gfx::CCamera::EventFrameOnBoard(const Event &event)
{
Math::Vector lookatPt, upVec;
m_cameraObj->SetViewFromHere(m_eyePt, m_directionH, m_directionV,
- lookatPt, vUpVec, m_type);
+ lookatPt, upVec, m_type);
Math::Vector eye = m_effectOffset * 0.3f + m_eyePt;
Math::Vector lookat = m_effectOffset * 0.3f + lookatPt;
diff --git a/src/graphics/engine/camera.h b/src/graphics/engine/camera.h
index 935f8b0..1a82f9f 100644
--- a/src/graphics/engine/camera.h
+++ b/src/graphics/engine/camera.h
@@ -15,7 +15,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// camera.h
+/**
+ * \file graphics/engine/camera.h
+ * \brief Camera handling - Gfx::CCamera class
+ */
#pragma once
@@ -353,13 +356,13 @@ protected:
float m_centeringTime;
float m_centeringProgress;
- CameraEffect m_effectType;
+ Gfx::CameraEffect m_effectType;
Math::Vector m_effectPos;
float m_effectForce;
float m_effectProgress;
Math::Vector m_effectOffset;
- OverEffect m_overType;
+ Gfx::CameraOverEffect m_overType;
float m_overForce;
float m_overTime;
Gfx::Color m_overColorBase;
diff --git a/src/graphics/engine/cloud.cpp b/src/graphics/engine/cloud.cpp
index d0e5ed8..f3c0002 100644
--- a/src/graphics/engine/cloud.cpp
+++ b/src/graphics/engine/cloud.cpp
@@ -19,5 +19,265 @@
#include "graphics/engine/cloud.h"
+#include "common/iman.h"
+#include "graphics/engine/engine.h"
+#include "graphics/engine/terrain.h"
+#include "math/geometry.h"
-// TODO implementation
+
+const int CLOUD_LINE_PREALLOCATE_COUNT = 100;
+
+const int DIMEXPAND = 4; // extension of the dimensions
+
+
+Gfx::CCloud::CCloud(CInstanceManager* iMan, Gfx::CEngine* engine)
+{
+ m_iMan = iMan;
+ m_iMan->AddInstance(CLASS_CLOUD, this);
+
+ m_engine = engine;
+ m_terrain = nullptr;
+
+ m_level = 0.0f;
+ m_wind = Math::Vector(0.0f, 0.0f, 0.0f);
+ m_subdiv = 8;
+ m_enable = true;
+
+ m_line.reserve(CLOUD_LINE_PREALLOCATE_COUNT);
+}
+
+Gfx::CCloud::~CCloud()
+{
+ m_iMan = nullptr;
+ m_engine = nullptr;
+ m_terrain = nullptr;
+}
+
+bool Gfx::CCloud::EventProcess(const Event &event)
+{
+ if ( event.type == EVENT_FRAME )
+ return EventFrame(event);
+
+ return true;
+}
+
+bool Gfx::CCloud::EventFrame(const Event &event)
+{
+ if (m_engine->GetPause()) return true;
+
+ m_time += event.rTime;
+
+ if (m_level == 0.0f) return true;
+
+ if (m_time - m_lastTest < 0.2f) return true;
+
+ m_lastTest = m_time;
+
+ return true;
+}
+
+void Gfx::CCloud::AdjustLevel(Math::Vector &pos, Math::Vector &eye, float deep,
+ Math::Point &uv1, Math::Point &uv2)
+{
+ uv1.x = (pos.x+20000.0f)/1280.0f;
+ uv1.y = (pos.z+20000.0f)/1280.0f;
+ uv1.x -= m_time*(m_wind.x/100.0f);
+ uv1.y -= m_time*(m_wind.z/100.0f);
+
+ uv2.x = 0.0f;
+ uv2.y = 0.0f;
+
+ float dist = Math::DistanceProjected(pos, eye);
+ float factor = powf(dist/deep, 2.0f);
+ pos.y -= m_level*factor*10.0f;
+}
+
+void Gfx::CCloud::Draw()
+{
+ /* TODO!
+ LPDIRECT3DDEVICE7 device;
+ D3DVERTEX2* vertex;
+ Math::Matrix* matView;
+ D3DMATERIAL7 material;
+ Math::Matrix matrix;
+ Math::Vector n, pos, p, eye;
+ Math::Point uv1, uv2;
+ float iDeep, deep, size, fogStart, fogEnd;
+ int i, j, u;
+
+ if ( !m_enable ) return;
+ if ( m_level == 0.0f ) return;
+ if ( m_lineUsed == 0 ) return;
+
+ vertex = (D3DVERTEX2*)malloc(sizeof(D3DVERTEX2)*(m_brick+2)*2);
+
+ iDeep = m_engine->GetDeepView();
+ deep = (m_brick*m_size)/2.0f;
+ m_engine->SetDeepView(deep);
+ m_engine->SetFocus(m_engine->GetFocus());
+ m_engine->UpdateMatProj(); // increases the depth of view
+
+ fogStart = deep*0.15f;
+ fogEnd = deep*0.24f;
+
+ device = m_engine->GetD3DDevice();
+ device->SetRenderState(D3DRENDERSTATE_AMBIENT, 0x00000000);
+ device->SetRenderState(D3DRENDERSTATE_LIGHTING, false);
+ device->SetRenderState(D3DRENDERSTATE_ZENABLE, false);
+ device->SetRenderState(D3DRENDERSTATE_FOGENABLE, true);
+ device->SetRenderState(D3DRENDERSTATE_FOGSTART, F2DW(fogStart));
+ device->SetRenderState(D3DRENDERSTATE_FOGEND, F2DW(fogEnd));
+
+ matView = m_engine->GetMatView();
+ {
+ D3DMATRIX mat = MAT_TO_D3DMAT(*matView);
+ device->SetTransform(D3DTRANSFORMSTATE_VIEW, &mat);
+ }
+
+ ZeroMemory( &material, sizeof(D3DMATERIAL7) );
+ material.diffuse = m_diffuse;
+ material.ambient = m_ambient;
+ m_engine->SetMaterial(material);
+
+ m_engine->SetTexture(m_filename, 0);
+ m_engine->SetTexture(m_filename, 1);
+
+ m_engine->SetState(D3DSTATETTb|D3DSTATEFOG|D3DSTATEWRAP);
+
+ matrix.LoadIdentity();
+ {
+ D3DMATRIX mat = MAT_TO_D3DMAT(matrix);
+ device->SetTransform(D3DTRANSFORMSTATE_WORLD, &mat);
+ }
+
+ size = m_size/2.0f;
+ eye = m_engine->GetEyePt();
+ n = Math::Vector(0.0f, -1.0f, 0.0f);
+
+ // Draws all the lines.
+ for ( i=0 ; i<m_lineUsed ; i++ )
+ {
+ pos.y = m_level;
+ pos.z = m_line[i].pz;
+ pos.x = m_line[i].px1;
+
+ u = 0;
+ p.x = pos.x-size;
+ p.z = pos.z+size;
+ p.y = pos.y;
+ AdjustLevel(p, eye, deep, uv1, uv2);
+ vertex[u++] = D3DVERTEX2(p, n, uv1.x,uv1.y, uv2.x,uv2.y);
+
+ p.x = pos.x-size;
+ p.z = pos.z-size;
+ p.y = pos.y;
+ AdjustLevel(p, eye, deep, uv1, uv2);
+ vertex[u++] = D3DVERTEX2(p, n, uv1.x,uv1.y, uv2.x,uv2.y);
+
+ for ( j=0 ; j<m_line[i].len ; j++ )
+ {
+ p.x = pos.x+size;
+ p.z = pos.z+size;
+ p.y = pos.y;
+ AdjustLevel(p, eye, deep, uv1, uv2);
+ vertex[u++] = D3DVERTEX2(p, n, uv1.x,uv1.y, uv2.x,uv2.y);
+
+ p.x = pos.x+size;
+ p.z = pos.z-size;
+ p.y = pos.y;
+ AdjustLevel(p, eye, deep, uv1, uv2);
+ vertex[u++] = D3DVERTEX2(p, n, uv1.x,uv1.y, uv2.x,uv2.y);
+
+ pos.x += size*2.0f;
+ }
+
+ device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, u, NULL);
+ m_engine->AddStatisticTriangle(u-2);
+ }
+
+ m_engine->SetDeepView(iDeep);
+ m_engine->SetFocus(m_engine->GetFocus());
+ m_engine->UpdateMatProj(); // gives depth to initial
+
+ free(vertex); */
+}
+
+void Gfx::CCloud::CreateLine(int x, int y, int len)
+{
+ Gfx::CloudLine line;
+
+ line.x = x;
+ line.y = y;
+ line.len = len;
+
+ float offset = m_brick*m_size/2.0f - m_size/2.0f;
+
+ line.px1 = m_size* line.x - offset;
+ line.px2 = m_size*(line.x+line.len) - offset;
+ line.pz = m_size* line.y - offset;
+
+ m_line.push_back(line);
+}
+
+void Gfx::CCloud::Create(const std::string& fileName,
+ Gfx::Color diffuse, Gfx::Color ambient,
+ float level)
+{
+ m_diffuse = diffuse;
+ m_ambient = ambient;
+ m_level = level;
+ m_time = 0.0f;
+ m_lastTest = 0.0f;
+ m_fileName = fileName;
+
+ if (! m_fileName.empty())
+ m_engine->LoadTexture(m_fileName);
+
+ if (m_terrain == nullptr)
+ m_terrain = static_cast<CTerrain*>(m_iMan->SearchInstance(CLASS_TERRAIN));
+
+ m_wind = m_terrain->GetWind();
+
+ m_brick = m_terrain->GetBrick()*m_terrain->GetMosaic()*DIMEXPAND;
+ m_size = m_terrain->GetSize();
+
+ m_brick /= m_subdiv*DIMEXPAND;
+ m_size *= m_subdiv*DIMEXPAND;
+
+ if (m_level == 0.0f)
+ return;
+
+ m_line.clear();
+ for (int y = 0; y < m_brick; y++)
+ CreateLine(0, y, m_brick);
+
+ return;
+}
+
+void Gfx::CCloud::Flush()
+{
+ m_level = 0.0f;
+}
+
+
+void Gfx::CCloud::SetLevel(float level)
+{
+ m_level = level;
+
+ Create(m_fileName, m_diffuse, m_ambient, m_level);
+}
+
+float Gfx::CCloud::GetLevel()
+{
+ return m_level;
+}
+
+void Gfx::CCloud::SetEnable(bool enable)
+{
+ m_enable = enable;
+}
+
+bool Gfx::CCloud::GetEnable()
+{
+ return m_enable;
+}
diff --git a/src/graphics/engine/cloud.h b/src/graphics/engine/cloud.h
index d2d29d7..881a598 100644
--- a/src/graphics/engine/cloud.h
+++ b/src/graphics/engine/cloud.h
@@ -15,7 +15,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// cloud.h
+/**
+ * \file graphics/engine/cloud.h
+ * \brief Cloud rendering - Gfx::CCloud class
+ */
#pragma once
@@ -24,6 +27,9 @@
#include "math/point.h"
#include "math/vector.h"
+#include <vector>
+#include <string>
+
class CInstanceManager;
@@ -34,13 +40,20 @@ namespace Gfx {
class CEngine;
class CTerrain;
-const short MAXCLOUDLINE = 100;
-
struct CloudLine
{
- short x, y; // beginning
- short len; // in length x
+ //! Beginning
+ short x, y;
+ //! In length x
+ short len;
float px1, px2, pz;
+
+ CloudLine()
+ {
+ x = y = 0;
+ len = 0;
+ px1 = px2 = pz = 0;
+ }
};
@@ -51,43 +64,59 @@ public:
~CCloud();
bool EventProcess(const Event &event);
+ //! Removes all the clouds
void Flush();
- bool Create(const char *filename, Gfx::Color diffuse, Gfx::Color ambient, float level);
+ //! Creates all areas of cloud
+ void Create(const std::string& fileName, Gfx::Color diffuse, Gfx::Color ambient, float level);
+ //! Draw the clouds
void Draw();
- bool SetLevel(float level);
- float RetLevel();
+ //! Modifies the cloud level
+ void SetLevel(float level);
+ //! Returns the current level of clouds
+ float GetLevel();
- void SetEnable(bool bEnable);
- bool RetEnable();
+ //! Activate management of clouds
+ void SetEnable(bool enable);
+ bool GetEnable();
protected:
+ //! Makes the clouds evolve
bool EventFrame(const Event &event);
- void AdjustLevel(Math::Vector &pos, Math::Vector &eye, float deep, Math::Point &uv1, Math::Point &uv2);
- bool CreateLine(int x, int y, int len);
+ //! Adjusts the position to normal, to imitate the clouds at movement
+ void AdjustLevel(Math::Vector &pos, Math::Vector &eye, float deep,
+ Math::Point &uv1, Math::Point &uv2);
+ //! Updates the positions, relative to the ground
+ void CreateLine(int x, int y, int len);
protected:
- CInstanceManager* m_iMan;
- CEngine* m_engine;
- CTerrain* m_terrain;
-
- char m_filename[100];
- float m_level; // overall level
- Math::Point m_speed; // feedrate (wind)
- Gfx::Color m_diffuse; // diffuse color
- Gfx::Color m_ambient; // ambient color
+ CInstanceManager* m_iMan;
+ Gfx::CEngine* m_engine;
+ Gfx::CTerrain* m_terrain;
+
+ std::string m_fileName;
+ //! Overall level
+ float m_level;
+ //! Feedrate (wind)
+ Math::Point m_speed;
+ //! Diffuse color
+ Gfx::Color m_diffuse;
+ //! Ambient color
+ Gfx::Color m_ambient;
float m_time;
float m_lastTest;
int m_subdiv;
- Math::Vector m_wind; // wind speed
- int m_brick; // brick mosaic
- float m_size; // size of a brick element
+ //! Wind speed
+ Math::Vector m_wind;
+ //! Brick mosaic
+ int m_brick;
+ //! Size of a brick element
+ float m_size;
- int m_lineUsed;
- CloudLine m_line[MAXCLOUDLINE];
+ std::vector<Gfx::CloudLine> m_line;
- bool m_bEnable;
+ bool m_enable;
};
diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp
index e544ee3..644ecaf 100644
--- a/src/graphics/engine/engine.cpp
+++ b/src/graphics/engine/engine.cpp
@@ -25,7 +25,19 @@
#include "common/key.h"
#include "common/logger.h"
#include "graphics/core/device.h"
+#include "graphics/engine/camera.h"
+#include "graphics/engine/cloud.h"
+#include "graphics/engine/lightman.h"
+#include "graphics/engine/lightning.h"
+#include "graphics/engine/particle.h"
+#include "graphics/engine/planet.h"
+#include "graphics/engine/pyro.h"
+#include "graphics/engine/terrain.h"
+#include "graphics/engine/text.h"
+#include "graphics/engine/water.h"
#include "math/geometry.h"
+#include "sound/sound.h"
+
// Initial size of various vectors
const int OBJECT_PREALLOCATE_COUNT = 1200;
@@ -40,32 +52,35 @@ const int LEVEL5_PREALLOCATE_COUNT = 100;
const int LEVEL5_VERTEX_PREALLOCATE_COUNT = 200;
+// TODO: temporary stub for CInterface
+class CInterface
+{
+public:
+ void Draw() {}
+};
+
Gfx::CEngine::CEngine(CInstanceManager *iMan, CApplication *app)
{
m_iMan = iMan;
m_app = app;
- m_device = NULL;
-
- m_wasInit = false;
+ m_device = nullptr;
m_iMan = iMan;
m_iMan->AddInstance(CLASS_ENGINE, this);
m_app = app;
- m_lightMan = NULL;
- m_text = NULL;
- m_particle = NULL;
- m_water = NULL;
- m_cloud = NULL;
- m_lightning = NULL;
- m_planet = NULL;
- m_sound = NULL;
- m_terrain = NULL;
+ m_lightMan = nullptr;
+ m_text = nullptr;
+ m_particle = nullptr;
+ m_water = nullptr;
+ m_cloud = nullptr;
+ m_lightning = nullptr;
+ m_planet = nullptr;
+ m_sound = nullptr;
+ m_terrain = nullptr;
m_focus = 0.75f;
- m_baseTime = 0;
- m_lastTime = 0;
- m_absTime = 0.0f;
+
m_rankView = 0;
m_ambientColor[0] = Gfx::Color(0.5f, 0.5f, 0.5f, 0.5f);
@@ -99,15 +114,15 @@ Gfx::CEngine::CEngine(CInstanceManager *iMan, CApplication *app)
m_overFront = true;
m_overColor = 0;
m_overMode = ENG_RSTATE_TCOLOR_BLACK;
- m_frontsizeName = ""; // no front image
- m_hiliteRank[0] = -1; // empty list
+ m_highlightRank[0] = -1; // empty list
+ m_highlightTime = 0.0f;
m_eyePt = Math::Vector(0.0f, 0.0f, 0.0f);
m_lookatPt = Math::Vector(0.0f, 0.0f, 1.0f);
m_drawWorld = true;
m_drawFront = false;
m_limitLOD[0] = 100.0f;
m_limitLOD[1] = 200.0f;
- m_particuleDensity = 1.0f;
+ m_particleDensity = 1.0f;
m_clippingDistance = 1.0f;
m_lastClippingDistance = m_clippingDistance;
m_objectDetail = 1.0f;
@@ -119,7 +134,7 @@ Gfx::CEngine::CEngine(CInstanceManager *iMan, CApplication *app)
m_lensMode = true;
m_waterMode = true;
m_skyMode = true;
- m_backForce = true;
+ m_backForce = false; // TODO: change to true?
m_planetMode = true;
m_lightMode = true;
m_editIndentMode = true;
@@ -131,33 +146,24 @@ Gfx::CEngine::CEngine(CInstanceManager *iMan, CApplication *app)
m_forceStateColor = true;
m_stateColor = false;
- m_blackSrcBlend[0] = 0;
- m_blackDestBlend[0] = 0;
- m_whiteSrcBlend[0] = 0;
- m_whiteDestBlend[0] = 0;
- m_diffuseSrcBlend[0] = 0;
- m_diffuseDestBlend[0] = 0;
- m_alphaSrcBlend[0] = 0;
- m_alphaDestBlend[0] = 0;
-
m_updateGeometry = false;
- m_mice[Gfx::ENG_MOUSE_NORM] = Gfx::EngineMouse( 0, 1, 32, Gfx::ENG_RSTATE_TCOLOR_WHITE, Gfx::ENG_RSTATE_TCOLOR_BLACK, Math::Point( 1.0f, 1.0f));
- m_mice[Gfx::ENG_MOUSE_WAIT] = Gfx::EngineMouse( 2, 3, 33, Gfx::ENG_RSTATE_TCOLOR_WHITE, Gfx::ENG_RSTATE_TCOLOR_BLACK, Math::Point( 8.0f, 12.0f));
- m_mice[Gfx::ENG_MOUSE_HAND] = Gfx::EngineMouse( 4, 5, 34, Gfx::ENG_RSTATE_TCOLOR_WHITE, Gfx::ENG_RSTATE_TCOLOR_BLACK, Math::Point( 7.0f, 2.0f));
- m_mice[Gfx::ENG_MOUSE_NO] = Gfx::EngineMouse( 6, 7, 35, Gfx::ENG_RSTATE_TCOLOR_WHITE, Gfx::ENG_RSTATE_TCOLOR_BLACK, Math::Point(10.0f, 10.0f));
- m_mice[Gfx::ENG_MOUSE_EDIT] = Gfx::EngineMouse( 8, 9, -1, Gfx::ENG_RSTATE_TCOLOR_BLACK, Gfx::ENG_RSTATE_TCOLOR_WHITE, Math::Point( 6.0f, 10.0f));
- m_mice[Gfx::ENG_MOUSE_CROSS] = Gfx::EngineMouse(10, 11, -1, Gfx::ENG_RSTATE_TCOLOR_BLACK, Gfx::ENG_RSTATE_TCOLOR_WHITE, Math::Point(10.0f, 10.0f));
- m_mice[Gfx::ENG_MOUSE_MOVEV] = Gfx::EngineMouse(12, 13, -1, Gfx::ENG_RSTATE_TCOLOR_BLACK, Gfx::ENG_RSTATE_TCOLOR_WHITE, Math::Point( 5.0f, 11.0f));
- m_mice[Gfx::ENG_MOUSE_MOVEH] = Gfx::EngineMouse(14, 15, -1, Gfx::ENG_RSTATE_TCOLOR_BLACK, Gfx::ENG_RSTATE_TCOLOR_WHITE, Math::Point(11.0f, 5.0f));
- m_mice[Gfx::ENG_MOUSE_MOVED] = Gfx::EngineMouse(16, 17, -1, Gfx::ENG_RSTATE_TCOLOR_BLACK, Gfx::ENG_RSTATE_TCOLOR_WHITE, Math::Point( 9.0f, 9.0f));
- m_mice[Gfx::ENG_MOUSE_MOVEI] = Gfx::EngineMouse(18, 19, -1, Gfx::ENG_RSTATE_TCOLOR_BLACK, Gfx::ENG_RSTATE_TCOLOR_WHITE, Math::Point( 9.0f, 9.0f));
- m_mice[Gfx::ENG_MOUSE_MOVE] = Gfx::EngineMouse(20, 21, -1, Gfx::ENG_RSTATE_TCOLOR_BLACK, Gfx::ENG_RSTATE_TCOLOR_WHITE, Math::Point(11.0f, 11.0f));
- m_mice[Gfx::ENG_MOUSE_TARGET] = Gfx::EngineMouse(22, 23, -1, Gfx::ENG_RSTATE_TCOLOR_BLACK, Gfx::ENG_RSTATE_TCOLOR_WHITE, Math::Point(15.0f, 15.0f));
- m_mice[Gfx::ENG_MOUSE_SCROLLL] = Gfx::EngineMouse(24, 25, 43, Gfx::ENG_RSTATE_TCOLOR_BLACK, Gfx::ENG_RSTATE_TCOLOR_WHITE, Math::Point( 2.0f, 9.0f));
- m_mice[Gfx::ENG_MOUSE_SCROLLR] = Gfx::EngineMouse(26, 27, 44, Gfx::ENG_RSTATE_TCOLOR_BLACK, Gfx::ENG_RSTATE_TCOLOR_WHITE, Math::Point(17.0f, 9.0f));
- m_mice[Gfx::ENG_MOUSE_SCROLLU] = Gfx::EngineMouse(28, 29, 45, Gfx::ENG_RSTATE_TCOLOR_BLACK, Gfx::ENG_RSTATE_TCOLOR_WHITE, Math::Point( 9.0f, 2.0f));
- m_mice[Gfx::ENG_MOUSE_SCROLLD] = Gfx::EngineMouse(30, 31, 46, Gfx::ENG_RSTATE_TCOLOR_BLACK, Gfx::ENG_RSTATE_TCOLOR_WHITE, Math::Point( 9.0f, 17.0f));
+ m_mice[Gfx::ENG_MOUSE_NORM] = Gfx::EngineMouse( 0, 1, 32, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Math::Point( 1.0f, 1.0f));
+ m_mice[Gfx::ENG_MOUSE_WAIT] = Gfx::EngineMouse( 2, 3, 33, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Math::Point( 8.0f, 12.0f));
+ m_mice[Gfx::ENG_MOUSE_HAND] = Gfx::EngineMouse( 4, 5, 34, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Math::Point( 7.0f, 2.0f));
+ m_mice[Gfx::ENG_MOUSE_NO] = Gfx::EngineMouse( 6, 7, 35, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Math::Point(10.0f, 10.0f));
+ m_mice[Gfx::ENG_MOUSE_EDIT] = Gfx::EngineMouse( 8, 9, -1, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 6.0f, 10.0f));
+ m_mice[Gfx::ENG_MOUSE_CROSS] = Gfx::EngineMouse(10, 11, -1, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Math::Point(10.0f, 10.0f));
+ m_mice[Gfx::ENG_MOUSE_MOVEV] = Gfx::EngineMouse(12, 13, -1, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 5.0f, 11.0f));
+ m_mice[Gfx::ENG_MOUSE_MOVEH] = Gfx::EngineMouse(14, 15, -1, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Math::Point(11.0f, 5.0f));
+ m_mice[Gfx::ENG_MOUSE_MOVED] = Gfx::EngineMouse(16, 17, -1, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 9.0f, 9.0f));
+ m_mice[Gfx::ENG_MOUSE_MOVEI] = Gfx::EngineMouse(18, 19, -1, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 9.0f, 9.0f));
+ m_mice[Gfx::ENG_MOUSE_MOVE] = Gfx::EngineMouse(20, 21, -1, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Math::Point(11.0f, 11.0f));
+ m_mice[Gfx::ENG_MOUSE_TARGET] = Gfx::EngineMouse(22, 23, -1, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Math::Point(15.0f, 15.0f));
+ m_mice[Gfx::ENG_MOUSE_SCROLLL] = Gfx::EngineMouse(24, 25, 43, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 2.0f, 9.0f));
+ m_mice[Gfx::ENG_MOUSE_SCROLLR] = Gfx::EngineMouse(26, 27, 44, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Math::Point(17.0f, 9.0f));
+ m_mice[Gfx::ENG_MOUSE_SCROLLU] = Gfx::EngineMouse(28, 29, 45, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 9.0f, 2.0f));
+ m_mice[Gfx::ENG_MOUSE_SCROLLD] = Gfx::EngineMouse(30, 31, 46, Gfx::ENG_RSTATE_TTEXTURE_BLACK, Gfx::ENG_RSTATE_TTEXTURE_WHITE, Math::Point( 9.0f, 17.0f));
m_mouseSize = Math::Point(0.04f, 0.04f * (800.0f / 600.0f));
m_mousePos = Math::Point(0.5f, 0.5f);
@@ -165,185 +171,721 @@ Gfx::CEngine::CEngine(CInstanceManager *iMan, CApplication *app)
m_mouseVisible = false;
m_texPath = "textures/";
- m_defaultTexParams.format = Gfx::TEX_IMG_RGBA;
+ m_defaultTexParams.format = Gfx::TEX_IMG_RGB;
m_defaultTexParams.mipmap = true;
m_defaultTexParams.minFilter = Gfx::TEX_MIN_FILTER_LINEAR_MIPMAP_LINEAR;
m_defaultTexParams.magFilter = Gfx::TEX_MAG_FILTER_LINEAR;
m_objectTree.reserve(LEVEL1_PREALLOCATE_COUNT);
m_objects.reserve(OBJECT_PREALLOCATE_COUNT);
- m_shadow.reserve(SHADOW_PREALLOCATE_COUNT);
- m_groundSpot.reserve(GROUNDSPOT_PREALLOCATE_COUNT);
+ m_shadows.reserve(SHADOW_PREALLOCATE_COUNT);
+ m_groundSpots.reserve(GROUNDSPOT_PREALLOCATE_COUNT);
}
Gfx::CEngine::~CEngine()
{
- m_iMan = NULL;
- m_app = NULL;
- m_device = NULL;
+ m_iMan = nullptr;
+ m_app = nullptr;
+ m_device = nullptr;
+ m_sound = nullptr;
+ m_terrain = nullptr;
+}
- m_sound = NULL;
- m_terrain = NULL;
+void Gfx::CEngine::SetDevice(Gfx::CDevice *device)
+{
+ m_device = device;
}
-bool Gfx::CEngine::GetWasInit()
+Gfx::CDevice* Gfx::CEngine::GetDevice()
+{
+ return m_device;
+}
+
+void Gfx::CEngine::SetTerrain(Gfx::CTerrain* terrain)
{
- return m_wasInit;
+ m_terrain = terrain;
}
-std::string Gfx::CEngine::GetError()
+Gfx::CText* Gfx::CEngine::GetText()
{
- return m_error;
+ return m_text;
}
bool Gfx::CEngine::Create()
{
- m_wasInit = true;
+ m_size = m_lastSize = m_app->GetVideoConfig().size;
- /*m_lightMan = new Gfx::CLight(m_iMan, this);
+ m_lightMan = new Gfx::CLightManager(m_iMan, this);
m_text = new Gfx::CText(m_iMan, this);
m_particle = new Gfx::CParticle(m_iMan, this);
m_water = new Gfx::CWater(m_iMan, this);
m_cloud = new Gfx::CCloud(m_iMan, this);
m_lightning = new Gfx::CLightning(m_iMan, this);
- m_planet = new Gfx::CPlanet(m_iMan, this);*/
+ m_planet = new Gfx::CPlanet(m_iMan, this);
+
+ m_text->SetDevice(m_device);
+ if (! m_text->Create())
+ {
+ std::string error = m_text->GetError();
+ GetLogger()->Error("Error creating CText: %s\n", error.c_str());
+ return false;
+ }
+
+ m_device->SetClearColor(Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f));
+ m_device->SetShadeModel(Gfx::SHADE_SMOOTH);
+ m_device->SetFillMode(Gfx::FILL_FILL);
+
+ SetFocus(m_focus);
m_matWorldInterface.LoadIdentity();
m_matViewInterface.LoadIdentity();
+
Math::LoadOrthoProjectionMatrix(m_matProjInterface, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f);
+ Gfx::TextureCreateParams params;
+ params.format = Gfx::TEX_IMG_RGB;
+ params.minFilter = Gfx::TEX_MIN_FILTER_NEAREST;
+ params.magFilter = Gfx::TEX_MAG_FILTER_NEAREST;
+ params.mipmap = false;
+ m_miceTexture = CreateTexture("mouse.png", params);
+
return true;
}
void Gfx::CEngine::Destroy()
{
- // TODO
+ m_text->Destroy();
- /*delete m_lightMan;
- m_lightMan = NULL;
+ delete m_lightMan;
+ m_lightMan = nullptr;
delete m_text;
- m_text = NULL;
+ m_text = nullptr;
delete m_particle;
- m_particle = NULL;
+ m_particle = nullptr;
delete m_water;
- m_water = NULL;
+ m_water = nullptr;
delete m_cloud;
- m_cloud = NULL;
+ m_cloud = nullptr;
delete m_lightning;
- m_lightning = NULL;
+ m_lightning = nullptr;
delete m_planet;
- m_planet = NULL;*/
+ m_planet = nullptr;
+}
- m_wasInit = false;
+void Gfx::CEngine::ResetAfterDeviceChanged()
+{
+ // TODO reload textures, reset device state, etc.
}
-void Gfx::CEngine::SetDevice(Gfx::CDevice *device)
+bool Gfx::CEngine::ProcessEvent(const Event &event)
{
- m_device = device;
+ if (event.type == EVENT_KEY_DOWN)
+ {
+ // !! Debug, to be removed later !!
+
+ if (event.key.key == KEY(F1))
+ {
+ m_mouseVisible = !m_mouseVisible;
+ m_app->SetSystemMouseVisible(! m_app->GetSystemMouseVisibile());
+ }
+ else if (event.key.key == KEY(F2))
+ {
+ int index = static_cast<int>(m_mouseType);
+ m_mouseType = static_cast<Gfx::EngineMouseType>( (index + 1) % Gfx::ENG_MOUSE_COUNT );
+ }
+ else if (event.key.key == KEY(F3))
+ {
+ m_backgroundQuarter = !m_backgroundQuarter;
+ if (m_backgroundQuarter)
+ {
+ m_backgroundFull = true;
+ m_backgroundName = "geneda.png";
+ }
+ else
+ {
+ m_backgroundFull = false;
+ m_backgroundName = "";
+ }
+ }
+ else if (event.key.key == KEY(F4))
+ {
+ m_backForce = !m_backForce;
+ if (m_backForce)
+ {
+ m_backgroundColorDown = Gfx::Color(0.2f, 0.2f, 0.2f);
+ m_backgroundColorUp = Gfx::Color(0.8f, 0.8f, 0.8f);
+ }
+ else
+ {
+ m_backgroundColorDown = m_backgroundColorUp = Gfx::Color(0.0f, 0.0f, 0.0f);
+ }
+ }
+ }
+ else if (event.type == EVENT_FRAME)
+ {
+ m_highlightTime += event.rTime;
+ }
+
+ // By default, pass on all events
+ return true;
}
-Gfx::CDevice* Gfx::CEngine::GetDevice()
+void Gfx::CEngine::FrameMove(float rTime)
{
- return m_device;
+ m_lightMan->UpdateProgression(rTime);
+ m_particle->FrameParticle(rTime);
+ ComputeDistance();
+ UpdateGeometry();
+
+ if (m_groundMark.draw)
+ {
+ if (m_groundMark.phase == Gfx::ENG_GR_MARK_PHASE_INC) // growing?
+ {
+ m_groundMark.intensity += rTime*(1.0f/m_groundMark.delay[0]);
+ if (m_groundMark.intensity >= 1.0f)
+ {
+ m_groundMark.intensity = 1.0f;
+ m_groundMark.fix = 0.0f;
+ m_groundMark.phase = Gfx::ENG_GR_MARK_PHASE_FIX;
+ }
+ }
+ else if (m_groundMark.phase == Gfx::ENG_GR_MARK_PHASE_FIX) // fixed?
+ {
+ m_groundMark.fix += rTime*(1.0f/m_groundMark.delay[1]);
+ if (m_groundMark.fix >= 1.0f)
+ m_groundMark.phase = Gfx::ENG_GR_MARK_PHASE_DEC;
+ }
+ else if (m_groundMark.phase == Gfx::ENG_GR_MARK_PHASE_DEC) // decay?
+ {
+ m_groundMark.intensity -= rTime*(1.0f/m_groundMark.delay[2]);
+ if (m_groundMark.intensity < 0.0f)
+ {
+ m_groundMark.intensity = 0.0f;
+ m_groundMark.phase = Gfx::ENG_GR_MARK_PHASE_NULL;
+ m_groundMark.draw = false;
+ }
+ }
+ }
+
+ if (m_sound == nullptr)
+ m_sound = static_cast<CSoundInterface*>( m_iMan->SearchInstance(CLASS_SOUND) );
+
+ m_sound->FrameMove(rTime);
}
-bool Gfx::CEngine::AfterDeviceSetInit()
+void Gfx::CEngine::StepSimulation(float rTime)
{
- m_device->SetClearColor(Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f));
+ m_app->StepSimulation(rTime);
+}
- m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_TEST, false);
+bool Gfx::CEngine::WriteScreenShot(const std::string& fileName, int width, int height)
+{
+ // TODO!
+ return true;
+}
- Gfx::TextureCreateParams params;
- params.format = Gfx::TEX_IMG_RGB;
- params.minFilter = Gfx::TEX_MIN_FILTER_NEAREST;
- params.magFilter = Gfx::TEX_MAG_FILTER_NEAREST;
- params.mipmap = false;
- m_miceTexture = CreateTexture("mouse.png", params);
+bool Gfx::CEngine::ReadSettings()
+{
+ // TODO!
+ return true;
+}
+bool Gfx::CEngine::WriteSettings()
+{
+ // TODO!
return true;
}
-void Gfx::CEngine::ResetAfterDeviceChanged()
+void Gfx::CEngine::SetPause(bool pause)
{
- // TODO
+ m_pause = pause;
}
+bool Gfx::CEngine::GetPause()
+{
+ return m_pause;
+}
-Gfx::Texture Gfx::CEngine::CreateTexture(const std::string &texName, const Gfx::TextureCreateParams &params)
+void Gfx::CEngine::SetMovieLock(bool lock)
{
- CImage img;
- if (! img.Load(m_app->GetDataFilePath(m_texPath, texName)))
+ m_movieLock = lock;
+}
+
+bool Gfx::CEngine::GetMovieLock()
+{
+ return m_movieLock;
+}
+
+void Gfx::CEngine::SetShowStats(bool show)
+{
+ m_showStats = show;
+}
+
+bool Gfx::CEngine::GetShowStats()
+{
+ return m_showStats;
+}
+
+void Gfx::CEngine::SetRenderEnable(bool enable)
+{
+ m_render = enable;
+}
+
+Math::IntPoint Gfx::CEngine::GetWindowSize()
+{
+ return m_size;
+}
+
+Math::IntPoint Gfx::CEngine::GetLastWindowSize()
+{
+ return m_lastSize;
+}
+
+Math::Point Gfx::CEngine::WindowToInterfaceCoords(Math::IntPoint pos)
+{
+ return Math::Point( static_cast<float>(pos.x) / static_cast<float>(m_size.x),
+ 1.0f - static_cast<float>(pos.y) / static_cast<float>(m_size.y) );
+}
+
+Math::IntPoint Gfx::CEngine::InterfaceToWindowCoords(Math::Point pos)
+{
+ return Math::IntPoint(static_cast<int>(pos.x * m_size.x),
+ static_cast<int>((1.0f - pos.y) * m_size.y));
+}
+
+Math::Point Gfx::CEngine::WindowToInterfaceSize(Math::IntPoint size)
+{
+ return Math::Point(static_cast<float>(size.x) / static_cast<float>(m_size.x),
+ static_cast<float>(size.y) / static_cast<float>(m_size.y));
+}
+
+Math::IntPoint Gfx::CEngine::InterfaceToWindowSize(Math::Point size)
+{
+ return Math::IntPoint(static_cast<int>(size.x * m_size.x),
+ static_cast<int>(size.y * m_size.y));
+}
+
+std::string Gfx::CEngine::GetTextureDir()
+{
+ return m_texPath;
+}
+
+void Gfx::CEngine::AddStatisticTriangle(int count)
+{
+ m_statisticTriangle += count;
+}
+
+int Gfx::CEngine::GetStatisticTriangle()
+{
+ return m_statisticTriangle;
+}
+
+
+
+/*******************************************************
+ Object management
+ *******************************************************/
+
+
+
+int Gfx::CEngine::CreateObject()
+{
+ // TODO!
+ return 0;
+}
+
+void Gfx::CEngine::FlushObject()
+{
+ // TODO!
+}
+
+bool Gfx::CEngine::DeleteObject(int objRank)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::SetDrawWorld(int objRank, bool draw)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::SetDrawFront(int objRank, bool draw)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::AddTriangle(int objRank, Gfx::VertexTex2* vertex, int nb,
+ const Gfx::Material& mat, int state,
+ std::string texName1, std::string texName2,
+ float min, float max, bool globalUpdate)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::AddSurface(int objRank, Gfx::VertexTex2* vertex, int nb,
+ const Gfx::Material& mat, int state,
+ std::string texName1, std::string texName2,
+ float min, float max, bool globalUpdate)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::AddQuick(int objRank, const Gfx::EngineObjLevel5& buffer,
+ std::string texName1, std::string texName2,
+ float min, float max, bool globalUpdate)
+{
+ // TODO!
+ return true;
+}
+
+Gfx::EngineObjLevel5* Gfx::CEngine::SearchTriangle(int objRank, const Gfx::Material& mat,
+ int state, std::string texName1,
+ std::string texName2, float min, float max)
+{
+ // TODO!
+ return nullptr;
+}
+
+void Gfx::CEngine::ChangeLOD()
+{
+ // TODO!
+}
+
+bool Gfx::CEngine::ChangeSecondTexture(int objRank, const std::string& texName2)
+{
+ // TODO!
+ return true;
+}
+
+int Gfx::CEngine::GetTotalTriangles(int objRank)
+{
+ // TODO!
+ return 0;
+}
+
+int Gfx::CEngine::GetTriangles(int objRank, float min, float max, Gfx::EngineTriangle* buffer, int size, float percent)
+{
+ // TODO!
+ return 0;
+}
+
+bool Gfx::CEngine::GetBBox(int objRank, Math::Vector& min, Math::Vector& max)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::ChangeTextureMapping(int objRank, const Gfx::Material& mat, int state,
+ const std::string& texName1, const std::string& texName2,
+ float min, float max, Gfx::EngineTextureMapping mode,
+ float au, float bu, float av, float bv)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::TrackTextureMapping(int objRank, const Gfx::Material& mat, int state,
+ const std::string& texName1, const std::string& texName2,
+ float min, float max, Gfx::EngineTextureMapping mode,
+ float pos, float factor, float tl, float ts, float tt)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectTransform(int objRank, const Math::Matrix& transform)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::GetObjectTransform(int objRank, Math::Matrix& transform)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectType(int objRank, Gfx::EngineObjectType type)
+{
+ // TODO!
+ return true;
+}
+
+Gfx::EngineObjectType Gfx::CEngine::GetObjectType(int objRank)
+{
+ // TODO!
+ return Gfx::ENG_OBJTYPE_FIX;
+}
+
+bool Gfx::CEngine::SetObjectTransparency(int objRank, float value)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::ShadowCreate(int objRank)
+{
+ // TODO!
+ return true;
+}
+
+void Gfx::CEngine::ShadowDelete(int objRank)
+{
+ // TODO!
+}
+
+bool Gfx::CEngine::SetObjectShadowHide(int objRank, bool hide)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectShadowType(int objRank, Gfx::EngineShadowType type)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectShadowPos(int objRank, const Math::Vector& pos)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectShadowNormal(int objRank, const Math::Vector& n)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectShadowAngle(int objRank, float angle)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectShadowRadius(int objRank, float radius)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectShadowIntensity(int objRank, float intensity)
+{
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectShadowHeight(int objRank, float h)
+{
+ // TODO!
+ return true;
+}
+
+float Gfx::CEngine::GetObjectShadowRadius(int objRank)
+{
+ // TODO!
+ return 0.0f;
+}
+
+bool Gfx::CEngine::GetHighlight(Math::Point &p1, Math::Point &p2)
+{
+ p1 = m_highlightP1;
+ p2 = m_highlightP2;
+ return m_highlight;
+}
+
+void Gfx::CEngine::SetHighlightRank(int *rankList)
+{
+ int i = 0;
+ while ( *rankList != -1 )
{
- std::stringstream str;
- str << "Couldn't load texture '" << texName << "': " << img.GetError();
- m_error = str.str();
- return Gfx::Texture(); // invalid texture
+ m_highlightRank[i++] = *rankList++;
}
+ m_highlightRank[i] = -1; // terminator
+}
- Gfx::Texture result = m_device->CreateTexture(&img, params);
+bool Gfx::CEngine::GetBBox2D(int objRank, Math::Point &min, Math::Point &max)
+{
+ min.x = 1000000.0f;
+ min.y = 1000000.0f;
+ max.x = -1000000.0f;
+ max.y = -1000000.0f;
- if (! result.valid)
+ for (int i = 0; i < 8; i++)
{
- std::stringstream str;
- str << "Couldn't load texture '" << texName << "': " << m_device->GetError();
- m_error = str.str();
- return result;
+ Math::Vector p;
+
+ if ( i & (1<<0) ) p.x = m_objects[objRank].bboxMin.x;
+ else p.x = m_objects[objRank].bboxMax.x;
+ if ( i & (1<<1) ) p.y = m_objects[objRank].bboxMin.y;
+ else p.y = m_objects[objRank].bboxMax.y;
+ if ( i & (1<<2) ) p.z = m_objects[objRank].bboxMin.z;
+ else p.z = m_objects[objRank].bboxMax.z;
+
+ Math::Vector pp;
+ if (TransformPoint(pp, objRank, p))
+ {
+ if (pp.x < min.x) min.x = pp.x;
+ if (pp.x > max.x) max.x = pp.x;
+ if (pp.y < min.y) min.y = pp.y;
+ if (pp.y > max.y) max.y = pp.y;
+ }
}
- m_texNameMap[texName] = result;
- m_revTexNameMap[result] = texName;
+ if ( min.x == 1000000.0f ||
+ min.y == 1000000.0f ||
+ max.x == -1000000.0f ||
+ max.y == -1000000.0f ) return false;
- return result;
+ return true;
}
-Gfx::Texture Gfx::CEngine::CreateTexture(const std::string &texName)
+void Gfx::CEngine::GroundSpotFlush()
{
- return CreateTexture(texName, m_defaultTexParams);
+ // TODO
}
-void Gfx::CEngine::DestroyTexture(const std::string &texName)
+int Gfx::CEngine::GroundSpotCreate()
{
- std::map<std::string, Gfx::Texture>::iterator it = m_texNameMap.find(texName);
- if (it == m_texNameMap.end())
- return;
+ // TODO!
+ return 0;
+}
- std::map<Gfx::Texture, std::string>::iterator revIt = m_revTexNameMap.find((*it).second);
+void Gfx::CEngine::GroundSpotDelete(int rank)
+{
+ // TODO!
+}
- m_device->DestroyTexture((*it).second);
+bool Gfx::CEngine::SetObjectGroundSpotPos(int rank, const Math::Vector& pos)
+{
+ // TODO!
+ return true;
+}
- m_revTexNameMap.erase(revIt);
- m_texNameMap.erase(it);
+bool Gfx::CEngine::SetObjectGroundSpotRadius(int rank, float radius)
+{
+ // TODO!
+ return true;
}
-void Gfx::CEngine::SetTexture(const std::string &name, int stage)
+bool Gfx::CEngine::SetObjectGroundSpotColor(int rank, const Gfx::Color& color)
{
- std::map<std::string, Gfx::Texture>::iterator it = m_texNameMap.find(name);
- if (it != m_texNameMap.end())
- m_device->SetTexture(stage, (*it).second);
+ // TODO!
+ return true;
+}
- // TODO if not present...
+bool Gfx::CEngine::SetObjectGroundSpotMinMax(int rank, float min, float max)
+{
+ // TODO!
+ return true;
}
-void Gfx::CEngine::SetMaterial(const Gfx::Material &mat)
+bool Gfx::CEngine::SetObjectGroundSpotSmooth(int rank, float smooth)
{
- m_device->SetMaterial(mat);
+ // TODO!
+ return true;
+}
+
+int Gfx::CEngine::GroundMarkCreate(Math::Vector pos, float radius,
+ float delay1, float delay2, float delay3,
+ int dx, int dy, char* table)
+{
+ // TODO!
+ return 0;
+}
+
+bool Gfx::CEngine::GroundMarkDelete(int rank)
+{
+ // TODO!
+ return true;
+}
+
+void Gfx::CEngine::ComputeDistance()
+{
+ // TODO!
+}
+
+void Gfx::CEngine::UpdateGeometry()
+{
+ // TODO!
+}
+
+void Gfx::CEngine::Update()
+{
+ ComputeDistance();
+ UpdateGeometry();
+}
+
+bool Gfx::CEngine::DetectBBox(int objRank, Math::Point mouse)
+{
+ // TODO!
+ return true;
+}
+
+int Gfx::CEngine::DetectObject(Math::Point mouse)
+{
+ // TODO!
+ return 0;
+}
+
+bool Gfx::CEngine::DetectTriangle(Math::Point mouse, Gfx::VertexTex2* triangle, int objRank, float& dist)
+{
+ // TODO!
+ return true;
}
-void Gfx::CEngine::SetState(int state, Gfx::Color color)
+bool Gfx::CEngine::IsVisible(int objRank)
{
- if ( state == m_lastState && color == m_lastColor )
+ // TODO!
+ return true;
+}
+
+bool Gfx::CEngine::TransformPoint(Math::Vector& p2D, int objRank, Math::Vector p3D)
+{
+ p3D = Math::Transform(m_objects[objRank].transform, p3D);
+ p3D = Math::Transform(m_matView, p3D);
+
+ if (p3D.z < 2.0f) return false; // behind?
+
+ p2D.x = (p3D.x/p3D.z)*m_matProj.Get(1,1);
+ p2D.y = (p3D.y/p3D.z)*m_matProj.Get(2,2);
+ p2D.z = p3D.z;
+
+ p2D.x = (p2D.x+1.0f)/2.0f; // [-1..1] -> [0..1]
+ p2D.y = (p2D.y+1.0f)/2.0f;
+
+ return true;
+}
+
+
+
+/*******************************************************
+ Mode setting
+ *******************************************************/
+
+
+
+void Gfx::CEngine::SetState(int state, const Gfx::Color& color)
+{
+ if (state == m_lastState && color == m_lastColor)
return;
m_lastState = state;
m_lastColor = color;
- if ( m_alphaMode != 1 && (state & Gfx::ENG_RSTATE_ALPHA) )
+ if (m_alphaMode != 1 && (state & Gfx::ENG_RSTATE_ALPHA))
{
state &= ~Gfx::ENG_RSTATE_ALPHA;
@@ -351,120 +893,168 @@ void Gfx::CEngine::SetState(int state, Gfx::Color color)
state |= Gfx::ENG_RSTATE_TTEXTURE_BLACK;
}
- // TODO other modes & thorough testing
- if (state & Gfx::ENG_RSTATE_TTEXTURE_BLACK) // The transparent black texture?
+ if (state & Gfx::ENG_RSTATE_TTEXTURE_BLACK) // transparent black texture?
{
m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, false);
- m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, true);
m_device->SetRenderState(Gfx::RENDER_STATE_ALPHA_TEST, false);
- m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+ m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, true);
m_device->SetBlendFunc(Gfx::BLEND_ONE, Gfx::BLEND_INV_SRC_COLOR);
- m_device->SetTextureEnabled(0, true);
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+
m_device->SetTextureFactor(color);
Gfx::TextureStageParams params;
params.colorOperation = Gfx::TEX_MIX_OPER_MODULATE;
params.colorArg1 = Gfx::TEX_MIX_ARG_TEXTURE;
params.colorArg2 = Gfx::TEX_MIX_ARG_FACTOR;
- params.alphaOperation = Gfx::TEX_MIX_OPER_MODULATE;
+ params.alphaOperation = Gfx::TEX_MIX_OPER_DEFAULT; // TODO: replace with src color ?
+
+ m_device->SetTextureEnabled(0, true);
m_device->SetTextureStageParams(0, params);
}
- else if (state & Gfx::ENG_RSTATE_TTEXTURE_WHITE) // The transparent white texture?
+ else if (state & Gfx::ENG_RSTATE_TTEXTURE_WHITE) // transparent white texture?
{
m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, false);
- m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, true);
m_device->SetRenderState(Gfx::RENDER_STATE_ALPHA_TEST, false);
- m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+ m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, true);
m_device->SetBlendFunc(Gfx::BLEND_DST_COLOR, Gfx::BLEND_ZERO);
- m_device->SetTextureEnabled(0, true);
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+
m_device->SetTextureFactor(color.Inverse());
Gfx::TextureStageParams params;
params.colorOperation = Gfx::TEX_MIX_OPER_ADD;
params.colorArg1 = Gfx::TEX_MIX_ARG_TEXTURE;
params.colorArg2 = Gfx::TEX_MIX_ARG_FACTOR;
- params.alphaOperation = Gfx::TEX_MIX_OPER_MODULATE;
+ params.alphaOperation = Gfx::TEX_MIX_OPER_DEFAULT; // TODO: replace with src color ?
+
+ m_device->SetTextureEnabled(0, true);
m_device->SetTextureStageParams(0, params);
}
- else if (state & Gfx::ENG_RSTATE_TCOLOR_BLACK) // The transparent black color?
+ else if (state & Gfx::ENG_RSTATE_TCOLOR_BLACK) // transparent black color?
{
- m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, false);
- m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, true);
- m_device->SetRenderState(Gfx::RENDER_STATE_ALPHA_TEST, false);
- m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+ m_device->SetRenderState(Gfx::RENDER_STATE_ALPHA_TEST, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, true);
m_device->SetBlendFunc(Gfx::BLEND_ONE, Gfx::BLEND_INV_SRC_COLOR);
-
- m_device->SetTextureFactor(color);
- m_device->SetTextureEnabled(0, true);
- m_device->SetTextureStageParams(0, Gfx::TextureStageParams());
}
- else if (state & Gfx::ENG_RSTATE_TCOLOR_WHITE) // The transparent white color?
+ else if (state & Gfx::ENG_RSTATE_TCOLOR_WHITE) // transparent white color?
{
- m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, false);
- m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, true);
- m_device->SetRenderState(Gfx::RENDER_STATE_ALPHA_TEST, false);
- m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+ m_device->SetRenderState(Gfx::RENDER_STATE_ALPHA_TEST, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, true);
m_device->SetBlendFunc(Gfx::BLEND_DST_COLOR, Gfx::BLEND_ZERO);
+ }
+ else if (state & Gfx::ENG_RSTATE_TDIFFUSE) // diffuse color as transparent?
+ {
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_ALPHA_TEST, false);
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, true);
+ m_device->SetBlendFunc(Gfx::BLEND_SRC_ALPHA, Gfx::BLEND_DST_ALPHA);
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+
+ Gfx::TextureStageParams params;
+ params.colorOperation = Gfx::TEX_MIX_OPER_REPLACE;
+ params.colorArg1 = Gfx::TEX_MIX_ARG_TEXTURE;
+ params.alphaOperation = Gfx::TEX_MIX_OPER_DEFAULT; // TODO: replace with src color ?
- m_device->SetTextureFactor(color.Inverse());
m_device->SetTextureEnabled(0, true);
- m_device->SetTextureStageParams(0, Gfx::TextureStageParams());
+ m_device->SetTextureStageParams(0, params);
}
- else if (state & Gfx::ENG_RSTATE_TDIFFUSE) // diffuse color as transparent?
+ else if (state & Gfx::ENG_RSTATE_OPAQUE_TEXTURE) // opaque texture ?
{
- /*m_device->SetRenderState(D3DRENDERSTATE_FOGENABLE, false);
- m_device->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, false);
- m_device->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, true);
- m_device->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, false);
- m_device->SetRenderState(D3DRENDERSTATE_SRCBLEND, m_diffuseSrcBlend[1]);
- m_device->SetRenderState(D3DRENDERSTATE_DESTBLEND, m_diffuseDestBlend[1]);
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_ALPHA_TEST, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, false);
- m_device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
- m_device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
- m_device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);*/
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+ m_device->SetTextureEnabled(0, true);
+ m_device->SetTextureStageParams(0, Gfx::TextureStageParams()); // default operation
}
- else if (state & Gfx::ENG_RSTATE_ALPHA) // image with alpha channel?
+ else if (state & Gfx::ENG_RSTATE_OPAQUE_COLOR) // opaque color ?
{
- /*m_device->SetRenderState(D3DRENDERSTATE_FOGENABLE, true);
- m_device->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, true);
- m_device->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, false);
- m_device->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, true);
- m_device->SetRenderState(D3DRENDERSTATE_ALPHAFUNC, D3DCMP_GREATER);
- m_device->SetRenderState(D3DRENDERSTATE_ALPHAREF, (DWORD)(128));
- m_device->SetRenderState(D3DRENDERSTATE_SRCBLEND, m_alphaSrcBlend[1]);
- m_device->SetRenderState(D3DRENDERSTATE_DESTBLEND, m_alphaSrcBlend[1]);
-
- m_device->SetRenderState(D3DRENDERSTATE_TEXTUREFACTOR, color);
- m_device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
- m_device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
- m_device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
- m_device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
- m_device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);*/
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_ALPHA_TEST, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, false);
}
- else // normal ?
+ else if (state & Gfx::ENG_RSTATE_TEXT) // font rendering?
+ {
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_ALPHA_TEST, false);
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, true);
+ m_device->SetBlendFunc(Gfx::BLEND_SRC_ALPHA, Gfx::BLEND_INV_SRC_ALPHA);
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+
+ Gfx::TextureStageParams params;
+ params.colorOperation = Gfx::TEX_MIX_OPER_DEFAULT; // default modulate operation
+ params.alphaOperation = Gfx::TEX_MIX_OPER_DEFAULT; // default modulate operation
+
+ m_device->SetTextureEnabled(0, true);
+ m_device->SetTextureStageParams(0, params);
+ }
+ else if (state & Gfx::ENG_RSTATE_ALPHA) // image with alpha channel?
{
+ m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, false);
+
m_device->SetRenderState(Gfx::RENDER_STATE_FOG, true);
m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, true);
- m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, false);
- m_device->SetRenderState(Gfx::RENDER_STATE_ALPHA_TEST, false);
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_ALPHA_TEST, true);
+
+ m_device->SetAlphaTestFunc(Gfx::COMP_FUNC_GREATER, 0.5f);
+
m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+ m_device->SetTextureFactor(color);
+
+ Gfx::TextureStageParams params;
+ params.colorOperation = Gfx::TEX_MIX_OPER_MODULATE;
+ params.colorArg1 = Gfx::TEX_MIX_ARG_TEXTURE;
+ params.colorArg2 = Gfx::TEX_MIX_ARG_SRC_COLOR;
+ params.alphaOperation = Gfx::TEX_MIX_OPER_REPLACE;
+ params.alphaArg1 = Gfx::TEX_MIX_ARG_TEXTURE;
+
m_device->SetTextureEnabled(0, true);
- m_device->SetTextureStageParams(0, Gfx::TextureStageParams());
+ m_device->SetTextureStageParams(0, params);
+ }
+ else // normal ?
+ {
+ m_device->SetRenderState(Gfx::RENDER_STATE_ALPHA_TEST, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, false);
- /*m_device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
- m_device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
- m_device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
- m_device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);*/
+ m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, true);
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, true);
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+
+ Gfx::TextureStageParams params;
+ params.colorOperation = Gfx::TEX_MIX_OPER_DEFAULT; // default modulate
+ params.alphaOperation = Gfx::TEX_MIX_OPER_DEFAULT; // TODO: replace with src color ?
+
+ m_device->SetTextureEnabled(0, true);
+ m_device->SetTextureStageParams(0, params);
}
if (state & Gfx::ENG_RSTATE_FOG)
@@ -476,21 +1066,25 @@ void Gfx::CEngine::SetState(int state, Gfx::Color color)
if ( !m_groundSpotVisible && (state & Gfx::ENG_RSTATE_SECOND) != 0 ) second = false;
if ( !m_dirty && (state & Gfx::ENG_RSTATE_SECOND) == 0 ) second = false;
- if ( (state & ENG_RSTATE_DUAL_BLACK) && second )
+ if ((state & ENG_RSTATE_DUAL_BLACK) && second)
{
- /*m_device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
- m_device->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
- m_device->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
- m_device->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
- m_device->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1);*/
+ Gfx::TextureStageParams params;
+ params.colorOperation = Gfx::TEX_MIX_OPER_MODULATE;
+ params.colorArg1 = Gfx::TEX_MIX_ARG_TEXTURE;
+ params.colorArg2 = Gfx::TEX_MIX_ARG_COMPUTED_COLOR;
+ params.alphaOperation = Gfx::TEX_MIX_OPER_DEFAULT; // TODO: ???
+ m_device->SetTextureEnabled(0, true);
+ m_device->SetTextureStageParams(1, params);
}
- else if ( (state & ENG_RSTATE_DUAL_WHITE) && second )
+ else if ((state & ENG_RSTATE_DUAL_WHITE) && second)
{
- /*m_device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD);
- m_device->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
- m_device->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
- m_device->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
- m_device->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1);*/
+ Gfx::TextureStageParams params;
+ params.colorOperation = Gfx::TEX_MIX_OPER_ADD;
+ params.colorArg1 = Gfx::TEX_MIX_ARG_TEXTURE;
+ params.colorArg2 = Gfx::TEX_MIX_ARG_COMPUTED_COLOR;
+ params.alphaOperation = Gfx::TEX_MIX_OPER_DEFAULT; // TODO: ???
+ m_device->SetTextureEnabled(0, true);
+ m_device->SetTextureStageParams(1, params);
}
else
{
@@ -499,18 +1093,25 @@ void Gfx::CEngine::SetState(int state, Gfx::Color color)
if (state & Gfx::ENG_RSTATE_WRAP)
{
- /*m_device->SetTextureStageState(0, D3DTSS_ADDRESS, D3DTADDRESS_WRAP);
- m_device->SetTextureStageState(1, D3DTSS_ADDRESS, D3DTADDRESS_WRAP);*/
- }
- else if (state & Gfx::ENG_RSTATE_CLAMP)
- {
- /*m_device->SetTextureStageState(0, D3DTSS_ADDRESS, D3DTADDRESS_CLAMP);
- m_device->SetTextureStageState(1, D3DTSS_ADDRESS, D3DTADDRESS_CLAMP);*/
+ // TODO: separate function for setting wrap mode?
+
+ Gfx::TextureStageParams p1 = m_device->GetTextureStageParams(0);
+ p1.wrapS = p1.wrapT = Gfx::TEX_WRAP_REPEAT;
+ m_device->SetTextureStageParams(0, p1);
+
+ Gfx::TextureStageParams p2 = m_device->GetTextureStageParams(1);
+ p2.wrapS = p2.wrapT = Gfx::TEX_WRAP_REPEAT;
+ m_device->SetTextureStageParams(1, p2);
}
- else
+ else // if (state & Gfx::ENG_RSTATE_CLAMP) or otherwise
{
- /*m_device->SetTextureStageState(0, D3DTSS_ADDRESS, D3DTADDRESS_CLAMP);
- m_device->SetTextureStageState(1, D3DTSS_ADDRESS, D3DTADDRESS_CLAMP);*/
+ Gfx::TextureStageParams p1 = m_device->GetTextureStageParams(0);
+ p1.wrapS = p1.wrapT = Gfx::TEX_WRAP_CLAMP;
+ m_device->SetTextureStageParams(0, p1);
+
+ Gfx::TextureStageParams p2 = m_device->GetTextureStageParams(1);
+ p2.wrapS = p2.wrapT = Gfx::TEX_WRAP_CLAMP;
+ m_device->SetTextureStageParams(1, p2);
}
if (state & Gfx::ENG_RSTATE_2FACE)
@@ -529,90 +1130,1657 @@ void Gfx::CEngine::SetState(int state, Gfx::Color color)
m_device->SetGlobalAmbient(m_ambientColor[m_rankView]);
}
-bool Gfx::CEngine::ProcessEvent(const Event &event)
+void Gfx::CEngine::SetMaterial(const Gfx::Material &mat)
{
- if (event.type == EVENT_MOUSE_MOVE)
+ m_lastMaterial = mat;
+ m_device->SetMaterial(mat);
+}
+
+void Gfx::CEngine::SetViewParams(const Math::Vector& eyePt, const Math::Vector& lookatPt,
+ const Math::Vector& upVec, float eyeDistance)
+{
+ m_eyePt = eyePt;
+ m_lookatPt = lookatPt;
+ m_eyeDirH = Math::RotateAngle(eyePt.x - lookatPt.x, eyePt.z - lookatPt.z);
+ m_eyeDirV = Math::RotateAngle(Math::DistanceProjected(eyePt, lookatPt), eyePt.y - lookatPt.y);
+
+ Math::LoadViewMatrix(m_matView, eyePt, lookatPt, upVec);
+
+ if (m_sound == nullptr)
+ m_sound = static_cast<CSoundInterface*>( m_iMan->SearchInstance(CLASS_SOUND) );
+
+ m_sound->SetListener(eyePt, lookatPt);
+}
+
+Gfx::Texture Gfx::CEngine::CreateTexture(const std::string &texName, const Gfx::TextureCreateParams &params)
+{
+ if (m_texBlacklist.find(texName) != m_texBlacklist.end())
+ return Gfx::Texture(); // invalid texture
+
+ // TODO: detect alpha channel?
+
+ CImage img;
+ if (! img.Load(m_app->GetDataFilePath(m_texPath, texName)))
{
- m_mousePos = event.mouseMove.pos;
+ std::string error = img.GetError();
+ GetLogger()->Error("Couldn't load texture '%s': %s\n", texName.c_str(), error.c_str());
+ GetLogger()->Error("Blacklisting texture '%s'\n", texName.c_str());
+ m_texBlacklist.insert(texName);
+ return Gfx::Texture(); // invalid texture
}
- else if (event.type == EVENT_KEY_DOWN)
+
+ Gfx::Texture result = m_device->CreateTexture(&img, params);
+
+ if (! result.valid)
{
- // !! Debug, to be removed later !!
+ std::string error = m_device->GetError();
+ GetLogger()->Error("Couldn't load texture '%s': %s\n", texName.c_str(), error.c_str());
+ GetLogger()->Error("Blacklisting texture '%s'\n", texName.c_str());
+ m_texBlacklist.insert(texName);
+ return result;
+ }
- if (event.key.key == KEY(F1))
+ m_texNameMap[texName] = result;
+ m_revTexNameMap[result] = texName;
+
+ return result;
+}
+
+Gfx::Texture Gfx::CEngine::CreateTexture(const std::string &texName)
+{
+ return CreateTexture(texName, m_defaultTexParams);
+}
+
+void Gfx::CEngine::DestroyTexture(const std::string &texName)
+{
+ std::map<std::string, Gfx::Texture>::iterator it = m_texNameMap.find(texName);
+ if (it == m_texNameMap.end())
+ return;
+
+ std::map<Gfx::Texture, std::string>::iterator revIt = m_revTexNameMap.find((*it).second);
+
+ m_device->DestroyTexture((*it).second);
+
+ m_revTexNameMap.erase(revIt);
+ m_texNameMap.erase(it);
+}
+
+bool Gfx::CEngine::LoadTexture(const std::string& name)
+{
+ if (m_texBlacklist.find(name) != m_texBlacklist.end())
+ return false;
+
+ std::map<std::string, Gfx::Texture>::iterator it = m_texNameMap.find(name);
+ if (it != m_texNameMap.end())
+ return true;
+
+ Gfx::Texture tex = CreateTexture(name);
+ return tex.valid;
+}
+
+// TODO: create separate variables for 4 quarter names
+void QuarterName(std::string& buffer, const std::string& name, int quarter)
+{
+ size_t pos = name.find('.');
+ if (pos == std::string::npos)
+ {
+ buffer = name;
+ return;
+ }
+
+ buffer = name.substr(0, pos) + std::string(1, static_cast<char>('a' + quarter)) + name.substr(pos);
+}
+
+bool Gfx::CEngine::LoadAllTextures()
+{
+ LoadTexture("text.png");
+ LoadTexture("mouse.png");
+ LoadTexture("button1.png");
+ LoadTexture("button2.png");
+ LoadTexture("button3.png");
+ LoadTexture("effect00.png");
+ LoadTexture("effect01.png");
+ LoadTexture("effect02.png");
+ LoadTexture("map.png");
+
+ if (! m_backgroundName.empty())
+ {
+ if (m_backgroundQuarter) // image into 4 pieces?
{
- m_mouseVisible = !m_mouseVisible;
- m_app->SetSystemMouseVisible(! m_app->GetSystemMouseVisibile());
+ for (int i = 0; i < 4; i++)
+ {
+ std::string name;
+ QuarterName(name, m_backgroundName, i);
+ LoadTexture(name);
+ }
}
- else if (event.key.key == KEY(F2))
+ else
{
- int index = static_cast<int>(m_mouseType);
- m_mouseType = static_cast<Gfx::EngineMouseType>( (index + 1) % Gfx::ENG_MOUSE_COUNT );
+ LoadTexture(m_backgroundName);
}
}
- // By default, pass on all events
- return true;
+ if (! m_foregroundName.empty())
+ LoadTexture(m_foregroundName);
+
+ m_planet->LoadTexture();
+
+ bool ok = true;
+
+ /* TODO
+ D3DObjLevel1* p1;
+ D3DObjLevel2* p2;
+ int l1;
+ p1 = m_objectPointer;
+ for ( l1=0 ; l1<p1->totalUsed ; l1++ )
+ {
+ p2 = p1->table[l1];
+
+ if ( p2 == 0 || p2->texName1[0] != 0 )
+ {
+ if ( !LoadTexture(p2->texName1) ) ok = false;
+ }
+
+ if ( p2 == 0 || p2->texName2[0] != 0 )
+ {
+ if ( !LoadTexture(p2->texName2) ) ok = false;
+ }
+ }*/
+
+ return ok;
}
-bool Gfx::CEngine::Render()
+bool Gfx::CEngine::SetTexture(const std::string& name, int stage)
{
- m_statisticTriangle = 0;
+ auto it = m_texNameMap.find(name);
+ if (it != m_texNameMap.end())
+ {
+ m_device->SetTexture(stage, (*it).second);
+ return true;
+ }
+
+ if (! LoadTexture(name))
+ {
+ m_device->SetTexture(stage, 0); // invalid texture
+ return false;
+ }
+
+ it = m_texNameMap.find(name);
+ if (it != m_texNameMap.end())
+ {
+ m_device->SetTexture(stage, (*it).second);
+ return true;
+ }
+
+ m_device->SetTexture(stage, 0); // invalid texture
+ return false; // should not happen normally
+}
+
+void Gfx::CEngine::SetLimitLOD(int rank, float limit)
+{
+ m_limitLOD[rank] = limit;
+}
+float Gfx::CEngine::GetLimitLOD(int rank, bool last)
+{
+ float limit = 0.0f;
+
+ if (last)
+ {
+ limit = m_limitLOD[rank];
+ limit *= m_lastSize.x/640.0f; // limit further if large window!
+ limit += m_limitLOD[0]*(m_lastObjectDetail*2.0f);
+ }
+ else
+ {
+ limit = m_limitLOD[rank];
+ limit *= m_size.x/640.0f; // limit further if large window!
+ limit += m_limitLOD[0]*(m_objectDetail*2.0f);
+ }
+
+ if (limit < 0.0f) limit = 0.0f;
+
+ return limit;
+}
+
+void Gfx::CEngine::SetTerrainVision(float vision)
+{
+ m_terrainVision = vision;
+}
+
+void Gfx::CEngine::SetFocus(float focus)
+{
+ m_focus = focus;
+ m_size = m_app->GetVideoConfig().size;
+
+ float aspect = (static_cast<float>(m_size.y)) / m_size.x;
+ Math::LoadProjectionMatrix(m_matProj, m_focus, aspect, 0.5f, m_deepView[0]);
+}
+
+float Gfx::CEngine::GetFocus()
+{
+ return m_focus;
+}
+
+void Gfx::CEngine::SetGroundSpot(bool mode)
+{
+ m_groundSpotVisible = mode;
+}
+
+bool Gfx::CEngine::GetGroundSpot()
+{
+ return m_groundSpotVisible;
+}
+
+void Gfx::CEngine::SetShadow(bool mode)
+{
+ m_shadowVisible = mode;
+}
+
+bool Gfx::CEngine::GetShadow()
+{
+ return m_shadowVisible;
+}
+
+void Gfx::CEngine::SetDirty(bool mode)
+{
+ m_dirty = mode;
+}
+
+bool Gfx::CEngine::GetDirty()
+{
+ return m_dirty;
+}
+
+void Gfx::CEngine::SetFog(bool mode)
+{
+ m_fog = mode;
+}
+
+bool Gfx::CEngine::GetFog()
+{
+ return m_fog;
+}
+
+bool Gfx::CEngine::GetStateColor()
+{
+ return m_stateColor;
+}
+
+void Gfx::CEngine::SetSecondTexture(int texNum)
+{
+ m_secondTexNum = texNum;
+}
+
+int Gfx::CEngine::GetSecondTexture()
+{
+ return m_secondTexNum;
+}
+
+void Gfx::CEngine::SetRankView(int rank)
+{
+ if (rank < 0) rank = 0;
+ if (rank > 1) rank = 1;
+
+ if (m_rankView == 0 && rank == 1) // enters the water?
+ m_lightMan->AdaptLightColor(m_waterAddColor, +1.0f);
+
+ if (m_rankView == 1 && rank == 0) // out of the water?
+ m_lightMan->AdaptLightColor(m_waterAddColor, -1.0f);
+
+ m_rankView = rank;
+}
+
+int Gfx::CEngine::GetRankView()
+{
+ return m_rankView;
+}
+
+void Gfx::CEngine::SetDrawWorld(bool draw)
+{
+ m_drawWorld = draw;
+}
+
+void Gfx::CEngine::SetDrawFront(bool draw)
+{
+ m_drawFront = draw;
+}
+
+void Gfx::CEngine::SetAmbientColor(const Gfx::Color& color, int rank)
+{
+ m_ambientColor[rank] = color;
+}
+
+Gfx::Color Gfx::CEngine::GetAmbientColor(int rank)
+{
+ return m_ambientColor[rank];
+}
+
+void Gfx::CEngine::SetWaterAddColor(const Gfx::Color& color)
+{
+ m_waterAddColor = color;
+}
+
+Gfx::Color Gfx::CEngine::GetWaterAddColor()
+{
+ return m_waterAddColor;
+}
+
+void Gfx::CEngine::SetFogColor(const Gfx::Color& color, int rank)
+{
+ m_fogColor[rank] = color;
+}
+
+Gfx::Color Gfx::CEngine::GetFogColor(int rank)
+{
+ return m_fogColor[rank];
+}
+
+void Gfx::CEngine::SetDeepView(float length, int rank, bool ref)
+{
+ if (ref)
+ length *= m_clippingDistance;
+
+ m_deepView[rank] = length;
+}
+
+float Gfx::CEngine::GetDeepView(int rank)
+{
+ return m_deepView[rank];
+}
+
+void Gfx::CEngine::SetFogStart(float start, int rank)
+{
+ m_fogStart[rank] = start;
+}
+
+float Gfx::CEngine::GetFogStart(int rank)
+{
+ return m_fogStart[rank];
+}
+
+void Gfx::CEngine::SetBackground(const std::string& name, Gfx::Color up, Gfx::Color down,
+ Gfx::Color cloudUp, Gfx::Color cloudDown,
+ bool full, bool quarter)
+{
+ m_backgroundName = name;
+ m_backgroundColorUp = up;
+ m_backgroundColorDown = down;
+ m_backgroundCloudUp = cloudUp;
+ m_backgroundCloudDown = cloudDown;
+ m_backgroundFull = full;
+ m_backgroundQuarter = quarter;
+}
+
+void Gfx::CEngine::GetBackground(std::string& name, Gfx::Color& up, Gfx::Color& down,
+ Gfx::Color& cloudUp, Gfx::Color& cloudDown,
+ bool &full, bool &quarter)
+{
+ name = m_backgroundName;
+ up = m_backgroundColorUp;
+ down = m_backgroundColorDown;
+ cloudUp = m_backgroundCloudUp;
+ cloudDown = m_backgroundCloudDown;
+ full = m_backgroundFull;
+ quarter = m_backgroundQuarter;
+}
+
+void Gfx::CEngine::SetForegroundName(const std::string& name)
+{
+ if (! m_foregroundName.empty())
+ DestroyTexture(m_foregroundName);
+
+ m_foregroundName = name;
+}
+
+void Gfx::CEngine::SetOverFront(bool front)
+{
+ m_overFront = front;
+}
+
+void Gfx::CEngine::SetOverColor(const Gfx::Color& color, int mode)
+{
+ m_overColor = color;
+ m_overMode = mode;
+}
+
+void Gfx::CEngine::SetParticleDensity(float value)
+{
+ if (value < 0.0f) value = 0.0f;
+ if (value > 2.0f) value = 2.0f;
+ m_particleDensity = value;
+}
+
+float Gfx::CEngine::GetParticleDensity()
+{
+ return m_particleDensity;
+}
+
+float Gfx::CEngine::ParticleAdapt(float factor)
+{
+ if (m_particleDensity == 0.0f)
+ return 1000000.0f;
+
+ return factor / m_particleDensity;
+}
+
+void Gfx::CEngine::SetClippingDistance(float value)
+{
+ if (value < 0.5f) value = 0.5f;
+ if (value > 2.0f) value = 2.0f;
+ m_clippingDistance = value;
+}
+
+float Gfx::CEngine::GetClippingDistance()
+{
+ return m_clippingDistance;
+}
+
+void Gfx::CEngine::SetObjectDetail(float value)
+{
+ if ( value < 0.0f ) value = 0.0f;
+ if ( value > 2.0f ) value = 2.0f;
+ m_objectDetail = value;
+}
+
+float Gfx::CEngine::GetObjectDetail()
+{
+ return m_objectDetail;
+}
+
+void Gfx::CEngine::SetGadgetQuantity(float value)
+{
+ if (value < 0.0f) value = 0.0f;
+ if (value > 1.0f) value = 1.0f;
+
+ m_gadgetQuantity = value;
+}
+
+float Gfx::CEngine::GetGadgetQuantity()
+{
+ return m_gadgetQuantity;
+}
+
+void Gfx::CEngine::SetTextureQuality(int value)
+{
+ if (value < 0) value = 0;
+ if (value > 2) value = 2;
+
+ if (value != m_textureQuality)
+ {
+ m_textureQuality = value;
+ LoadAllTextures();
+ }
+}
+
+int Gfx::CEngine::GetTextureQuality()
+{
+ return m_textureQuality;
+}
+
+void Gfx::CEngine::SetTotoMode(bool present)
+{
+ m_totoMode = present;
+}
+
+bool Gfx::CEngine::GetTotoMode()
+{
+ return m_totoMode;
+}
+
+void Gfx::CEngine::SetLensMode(bool present)
+{
+ m_lensMode = present;
+}
+
+bool Gfx::CEngine::GetLensMode()
+{
+ return m_lensMode;
+}
+
+void Gfx::CEngine::SetWaterMode(bool present)
+{
+ m_waterMode = present;
+}
+
+bool Gfx::CEngine::GetWaterMode()
+{
+ return m_waterMode;
+}
+
+void Gfx::CEngine::SetLightingMode(bool present)
+{
+ m_lightMode = present;
+}
+
+bool Gfx::CEngine::GetLightingMode()
+{
+ return m_lightMode;
+}
+
+void Gfx::CEngine::SetSkyMode(bool present)
+{
+ m_skyMode = present;
+}
+
+bool Gfx::CEngine::GetSkyMode()
+{
+ return m_skyMode;
+}
+
+void Gfx::CEngine::SetBackForce(bool present)
+{
+ m_backForce = present;
+}
+
+bool Gfx::CEngine::GetBackForce()
+{
+ return m_backForce;
+}
+
+void Gfx::CEngine::SetPlanetMode(bool present)
+{
+ m_planetMode = present;
+}
+
+bool Gfx::CEngine::GetPlanetMode()
+{
+ return m_planetMode;
+}
+
+void Gfx::CEngine::SetLightMode(bool present)
+{
+ m_lightMode = present;
+}
+
+bool Gfx::CEngine::GetLightMode()
+{
+ return m_lightMode;
+}
+
+void Gfx::CEngine::SetEditIndentMode(bool autoIndent)
+{
+ m_editIndentMode = autoIndent;
+}
+
+bool Gfx::CEngine::GetEditIndentMode()
+{
+ return m_editIndentMode;
+}
+
+void Gfx::CEngine::SetEditIndentValue(int value)
+{
+ m_editIndentValue = value;
+}
+
+int Gfx::CEngine::GetEditIndentValue()
+{
+ return m_editIndentValue;
+}
+
+void Gfx::CEngine::SetSpeed(float speed)
+{
+ m_speed = speed;
+}
+
+float Gfx::CEngine::GetSpeed()
+{
+ return m_speed;
+}
+
+void Gfx::CEngine::SetTracePrecision(float factor)
+{
+ m_tracePrecision = factor;
+}
+
+float Gfx::CEngine::GetTracePrecision()
+{
+ return m_tracePrecision;
+}
+
+void Gfx::CEngine::SetMouseVisible(bool visible)
+{
+ m_mouseVisible = visible;
+}
+
+bool Gfx::CEngine::GetMouseVisible()
+{
+ return m_mouseVisible;
+}
+
+void Gfx::CEngine::SetMousePos(Math::Point pos)
+{
+ m_mousePos = pos;
+}
+
+Math::Point Gfx::CEngine::GetMousePos()
+{
+ return m_mousePos;
+}
+
+void Gfx::CEngine::SetMouseType(Gfx::EngineMouseType type)
+{
+ m_mouseType = type;
+}
+
+Gfx::EngineMouseType Gfx::CEngine::GetMouseType()
+{
+ return m_mouseType;
+}
+
+const Math::Matrix& Gfx::CEngine::GetMatView()
+{
+ return m_matView;
+}
+
+Math::Vector Gfx::CEngine::GetEyePt()
+{
+ return m_eyePt;
+}
+
+Math::Vector Gfx::CEngine::GetLookatPt()
+{
+ return m_lookatPt;
+}
+
+float Gfx::CEngine::GetEyeDirH()
+{
+ return m_eyeDirH;
+}
+
+float Gfx::CEngine::GetEyeDirV()
+{
+ return m_eyeDirV;
+}
+
+bool Gfx::CEngine::IsVisiblePoint(const Math::Vector &pos)
+{
+ return Math::Distance(m_eyePt, pos) <= m_deepView[0];
+}
+
+void Gfx::CEngine::UpdateMatProj()
+{
+ m_device->SetTransform(Gfx::TRANSFORM_PROJECTION, m_matProj);
+}
+
+void Gfx::CEngine::ApplyChange()
+{
+ m_deepView[0] /= m_lastClippingDistance;
+ m_deepView[1] /= m_lastClippingDistance;
+
+ SetFocus(m_focus);
+ ChangeLOD();
+
+ m_deepView[0] *= m_clippingDistance;
+ m_deepView[1] *= m_clippingDistance;
+}
+
+
+
+/*******************************************************
+ Rendering
+ *******************************************************/
+
+
+
+/**
+ This function sets up render states, clears the
+ viewport, and renders the scene. */
+void Gfx::CEngine::Render()
+{
+ if (! m_render) return;
+
+ m_statisticTriangle = 0;
m_lastState = -1;
- SetState(Gfx::ENG_RSTATE_NORMAL);
+ m_lastColor = 999;
+ m_lastMaterial = Gfx::Material();
- m_device->BeginScene();
+ m_lightMan->UpdateLights();
- SetUp3DView();
+ Gfx::Color color;
+ if (m_skyMode && m_cloud->GetLevel() != 0.0f) // clouds?
+ color = m_backgroundCloudDown;
+ else
+ color = m_backgroundColorDown;
- if (! Draw3DScene() )
- return false;
+ m_device->SetClearColor(color);
+
+ // Begin the scene
+ m_device->BeginScene();
- SetUpInterfaceView();
+ if (m_drawWorld)
+ Draw3DScene();
- if (! DrawInterface() )
- return false;
+ DrawInterface();
+ // End the scene
m_device->EndScene();
+}
- return true;
+void Gfx::CEngine::Draw3DScene()
+{
+ /* TODO!
+ D3DObjLevel1* p1;
+ D3DObjLevel2* p2;
+ D3DObjLevel3* p3;
+ D3DObjLevel4* p4;
+ D3DObjLevel5* p5;
+ D3DVERTEX2* pv;
+ int l1, l2, l3, l4, l5, objRank;*/
+
+ if (m_groundSpotVisible)
+ UpdateGroundSpotTextures();
+
+ DrawBackground(); // draws the background
+ if (m_planetMode) DrawPlanet(); // draws the planets
+ if (m_skyMode) m_cloud->Draw(); // draws the clouds
+
+
+ // Display the objects
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_TEST, true);
+ m_device->SetRenderState(Gfx::RENDER_STATE_LIGHTING, true);
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, true);
+
+ float fogStart = m_deepView[m_rankView]*m_fogStart[m_rankView];
+ float fogEnd = m_deepView[m_rankView];
+ m_device->SetFogParams(Gfx::FOG_LINEAR, m_fogColor[m_rankView], fogStart, fogEnd, 1.0f);
+
+ m_device->SetTransform(Gfx::TRANSFORM_PROJECTION, m_matProj);
+ m_device->SetTransform(Gfx::TRANSFORM_VIEW, m_matView);
+
+ if (m_waterMode) m_water->DrawBack(); // draws water background
+
+ if (m_shadowVisible)
+ {
+ // Draw the field
+ // TODO!
+ /*
+ p1 = m_objectPointer;
+ for ( l1=0 ; l1<p1->totalUsed ; l1++ )
+ {
+ p2 = p1->table[l1];
+ if ( p2 == 0 ) continue;
+ SetTexture(p2->texName1, 0);
+ SetTexture(p2->texName2, 1);
+ for ( l2=0 ; l2<p2->totalUsed ; l2++ )
+ {
+ p3 = p2->table[l2];
+ if ( p3 == 0 ) continue;
+ objRank = p3->objRank;
+ if ( m_objectParam[objRank].type != TYPETERRAIN ) continue;
+ if ( !m_objectParam[objRank].bDrawWorld ) continue;
+
+ {
+ D3DMATRIX mat = MAT_TO_D3DMAT(m_objectParam[objRank].transform);
+ m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &mat);
+ }
+
+ if ( !IsVisible(objRank) ) continue;
+ m_light->LightUpdate(m_objectParam[objRank].type);
+ for ( l3=0 ; l3<p3->totalUsed ; l3++ )
+ {
+ p4 = p3->table[l3];
+ if ( p4 == 0 ) continue;
+ if ( m_objectParam[objRank].distance < p4->min ||
+ m_objectParam[objRank].distance >= p4->max ) continue;
+ for ( l4=0 ; l4<p4->totalUsed ; l4++ )
+ {
+ p5 = p4->table[l4];
+ if ( p5 == 0 ) continue;
+ for ( l5=0 ; l5<p5->totalUsed ; l5++ )
+ {
+ p6 = p5->table[l5];
+ if ( p6 == 0 ) continue;
+ SetMaterial(p6->material);
+ SetState(p6->state);
+ if ( p6->type == D3DTYPE6T )
+ {
+ pv = &p6->vertex[0];
+ m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST,
+ D3DFVF_VERTEX2,
+ pv, p6->totalUsed,
+ NULL);
+ m_statisticTriangle += p6->totalUsed/3;
+ }
+ if ( p6->type == D3DTYPE6S )
+ {
+ pv = &p6->vertex[0];
+ m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP,
+ D3DFVF_VERTEX2,
+ pv, p6->totalUsed,
+ NULL);
+ m_statisticTriangle += p6->totalUsed-2;
+ }
+ }
+ }
+ }
+ }
+ }*/
+
+ // Draws the shadows
+ DrawShadow();
+ }
+
+ // Draw objects
+ bool transparent = false;
+ /* TODO!
+ p1 = m_objectPointer;
+ for ( l1=0 ; l1<p1->totalUsed ; l1++ )
+ {
+ p2 = p1->table[l1];
+ if ( p2 == 0 ) continue;
+ SetTexture(p2->texName1, 0);
+ SetTexture(p2->texName2, 1);
+ for ( l2=0 ; l2<p2->totalUsed ; l2++ )
+ {
+ p3 = p2->table[l2];
+ if ( p3 == 0 ) continue;
+ objRank = p3->objRank;
+ if ( m_bShadow && m_objectParam[objRank].type == TYPETERRAIN ) continue;
+ if ( !m_objectParam[objRank].bDrawWorld ) continue;
+
+ {
+ D3DMATRIX mat = MAT_TO_D3DMAT(m_objectParam[objRank].transform);
+ m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &mat);
+ }
+
+ if ( !IsVisible(objRank) ) continue;
+ m_light->LightUpdate(m_objectParam[objRank].type);
+ for ( l3=0 ; l3<p3->totalUsed ; l3++ )
+ {
+ p4 = p3->table[l3];
+ if ( p4 == 0 ) continue;
+ if ( m_objectParam[objRank].distance < p4->min ||
+ m_objectParam[objRank].distance >= p4->max ) continue;
+ for ( l4=0 ; l4<p4->totalUsed ; l4++ )
+ {
+ p5 = p4->table[l4];
+ if ( p5 == 0 ) continue;
+ for ( l5=0 ; l5<p5->totalUsed ; l5++ )
+ {
+ p6 = p5->table[l5];
+ if ( p6 == 0 ) continue;
+ SetMaterial(p6->material);
+ if ( m_objectParam[objRank].transparency != 0.0f ) // transparent ?
+ {
+ transparent = true;
+ continue;
+ }
+ SetState(p6->state);
+ if ( p6->type == D3DTYPE6T )
+ {
+ pv = &p6->vertex[0];
+ m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST,
+ D3DFVF_VERTEX2,
+ pv, p6->totalUsed,
+ NULL);
+ m_statisticTriangle += p6->totalUsed/3;
+ }
+ if ( p6->type == D3DTYPE6S )
+ {
+ pv = &p6->vertex[0];
+ m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP,
+ D3DFVF_VERTEX2,
+ pv, p6->totalUsed,
+ NULL);
+ m_statisticTriangle += p6->totalUsed-2;
+ }
+ }
+ }
+ }
+ }
+ }*/
+
+ if (transparent)
+ {
+ int tState = 0;
+ Gfx::Color tColor;
+ if (m_stateColor)
+ {
+ tState = Gfx::ENG_RSTATE_TTEXTURE_BLACK | Gfx::ENG_RSTATE_2FACE;
+ tColor = Gfx::Color(68.0f / 255.0f, 68.0f / 255.0f, 68.0f / 255.0f, 68.0f / 255.0f);
+ }
+ else
+ {
+ tState = Gfx::ENG_RSTATE_TCOLOR_BLACK;
+ tColor = Gfx::Color(136.0f / 255.0f, 136.0f / 255.0f, 136.0f / 255.0f, 136.0f / 255.0f);
+ }
+
+ // Draw transparent objects.
+ /* TODO!
+ p1 = m_objectPointer;
+ for ( l1=0 ; l1<p1->totalUsed ; l1++ )
+ {
+ p2 = p1->table[l1];
+ if ( p2 == 0 ) continue;
+ SetTexture(p2->texName1, 0);
+ SetTexture(p2->texName2, 1);
+ for ( l2=0 ; l2<p2->totalUsed ; l2++ )
+ {
+ p3 = p2->table[l2];
+ if ( p3 == 0 ) continue;
+ objRank = p3->objRank;
+ if ( m_bShadow && m_objectParam[objRank].type == TYPETERRAIN ) continue;
+ if ( !m_objectParam[objRank].bDrawWorld ) continue;
+
+ {
+ D3DMATRIX mat = MAT_TO_D3DMAT(m_objectParam[objRank].transform);
+ m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &mat);
+ }
+
+ if ( !IsVisible(objRank) ) continue;
+ m_light->LightUpdate(m_objectParam[objRank].type);
+ for ( l3=0 ; l3<p3->totalUsed ; l3++ )
+ {
+ p4 = p3->table[l3];
+ if ( p4 == 0 ) continue;
+ if ( m_objectParam[objRank].distance < p4->min ||
+ m_objectParam[objRank].distance >= p4->max ) continue;
+ for ( l4=0 ; l4<p4->totalUsed ; l4++ )
+ {
+ p5 = p4->table[l4];
+ if ( p5 == 0 ) continue;
+ for ( l5=0 ; l5<p5->totalUsed ; l5++ )
+ {
+ p6 = p5->table[l5];
+ if ( p6 == 0 ) continue;
+ SetMaterial(p6->material);
+ if ( m_objectParam[objRank].transparency == 0.0f ) continue;
+ SetState(tState, tColor);
+ if ( p6->type == D3DTYPE6T )
+ {
+ pv = &p6->vertex[0];
+ m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST,
+ D3DFVF_VERTEX2,
+ pv, p6->totalUsed,
+ NULL);
+ m_statisticTriangle += p6->totalUsed/3;
+ }
+ if ( p6->type == D3DTYPE6S )
+ {
+ pv = &p6->vertex[0];
+ m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP,
+ D3DFVF_VERTEX2,
+ pv, p6->totalUsed,
+ NULL);
+ m_statisticTriangle += p6->totalUsed-2;
+ }
+ }
+ }
+ }
+ }
+ } */
+ }
+
+ m_lightMan->UpdateLightsEnableState(Gfx::ENG_OBJTYPE_TERRAIN);
+
+ if (m_waterMode) m_water->DrawSurf(); // draws water surface
+
+ m_particle->DrawParticle(Gfx::SH_WORLD); // draws the particles of the 3D world
+ m_lightning->Draw(); // draws lightning
+ if (m_lensMode) DrawForegroundImage(); // draws the foreground
+ if (! m_overFront) DrawOverColor(); // draws the foreground color
}
-void Gfx::CEngine::SetUp3DView()
+void Gfx::CEngine::DrawInterface()
{
- // TODO
+ m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_TEST, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_LIGHTING, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
+
+ m_device->SetTransform(Gfx::TRANSFORM_VIEW, m_matViewInterface);
+ m_device->SetTransform(Gfx::TRANSFORM_PROJECTION, m_matProjInterface);
+ m_device->SetTransform(Gfx::TRANSFORM_WORLD, m_matWorldInterface);
+
+ // Draw the entire interface
+ CInterface* interface = static_cast<CInterface*>( m_iMan->SearchInstance(CLASS_INTERFACE) );
+ if (interface != nullptr)
+ interface->Draw();
+
+ m_particle->DrawParticle(Gfx::SH_INTERFACE); // draws the particles of the interface
+
+ // 3D objects drawn in front of interface
+ if (m_drawFront)
+ {
+ // Display the objects
+ m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_TEST, true);
+
+ m_device->SetTransform(Gfx::TRANSFORM_PROJECTION, m_matProj);
+
+ m_device->SetGlobalAmbient(m_ambientColor[m_rankView]);
+ m_device->SetRenderState(Gfx::RENDER_STATE_LIGHTING, true);
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, true);
+
+ float fogStart = m_deepView[m_rankView]*m_fogStart[m_rankView];
+ float fogEnd = m_deepView[m_rankView];
+ m_device->SetFogParams(Gfx::FOG_LINEAR, m_fogColor[m_rankView], fogStart, fogEnd, 1.0f);
+
+ m_device->SetTransform(Gfx::TRANSFORM_VIEW, m_matView);
+
+ // TODO!
+ /*
+ for (int l1 = 0; l1 < m_objectTree.size(); l1++)
+ {
+ Gfx::EngineObjLevel1* p1 = &m_objectTree[l1];
+ p2 = p1->table[l1];
+ if ( p2 == 0 ) continue;
+ SetTexture(p2->texName1, 0);
+ SetTexture(p2->texName2, 1);
+ for ( l2=0 ; l2<p2->totalUsed ; l2++ )
+ {
+ p3 = p2->table[l2];
+ if ( p3 == 0 ) continue;
+ objRank = p3->objRank;
+ if ( !m_objectParam[objRank].bDrawFront ) continue;
+
+ {
+ D3DMATRIX mat = MAT_TO_D3DMAT(m_objectParam[objRank].transform);
+ m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &mat);
+ }
+
+ if ( !IsVisible(objRank) ) continue;
+ m_light->LightUpdate(m_objectParam[objRank].type);
+ for ( l3=0 ; l3<p3->totalUsed ; l3++ )
+ {
+ p4 = p3->table[l3];
+ if ( p4 == 0 ) continue;
+ if ( m_objectParam[objRank].distance < p4->min ||
+ m_objectParam[objRank].distance >= p4->max ) continue;
+ for ( l4=0 ; l4<p4->totalUsed ; l4++ )
+ {
+ p5 = p4->table[l4];
+ if ( p5 == 0 ) continue;
+ for ( l5=0 ; l5<p5->totalUsed ; l5++ )
+ {
+ p6 = p5->table[l5];
+ if ( p6 == 0 ) continue;
+ SetMaterial(p6->material);
+ SetState(p6->state);
+ if ( p6->type == D3DTYPE6T )
+ {
+ pv = &p6->vertex[0];
+ m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST,
+ D3DFVF_VERTEX2,
+ pv, p6->totalUsed,
+ NULL);
+ m_statisticTriangle += p6->totalUsed/3;
+ }
+ if ( p6->type == D3DTYPE6S )
+ {
+ pv = &p6->vertex[0];
+ m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP,
+ D3DFVF_VERTEX2,
+ pv, p6->totalUsed,
+ NULL);
+ m_statisticTriangle += p6->totalUsed-2;
+ }
+ }
+ }
+ }
+ }
+ }*/
+
+ m_particle->DrawParticle(Gfx::SH_FRONT); // draws the particles of the 3D world
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_TEST, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_LIGHTING, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
+
+ m_device->SetTransform(Gfx::TRANSFORM_VIEW, m_matViewInterface);
+ m_device->SetTransform(Gfx::TRANSFORM_PROJECTION, m_matProjInterface);
+ m_device->SetTransform(Gfx::TRANSFORM_WORLD, m_matWorldInterface);
+ }
+
+ // Draw foreground color
+ if (m_overFront)
+ DrawOverColor();
+
+ // Mouse & highlight at the end
+ DrawMouse();
+ DrawHighlight();
}
-bool Gfx::CEngine::Draw3DScene()
+void Gfx::CEngine::UpdateGroundSpotTextures()
{
- // TODO
- return true;
+ // TODO the original code modifying the textures is very complex, so stub for now
+ GetLogger()->Info("CEngine::UpdateGroundSpotTextures(): stub!\n");
}
-void Gfx::CEngine::SetUpInterfaceView()
+void Gfx::CEngine::DrawShadow()
{
- m_device->SetTransform(Gfx::TRANSFORM_WORLD, m_matWorldInterface);
- m_device->SetTransform(Gfx::TRANSFORM_VIEW, m_matViewInterface);
+ m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_LIGHTING, false);
+
+ Math::Matrix matrix;
+ matrix.LoadIdentity();
+ m_device->SetTransform(Gfx::TRANSFORM_WORLD, matrix);
+
+
+ Gfx::Material material;
+ material.diffuse = Gfx::Color(1.0f, 1.0f, 1.0f);
+ material.ambient = Gfx::Color(0.5f, 0.5f, 0.5f);
+ SetMaterial(material);
+
+ // TODO: create a separate texture
+ SetTexture("text.png");
+
+ Math::Point ts, ti;
+
+ float dp = 0.5f/256.0f;
+ ts.y = 192.0f/256.0f;
+ ti.y = 224.0f/256.0f;
+ ts.y += dp;
+ ti.y -= dp;
+
+ Math::Vector n(0.0f, 1.0f, 0.0f);
+
+ float startDeepView = m_deepView[m_rankView]*m_fogStart[m_rankView];
+ float endDeepView = m_deepView[m_rankView];
+
+ float lastIntensity = -1.0f;
+ for (int i = 0; i < static_cast<int>( m_shadows.size() ); i++)
+ {
+ if (m_shadows[i].hide) continue;
+
+ Math::Vector pos = m_shadows[i].pos; // pos = center of the shadow on the ground
+
+ if (m_eyePt.y == pos.y) continue; // camera at the same level?
+
+ float d = 0.0f;
+ float D = 0.0f;
+
+ // h is the height above the ground to which the shadow
+ // will be drawn.
+ if (m_eyePt.y > pos.y) // camera on?
+ {
+ float height = m_eyePt.y-pos.y;
+ float h = m_shadows[i].radius;
+ float max = height*0.5f;
+ if ( h > max ) h = max;
+ if ( h > 4.0f ) h = 4.0f;
+
+ D = Math::Distance(m_eyePt, pos);
+ if ( D >= endDeepView ) continue;
+ d = D*h/height;
+
+ pos.x += (m_eyePt.x-pos.x)*d/D;
+ pos.z += (m_eyePt.z-pos.z)*d/D;
+ pos.y += h;
+ }
+ else // camera underneath?
+ {
+ float height = pos.y-m_eyePt.y;
+ float h = m_shadows[i].radius;
+ float max = height*0.1f;
+ if ( h > max ) h = max;
+ if ( h > 4.0f ) h = 4.0f;
+
+ D = Math::Distance(m_eyePt, pos);
+ if ( D >= endDeepView ) continue;
+ d = D*h/height;
+
+ pos.x += (m_eyePt.x-pos.x)*d/D;
+ pos.z += (m_eyePt.z-pos.z)*d/D;
+ pos.y -= h;
+ }
+
+ // The hFactor decreases the intensity and size increases more
+ // the object is high relative to the ground.
+ float hFactor = m_shadows[i].height/20.0f;
+ if ( hFactor < 0.0f ) hFactor = 0.0f;
+ if ( hFactor > 1.0f ) hFactor = 1.0f;
+ hFactor = powf(1.0f-hFactor, 2.0f);
+ if ( hFactor < 0.2f ) hFactor = 0.2f;
+
+ float radius = m_shadows[i].radius*1.5f;
+ radius *= 2.0f-hFactor; // greater if high
+ radius *= 1.0f-d/D; // smaller if close
+
+
+ Math::Vector corner[4];
+
+ if (m_shadows[i].type == Gfx::ENG_SHADOW_NORM)
+ {
+ corner[0].x = +radius;
+ corner[0].z = +radius;
+ corner[0].y = 0.0f;
+
+ corner[1].x = -radius;
+ corner[1].z = +radius;
+ corner[1].y = 0.0f;
+
+ corner[2].x = +radius;
+ corner[2].z = -radius;
+ corner[2].y = 0.0f;
+
+ corner[3].x = -radius;
+ corner[3].z = -radius;
+ corner[3].y = 0.0f;
+
+ ts.x = 64.0f/256.0f;
+ ti.x = 96.0f/256.0f;
+ }
+ else
+ {
+ Math::Point rot;
+
+ rot = Math::RotatePoint(-m_shadows[i].angle, Math::Point(radius, radius));
+ corner[0].x = rot.x;
+ corner[0].z = rot.y;
+ corner[0].y = 0.0f;
+
+ rot = Math::RotatePoint(-m_shadows[i].angle, Math::Point(-radius, radius));
+ corner[1].x = rot.x;
+ corner[1].z = rot.y;
+ corner[1].y = 0.0f;
+
+ rot = Math::RotatePoint(-m_shadows[i].angle, Math::Point(radius, -radius));
+ corner[2].x = rot.x;
+ corner[2].z = rot.y;
+ corner[2].y = 0.0f;
+
+ rot = Math::RotatePoint(-m_shadows[i].angle, Math::Point(-radius, -radius));
+ corner[3].x = rot.x;
+ corner[3].z = rot.y;
+ corner[3].y = 0.0f;
+
+ if (m_shadows[i].type == Gfx::ENG_SHADOW_WORM)
+ {
+ ts.x = 96.0f/256.0f;
+ ti.x = 128.0f/256.0f;
+ }
+ else
+ {
+ ts.x = 64.0f/256.0f;
+ ti.x = 96.0f/256.0f;
+ }
+ }
+
+ corner[0] = Math::CrossProduct(corner[0], m_shadows[i].normal);
+ corner[1] = Math::CrossProduct(corner[1], m_shadows[i].normal);
+ corner[2] = Math::CrossProduct(corner[2], m_shadows[i].normal);
+ corner[3] = Math::CrossProduct(corner[3], m_shadows[i].normal);
+
+ corner[0] += pos;
+ corner[1] += pos;
+ corner[2] += pos;
+ corner[3] += pos;
+
+ ts.x += dp;
+ ti.x -= dp;
+
+ Gfx::Vertex vertex[4] =
+ {
+ Gfx::Vertex(corner[1], n, Math::Point(ts.x, ts.y)),
+ Gfx::Vertex(corner[0], n, Math::Point(ti.x, ts.y)),
+ Gfx::Vertex(corner[3], n, Math::Point(ts.x, ti.y)),
+ Gfx::Vertex(corner[2], n, Math::Point(ti.x, ti.y))
+ };
+
+ float intensity = (0.5f+m_shadows[i].intensity*0.5f)*hFactor;
+
+ // Decreases the intensity of the shade if you're in the area
+ // between the beginning and the end of the fog.
+ if ( D > startDeepView )
+ intensity *= 1.0f-(D-startDeepView)/(endDeepView-startDeepView);
+
+ if (intensity == 0.0f) continue;
+
+ if (lastIntensity != intensity) // intensity changed?
+ {
+ lastIntensity = intensity;
+ SetState(Gfx::ENG_RSTATE_TTEXTURE_WHITE, Gfx::Color(intensity, intensity, intensity, intensity));
+ }
+
+ m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
+ AddStatisticTriangle(2);
+ }
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, true);
+ m_device->SetRenderState(Gfx::RENDER_STATE_LIGHTING, true);
+}
+
+// STATUS: TESTED, VERIFIED
+void Gfx::CEngine::DrawBackground()
+{
+ if (m_skyMode && m_cloud->GetLevel() != 0.0f) // clouds ?
+ {
+ if (m_backgroundCloudUp != m_backgroundCloudDown) // degraded?
+ DrawBackgroundGradient(m_backgroundCloudUp, m_backgroundCloudDown);
+ }
+ else
+ {
+ if (m_backgroundColorUp != m_backgroundColorDown) // degraded?
+ DrawBackgroundGradient(m_backgroundColorUp, m_backgroundColorDown);
+ }
+
+ if (m_backForce || (m_skyMode && !m_backgroundName.empty()) )
+ {
+ DrawBackgroundImage(); // image
+ }
+}
+
+// STATUS: TESTED
+void Gfx::CEngine::DrawBackgroundGradient(const Gfx::Color& up, const Gfx::Color& down)
+{
+ Math::Point p1(0.0f, 0.5f);
+ Math::Point p2(1.0f, 1.0f);
+
+ Gfx::Color color[3] =
+ {
+ up,
+ down,
+ Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f)
+ };
+
+ SetState(Gfx::ENG_RSTATE_OPAQUE_COLOR);
+
+ m_device->SetTransform(Gfx::TRANSFORM_VIEW, m_matViewInterface);
m_device->SetTransform(Gfx::TRANSFORM_PROJECTION, m_matProjInterface);
+ m_device->SetTransform(Gfx::TRANSFORM_WORLD, m_matWorldInterface);
+
+ Gfx::VertexCol vertex[4] =
+ {
+ Gfx::VertexCol(Math::Vector(p1.x, p1.y, 0.0f), color[1], color[2]),
+ Gfx::VertexCol(Math::Vector(p1.x, p2.y, 0.0f), color[0], color[2]),
+ Gfx::VertexCol(Math::Vector(p2.x, p1.y, 0.0f), color[1], color[2]),
+ Gfx::VertexCol(Math::Vector(p2.x, p2.y, 0.0f), color[0], color[2])
+ };
+
+ m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
+ AddStatisticTriangle(2);
}
-bool Gfx::CEngine::DrawInterface()
+// Status: PART_TESTED
+void Gfx::CEngine::DrawBackgroundImageQuarter(Math::Point p1, Math::Point p2, const std::string& name)
{
- Gfx::VertexCol vertices[3] =
+ Math::Vector n = Math::Vector(0.0f, 0.0f, -1.0f); // normal
+
+ float u1, u2, v1, v2;
+ if (m_backgroundFull)
+ {
+ u1 = 0.0f;
+ v1 = 0.0f;
+ u2 = 1.0f;
+ v2 = 1.0f;
+
+ if (m_backgroundQuarter)
+ {
+ u1 += 0.5f/512.0f;
+ v1 += 0.5f/384.0f;
+ u2 -= 0.5f/512.0f;
+ v2 -= 0.5f/384.0f;
+ }
+ }
+ else
{
- Gfx::VertexCol(Math::Vector( 0.25f, 0.25f, 0.0f), Gfx::Color(1.0f, 0.0f, 0.0f)),
- Gfx::VertexCol(Math::Vector( 0.75f, 0.25f, 0.0f), Gfx::Color(0.0f, 1.0f, 0.0f)),
- Gfx::VertexCol(Math::Vector( 0.5f, 0.75f, 0.0f), Gfx::Color(0.0f, 0.0f, 1.0f))
+ float h = 0.5f; // visible area vertically (1=all)
+ float a = m_eyeDirV-Math::PI*0.15f;
+ if (a > Math::PI ) a -= Math::PI*2.0f; // a = -Math::PI..Math::PI
+ if (a > Math::PI/4.0f) a = Math::PI/4.0f;
+ if (a < -Math::PI/4.0f) a = -Math::PI/4.0f;
+
+ u1 = -m_eyeDirH/Math::PI;
+ u2 = u1+1.0f/Math::PI;
+
+ v1 = (1.0f-h)*(0.5f+a/(2.0f*Math::PI/4.0f))+0.1f;
+ v2 = v1+h;
+ }
+
+ SetTexture(name);
+ SetState(Gfx::ENG_RSTATE_OPAQUE_TEXTURE | Gfx::ENG_RSTATE_WRAP);
+
+ m_device->SetTransform(Gfx::TRANSFORM_VIEW, m_matViewInterface);
+ m_device->SetTransform(Gfx::TRANSFORM_PROJECTION, m_matProjInterface);
+ m_device->SetTransform(Gfx::TRANSFORM_WORLD, m_matWorldInterface);
+
+ Gfx::Vertex vertex[4] =
+ {
+ Gfx::Vertex(Math::Vector(p1.x, p1.y, 0.0f), n, Math::Point(u1, v2)),
+ Gfx::Vertex(Math::Vector(p1.x, p2.y, 0.0f), n, Math::Point(u1, v1)),
+ Gfx::Vertex(Math::Vector(p2.x, p1.y, 0.0f), n, Math::Point(u2, v2)),
+ Gfx::Vertex(Math::Vector(p2.x, p2.y, 0.0f), n, Math::Point(u2, v1))
};
- m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLES, vertices, 3);
+ m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
+ AddStatisticTriangle(2);
+}
- DrawMouse();
+// Status: TESTED, VERIFIED
+void Gfx::CEngine::DrawBackgroundImage()
+{
+ Math::Point p1, p2;
+ std::string name;
- return true;
+ if (m_backgroundQuarter)
+ {
+ p1.x = 0.0f;
+ p1.y = 0.5f;
+ p2.x = 0.5f;
+ p2.y = 1.0f;
+ QuarterName(name, m_backgroundName, 0);
+ DrawBackgroundImageQuarter(p1, p2, name);
+
+ p1.x = 0.5f;
+ p1.y = 0.5f;
+ p2.x = 1.0f;
+ p2.y = 1.0f;
+ QuarterName(name, m_backgroundName, 1);
+ DrawBackgroundImageQuarter(p1, p2, name);
+
+ p1.x = 0.0f;
+ p1.y = 0.0f;
+ p2.x = 0.5f;
+ p2.y = 0.5f;
+ QuarterName(name, m_backgroundName, 2);
+ DrawBackgroundImageQuarter(p1, p2, name);
+
+ p1.x = 0.5f;
+ p1.y = 0.0f;
+ p2.x = 1.0f;
+ p2.y = 0.5f;
+ QuarterName(name, m_backgroundName, 3);
+ DrawBackgroundImageQuarter(p1, p2, name);
+ }
+ else
+ {
+ p1.x = 0.0f;
+ p1.y = 0.0f;
+ p2.x = 1.0f;
+ p2.y = 1.0f;
+ DrawBackgroundImageQuarter(p1, p2, m_backgroundName);
+ }
+}
+
+void Gfx::CEngine::DrawPlanet()
+{
+ if (! m_planet->PlanetExist()) return;
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_LIGHTING, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
+
+ m_device->SetTransform(Gfx::TRANSFORM_VIEW, m_matViewInterface);
+ m_device->SetTransform(Gfx::TRANSFORM_PROJECTION, m_matProjInterface);
+ m_device->SetTransform(Gfx::TRANSFORM_WORLD, m_matWorldInterface);
+
+ m_planet->Draw(); // draws the planets
+}
+
+// Status: PART_TESTED
+void Gfx::CEngine::DrawForegroundImage()
+{
+ if (m_foregroundName.empty()) return;
+
+ Math::Vector n = Math::Vector(0.0f, 0.0f, -1.0f); // normal
+
+ Math::Point p1(0.0f, 0.0f);
+ Math::Point p2(1.0f, 1.0f);
+
+ float u1 = -m_eyeDirH/(Math::PI*0.6f)+Math::PI*0.5f;
+ float u2 = u1+0.50f;
+
+ float v1 = 0.2f;
+ float v2 = 1.0f;
+
+
+ Gfx::Vertex vertex[4] =
+ {
+ Gfx::Vertex(Math::Vector(p1.x, p1.y, 0.0f), n, Math::Point(u1, v2)),
+ Gfx::Vertex(Math::Vector(p1.x, p2.y, 0.0f), n, Math::Point(u1, v1)),
+ Gfx::Vertex(Math::Vector(p2.x, p1.y, 0.0f), n, Math::Point(u2, v2)),
+ Gfx::Vertex(Math::Vector(p2.x, p2.y, 0.0f), n, Math::Point(u2, v1))
+ };
+
+ SetTexture(m_foregroundName);
+ SetState(Gfx::ENG_RSTATE_CLAMP | Gfx::ENG_RSTATE_TTEXTURE_BLACK);
+
+ m_device->SetTransform(Gfx::TRANSFORM_VIEW, m_matViewInterface);
+ m_device->SetTransform(Gfx::TRANSFORM_PROJECTION, m_matProjInterface);
+ m_device->SetTransform(Gfx::TRANSFORM_WORLD, m_matWorldInterface);
+
+ m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
+ AddStatisticTriangle(2);
+}
+
+// Status: PART_TESTED
+void Gfx::CEngine::DrawOverColor()
+{
+ if (! m_stateColor) return;
+
+ // TODO: fuzzy compare?
+ if ( (m_overColor == Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f) && m_overMode == Gfx::ENG_RSTATE_TCOLOR_BLACK) ||
+ (m_overColor == Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f) && m_overMode == Gfx::ENG_RSTATE_TCOLOR_WHITE) ) return;
+
+ Math::Point p1(0.0f, 0.0f);
+ Math::Point p2(1.0f, 1.0f);
+
+ Gfx::Color color[3] =
+ {
+ m_overColor,
+ m_overColor,
+ Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f)
+ };
+
+ SetState(m_overMode);
+
+ // TODO: set also with m_overMode ?
+ m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_LIGHTING, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, false);
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, false);
+
+ m_device->SetTransform(Gfx::TRANSFORM_VIEW, m_matViewInterface);
+ m_device->SetTransform(Gfx::TRANSFORM_PROJECTION, m_matProjInterface);
+ m_device->SetTransform(Gfx::TRANSFORM_WORLD, m_matWorldInterface);
+
+ Gfx::VertexCol vertex[4] =
+ {
+ Gfx::VertexCol(Math::Vector(p1.x, p1.y, 0.0f), color[1], color[2]),
+ Gfx::VertexCol(Math::Vector(p1.x, p2.y, 0.0f), color[0], color[2]),
+ Gfx::VertexCol(Math::Vector(p2.x, p1.y, 0.0f), color[1], color[2]),
+ Gfx::VertexCol(Math::Vector(p2.x, p2.y, 0.0f), color[0], color[2])
+ };
+
+ m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
+ AddStatisticTriangle(2);
+}
+
+// Status: TESTED, VERIFIED
+void Gfx::CEngine::DrawHighlight()
+{
+ Math::Point min, max;
+ min.x = 1000000.0f;
+ min.y = 1000000.0f;
+ max.x = -1000000.0f;
+ max.y = -1000000.0f;
+
+ int i = 0;
+ while (m_highlightRank[i] != -1)
+ {
+ Math::Point omin, omax;
+ if (GetBBox2D(m_highlightRank[i++], omin, omax))
+ {
+ min.x = Math::Min(min.x, omin.x);
+ min.y = Math::Min(min.y, omin.y);
+ max.x = Math::Max(max.x, omax.x);
+ max.y = Math::Max(max.y, omax.y);
+ }
+ }
+
+ if ( min.x == 1000000.0f ||
+ min.y == 1000000.0f ||
+ max.x == -1000000.0f ||
+ max.y == -1000000.0f )
+ {
+ m_highlight = false; // not highlighted
+ }
+ else
+ {
+ m_highlightP1 = min;
+ m_highlightP2 = max;
+ m_highlight = true;
+ }
+
+ if (! m_highlight)
+ return;
+
+ Math::Point p1 = m_highlightP1;
+ Math::Point p2 = m_highlightP2;
+
+ int nbOut = 0;
+ if (p1.x < 0.0f || p1.x > 1.0f) nbOut++;
+ if (p1.y < 0.0f || p1.y > 1.0f) nbOut++;
+ if (p2.x < 0.0f || p2.x > 1.0f) nbOut++;
+ if (p2.y < 0.0f || p2.y > 1.0f) nbOut++;
+ if (nbOut > 2)
+ return;
+
+ SetState(Gfx::ENG_RSTATE_OPAQUE_COLOR);
+
+ float d = 0.5f+sinf(m_highlightTime*6.0f)*0.5f;
+ d *= (p2.x-p1.x)*0.1f;
+ p1.x += d;
+ p1.y += d;
+ p2.x -= d;
+ p2.y -= d;
+
+ Gfx::Color color(1.0f, 1.0f, 0.0f); // yellow
+
+ Gfx::VertexCol line[3] =
+ {
+ Gfx::VertexCol(Math::Vector(), color),
+ Gfx::VertexCol(Math::Vector(), color),
+ Gfx::VertexCol(Math::Vector(), color)
+ };
+
+ float dx = (p2.x - p1.x) / 5.0f;
+ float dy = (p2.y - p1.y) / 5.0f;
+
+ line[0].coord = Math::Vector(p1.x, p1.y + dy, 0.0f);
+ line[1].coord = Math::Vector(p1.x, p1.y, 0.0f);
+ line[2].coord = Math::Vector(p1.x + dx, p1.y, 0.0f);
+ m_device->DrawPrimitive(Gfx::PRIMITIVE_LINE_STRIP, line, 3);
+
+ line[0].coord = Math::Vector(p2.x - dx, p1.y, 0.0f);
+ line[1].coord = Math::Vector(p2.x, p1.y, 0.0f);
+ line[2].coord = Math::Vector(p2.x, p1.y + dy, 0.0f);
+ m_device->DrawPrimitive(Gfx::PRIMITIVE_LINE_STRIP, line, 3);
+
+ line[0].coord = Math::Vector(p2.x, p2.y - dy, 0.0f);
+ line[1].coord = Math::Vector(p2.x, p2.y, 0.0f);
+ line[2].coord = Math::Vector(p2.x - dx, p2.y, 0.0f);
+ m_device->DrawPrimitive(Gfx::PRIMITIVE_LINE_STRIP, line, 3);
+
+ line[0].coord = Math::Vector(p1.x + dx, p2.y, 0.0f);
+ line[1].coord = Math::Vector(p1.x, p2.y, 0.0f);
+ line[2].coord = Math::Vector(p1.x, p2.y - dy, 0.0f);
+ m_device->DrawPrimitive(Gfx::PRIMITIVE_LINE_STRIP, line, 3);
}
+// Status: TESTED, VERIFIED
void Gfx::CEngine::DrawMouse()
{
if (! m_mouseVisible)
@@ -648,6 +2816,7 @@ void Gfx::CEngine::DrawMouse()
DrawMouseSprite(pos, m_mouseSize, m_mice[index].icon2);
}
+// Status: TESTED, VERIFIED
void Gfx::CEngine::DrawMouseSprite(Math::Point pos, Math::Point size, int icon)
{
if (icon == -1)
@@ -672,72 +2841,11 @@ void Gfx::CEngine::DrawMouseSprite(Math::Point pos, Math::Point size, int icon)
Gfx::Vertex vertex[4] =
{
Gfx::Vertex(Math::Vector(p1.x, p1.y, 0.0f), normal, Math::Point(u1, v2)),
- Gfx::Vertex(Math::Vector(p2.x, p1.y, 0.0f), normal, Math::Point(u2, v2)),
Gfx::Vertex(Math::Vector(p1.x, p2.y, 0.0f), normal, Math::Point(u1, v1)),
+ Gfx::Vertex(Math::Vector(p2.x, p1.y, 0.0f), normal, Math::Point(u2, v2)),
Gfx::Vertex(Math::Vector(p2.x, p2.y, 0.0f), normal, Math::Point(u2, v1))
};
m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
AddStatisticTriangle(2);
}
-
-bool Gfx::CEngine::GetPause()
-{
- return m_pause;
-}
-
-Math::Vector Gfx::CEngine::GetLookatPt()
-{
- return m_lookatPt;
-}
-
-Math::Vector Gfx::CEngine::GetEyePt()
-{
- return m_eyePt;
-}
-
-void Gfx::CEngine::SetMouseVisible(bool visible)
-{
- m_mouseVisible = visible;
-}
-
-bool Gfx::CEngine::GetMouseVisible()
-{
- return m_mouseVisible;
-}
-
-void Gfx::CEngine::SetMousePos(Math::Point pos)
-{
- m_mousePos = pos;
-}
-
-Math::Point Gfx::CEngine::GetMousePos()
-{
- return m_mousePos;
-}
-
-void Gfx::CEngine::SetMouseType(Gfx::EngineMouseType type)
-{
- m_mouseType = type;
-}
-
-Gfx::EngineMouseType Gfx::CEngine::GetMouseType()
-{
- return m_mouseType;
-}
-
-void Gfx::CEngine::AddStatisticTriangle(int count)
-{
- m_statisticTriangle += count;
-}
-
-void Gfx::CEngine::SetShowStat(bool show)
-{
- m_showStats = show;
-}
-
-bool Gfx::CEngine::GetShowStat()
-{
- return m_showStats;
-}
-
diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h
index 25c5e5d..5a64e4e 100644
--- a/src/graphics/engine/engine.h
+++ b/src/graphics/engine/engine.h
@@ -1,5 +1,5 @@
// * This file is part of the COLOBOT source code
-// * Copyright (C) 2001-2008, Daniel ROUX & EPSITEC SA, www.epsitec.ch
+// * Copyright (C) 2001-2008, Daniel ROUX& EPSITEC SA, www.epsitec.ch
// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
// *
// * This program is free software: you can redistribute it and/or modify
@@ -15,18 +15,20 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// engine.h
+/**
+ * \file graphics/engine/engine.h
+ * \brief Main graphics engine - Gfx::CEngine class
+ */
#pragma once
-
+#include "app/system.h"
#include "common/event.h"
#include "graphics/core/color.h"
#include "graphics/core/material.h"
#include "graphics/core/texture.h"
#include "graphics/core/vertex.h"
#include "math/intpoint.h"
-#include "math/intsize.h"
#include "math/matrix.h"
#include "math/point.h"
#include "math/vector.h"
@@ -35,12 +37,13 @@
#include <string>
#include <vector>
#include <map>
+#include <set>
class CApplication;
class CInstanceManager;
class CObject;
-class CSound;
+class CSoundInterface;
namespace Gfx {
@@ -76,7 +79,7 @@ struct EngineTriangle
Gfx::VertexTex2 triangle[3];
//! Material
Gfx::Material material;
- //! Render state (TODO: ?)
+ //! Render state
int state;
//! 1st texture
Gfx::Texture tex1;
@@ -169,7 +172,10 @@ struct EngineObjLevel5
Gfx::EngineTriangleType type;
std::vector<Gfx::VertexTex2> vertices;
- EngineObjLevel5();
+ EngineObjLevel5()
+ {
+ state = 0;
+ }
};
/**
@@ -181,7 +187,10 @@ struct EngineObjLevel4
std::vector<Gfx::EngineObjLevel5> up;
Gfx::EngineObjLevel3* down;
- EngineObjLevel4();
+ EngineObjLevel4()
+ {
+ reserved = 0;
+ }
};
/**
@@ -194,7 +203,10 @@ struct EngineObjLevel3
std::vector<Gfx::EngineObjLevel4> up;
Gfx::EngineObjLevel2* down;
- EngineObjLevel3();
+ EngineObjLevel3()
+ {
+ min = max = 0.0f;
+ }
};
/**
@@ -206,7 +218,10 @@ struct EngineObjLevel2
std::vector<Gfx::EngineObjLevel3> up;
Gfx::EngineObjLevel1* down;
- EngineObjLevel2();
+ EngineObjLevel2()
+ {
+ objRank = 0;
+ }
};
/**
@@ -218,7 +233,7 @@ struct EngineObjLevel1
Gfx::Texture tex2;
std::vector<Gfx::EngineObjLevel2> up;
- EngineObjLevel1();
+ EngineObjLevel1() {}
};
/**
@@ -297,12 +312,14 @@ struct EngineGroundSpot
\brief Phase of life of an EngineGroundMark */
enum EngineGroundMarkPhase
{
+ //! Null phase
+ ENG_GR_MARK_PHASE_NULL = 0,
//! Increase
ENG_GR_MARK_PHASE_INC = 1,
//! Fixed
ENG_GR_MARK_PHASE_FIX = 2,
//! Decrease
- ENG_GR_MARK_PHASE_DEC = 2
+ ENG_GR_MARK_PHASE_DEC = 3
};
/**
@@ -407,7 +424,13 @@ enum EngineRenderState
//! The transparent color (black = no)
ENG_RSTATE_TCOLOR_BLACK = (1<<16),
//! The transparent color (white = no)
- ENG_RSTATE_TCOLOR_WHITE = (1<<17)
+ ENG_RSTATE_TCOLOR_WHITE = (1<<17),
+ //! Mode for rendering text
+ ENG_RSTATE_TEXT = (1<<18),
+ //! Only opaque texture, no blending, etc.
+ ENG_RSTATE_OPAQUE_TEXTURE = (1<<19),
+ //! Only opaque color, no texture, blending, etc.
+ ENG_RSTATE_OPAQUE_COLOR = (1<<20)
};
@@ -511,114 +534,138 @@ struct EngineMouse
class CEngine
{
public:
- CEngine(CInstanceManager *iMan, CApplication *app);
+ CEngine(CInstanceManager* iMan, CApplication* app);
~CEngine();
- //! Returns whether the device was initialized
- bool GetWasInit();
- //! Returns the last error encountered
- std::string GetError();
-
- //! Performs the first initialization, before a device was set
- bool Create();
- //! Frees all resources before exit
- void Destroy();
-
//! Sets the device to be used
- void SetDevice(Gfx::CDevice *device);
+ void SetDevice(Gfx::CDevice* device);
//! Returns the current device
Gfx::CDevice* GetDevice();
- //! Performs initialization after a device was created and set
- bool AfterDeviceSetInit();
+ //! Sets the terrain object
+ void SetTerrain(Gfx::CTerrain* terrain);
+
+ //! Returns the text rendering engine
+ CText* GetText();
+
+
+ //! Performs the initialization; must be called after device was set
+ bool Create();
+ //! Frees all resources before exit
+ void Destroy();
//! Resets some states and flushes textures after device was changed (e.g. resoulution changed)
void ResetAfterDeviceChanged();
- void SetTerrain(Gfx::CTerrain* terrain);
+
+ //! Called once per frame, the call is the entry point for rendering
+ void Render();
+
//! Processes incoming event
- bool ProcessEvent(const Event &event);
+ bool ProcessEvent(const Event& event);
- //! Renders a single frame
- bool Render();
+ //! Called once per frame, the call is the entry point for animating the scene
+ void FrameMove(float rTime);
+ //! Evolved throughout the game
+ void StepSimulation(float rTime);
- bool WriteProfile();
+ //! Writes a screenshot containing the current frame
+ bool WriteScreenShot(const std::string& fileName, int width, int height);
+
+ //! Reads settings from INI
+ bool ReadSettings();
+ //! Writes settings to INI
+ bool WriteSettings();
+
+ //@{
+ //! Management of game pause mode
void SetPause(bool pause);
bool GetPause();
+ //@}
+ //@{
+ //! Management of lock for the duration of movie sequence
void SetMovieLock(bool lock);
bool GetMovieLock();
+ //@}
- void SetShowStat(bool show);
- bool GetShowStat();
+ //@{
+ //! Management of displaying statistic information
+ void SetShowStats(bool show);
+ bool GetShowStats();
+ //@}
+ //! Enables/disables rendering
void SetRenderEnable(bool enable);
- int OneTimeSceneInit();
- int InitDeviceObjects();
- int DeleteDeviceObjects();
- int RestoreSurfaces();
- int FrameMove(float rTime);
- void StepSimulation(float rTime);
- int FinalCleanup();
+ //! Returns current size of viewport window
+ Math::IntPoint GetWindowSize();
+ //! Returns the last size of viewport window
+ Math::IntPoint GetLastWindowSize();
+
+ //@{
+ //! Conversion functions between window and interface coordinates
+ /** Window coordinates are from top-left (0,0) to bottom-right (w,h) - size of window
+ Interface cords are from bottom-left (0,0) to top-right (1,1) - and do not depend on window size */
+ Math::Point WindowToInterfaceCoords(Math::IntPoint pos);
+ Math::IntPoint InterfaceToWindowCoords(Math::Point pos);
+ //@}
+
+ //@{
+ //! Conversion functions between window and interface sizes
+ /** Unlike coordinate conversions, this is only scale conversion, not translation and scale. */
+ Math::Point WindowToInterfaceSize(Math::IntPoint size);
+ Math::IntPoint InterfaceToWindowSize(Math::Point size);
+ //@}
+
+ //! Returns the name of directory with textures
+ std::string GetTextureDir();
+
+ //! Increments the triangle counter for the current frame
void AddStatisticTriangle(int nb);
+ //! Returns the number of triangles in current frame
int GetStatisticTriangle();
- void SetHiliteRank(int *rankList);
- bool GetHilite(Math::Point &p1, Math::Point &p2);
- bool GetSpriteCoord(int &x, int &y);
- void SetInfoText(int line, char* text);
- char* GetInfoText(int line);
- void FirstExecuteAdapt(bool first);
- bool GetFullScreen();
- Math::Matrix* GetMatView();
- Math::Matrix* GetMatLeftView();
- Math::Matrix* GetMatRightView();
+ /* *************** Object management *************** */
- void TimeInit();
- void TimeEnterGel();
- void TimeExitGel();
- float TimeGet();
-
- int GetRestCreate();
int CreateObject();
void FlushObject();
bool DeleteObject(int objRank);
bool SetDrawWorld(int objRank, bool draw);
bool SetDrawFront(int objRank, bool draw);
- bool AddTriangle(int objRank, Gfx::VertexTex2* vertex, int nb, const Gfx::Material &mat,
+ bool AddTriangle(int objRank, Gfx::VertexTex2* vertex, int nb, const Gfx::Material& mat,
int state, std::string texName1, std::string texName2,
float min, float max, bool globalUpdate);
- bool AddSurface(int objRank, Gfx::VertexTex2* vertex, int nb, const Gfx::Material &mat,
+ bool AddSurface(int objRank, Gfx::VertexTex2* vertex, int nb, const Gfx::Material& mat,
int state, std::string texName1, std::string texName2,
float min, float max, bool globalUpdate);
- bool AddQuick(int objRank, Gfx::EngineObjLevel5* buffer,
+ bool AddQuick(int objRank, const Gfx::EngineObjLevel5& buffer,
std::string texName1, std::string texName2,
float min, float max, bool globalUpdate);
- Gfx::EngineObjLevel5* SearchTriangle(int objRank, const Gfx::Material &mat,
+ Gfx::EngineObjLevel5* SearchTriangle(int objRank, const Gfx::Material& mat,
int state, std::string texName1, std::string texName2,
float min, float max);
void ChangeLOD();
- bool ChangeSecondTexture(int objRank, char* texName2);
+ bool ChangeSecondTexture(int objRank, const std::string& texName2);
int GetTotalTriangles(int objRank);
int GetTriangles(int objRank, float min, float max, Gfx::EngineTriangle* buffer, int size, float percent);
- bool GetBBox(int objRank, Math::Vector &min, Math::Vector &max);
- bool ChangeTextureMapping(int objRank, const Gfx::Material &mat, int state,
- const std::string &texName1, const std::string &texName2,
+ bool GetBBox(int objRank, Math::Vector& min, Math::Vector& max);
+ bool ChangeTextureMapping(int objRank, const Gfx::Material& mat, int state,
+ const std::string& texName1, const std::string& texName2,
float min, float max, Gfx::EngineTextureMapping mode,
float au, float bu, float av, float bv);
- bool TrackTextureMapping(int objRank, const Gfx::Material &mat, int state,
- const std::string &texName1, const std::string &texName2,
+ bool TrackTextureMapping(int objRank, const Gfx::Material& mat, int state,
+ const std::string& texName1, const std::string& texName2,
float min, float max, Gfx::EngineTextureMapping mode,
float pos, float factor, float tl, float ts, float tt);
- bool SetObjectTransform(int objRank, const Math::Matrix &transform);
- bool GetObjectTransform(int objRank, Math::Matrix &transform);
+ bool SetObjectTransform(int objRank, const Math::Matrix& transform);
+ bool GetObjectTransform(int objRank, Math::Matrix& transform);
bool SetObjectType(int objRank, Gfx::EngineObjectType type);
Gfx::EngineObjectType GetObjectType(int objRank);
bool SetObjectTransparency(int objRank, float value);
@@ -627,20 +674,25 @@ public:
void ShadowDelete(int objRank);
bool SetObjectShadowHide(int objRank, bool hide);
bool SetObjectShadowType(int objRank, Gfx::EngineShadowType type);
- bool SetObjectShadowPos(int objRank, const Math::Vector &pos);
- bool SetObjectShadowNormal(int objRank, const Math::Vector &n);
+ bool SetObjectShadowPos(int objRank, const Math::Vector& pos);
+ bool SetObjectShadowNormal(int objRank, const Math::Vector& n);
bool SetObjectShadowAngle(int objRank, float angle);
bool SetObjectShadowRadius(int objRank, float radius);
bool SetObjectShadowIntensity(int objRank, float intensity);
bool SetObjectShadowHeight(int objRank, float h);
float GetObjectShadowRadius(int objRank);
+ //! Lists the ranks of objects and subobjects selected
+ void SetHighlightRank(int* rankList);
+ //! Returns the highlighted rectangle
+ bool GetHighlight(Math::Point& p1, Math::Point& p2);
+
void GroundSpotFlush();
int GroundSpotCreate();
void GroundSpotDelete(int rank);
- bool SetObjectGroundSpotPos(int rank, const Math::Vector &pos);
+ bool SetObjectGroundSpotPos(int rank, const Math::Vector& pos);
bool SetObjectGroundSpotRadius(int rank, float radius);
- bool SetObjectGroundSpotColor(int rank, const Gfx::Color &color);
+ bool SetObjectGroundSpotColor(int rank, const Gfx::Color& color);
bool SetObjectGroundSpotMinMax(int rank, float min, float max);
bool SetObjectGroundSpotSmooth(int rank, float smooth);
@@ -649,216 +701,356 @@ public:
int dx, int dy, char* table);
bool GroundMarkDelete(int rank);
+ //! Updates the state after creating objects
void Update();
- void SetViewParams(const Math::Vector &eyePt, const Math::Vector &lookatPt,
- const Math::Vector &upVec, float eyeDistance);
- Gfx::Texture CreateTexture(const std::string &texName,
- const Gfx::TextureCreateParams &params);
- Gfx::Texture CreateTexture(const std::string &texName);
- void DestroyTexture(const std::string &texName);
+ /* *************** Mode setting *************** */
+
+ //! Sets the current rendering state
+ void SetState(int state, const Gfx::Color& color = Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f));
+
+ //! Sets the current material
+ void SetMaterial(const Gfx::Material& mat);
+
+ //! Specifies the location and direction of view
+ void SetViewParams(const Math::Vector& eyePt, const Math::Vector& lookatPt,
+ const Math::Vector& upVec, float eyeDistance);
+
+ //! Creates texture with the specified params
+ Gfx::Texture CreateTexture(const std::string& texName,
+ const Gfx::TextureCreateParams& params);
+ //! Creates texture
+ Gfx::Texture CreateTexture(const std::string& texName);
- bool LoadTexture(const std::string &name, int stage = 0);
+ //! Destroys texture, unloading it and removing from cache
+ void DestroyTexture(const std::string& texName);
+
+ //! Loads texture, creating it if not already present
+ bool LoadTexture(const std::string& name);
+ //! Loads all necessary textures
bool LoadAllTextures();
+ //! Sets texture for given stage; if not present in cache, the texture is loaded
+ bool SetTexture(const std::string& name, int stage = 0);
+
+ //@{
+ //! Border management (distance limits) depends of the resolution (LOD = level-of-detail)
void SetLimitLOD(int rank, float limit);
float GetLimitLOD(int rank, bool last=false);
+ //@}
+ //! Defines of the distance field of vision
void SetTerrainVision(float vision);
+ //@{
+ //! Management of camera angle
+ /**
+ 0.75 = normal
+ 1.50 = wide-angle */
+ void SetFocus(float focus);
+ float GetFocus();
+ //@}
+
+ //@{
+ //! Management of the global mode of marking
void SetGroundSpot(bool mode);
bool GetGroundSpot();
+ //@}
+
+ //@{
+ //! Management of the global mode of shading
void SetShadow(bool mode);
bool GetShadow();
+ //@}
+
+ //@{
+ //! Management of the global mode of contamination
void SetDirty(bool mode);
bool GetDirty();
+ //@}
+
+ //@{
+ //! Management of the global mode of horizontal fog patches
void SetFog(bool mode);
bool GetFog();
+ //@}
+
+ //! Indicates whether it is possible to give a color SetState
bool GetStateColor();
+ //@{
+ //! Management of the global mode of secondary texturing
void SetSecondTexture(int texNum);
int GetSecondTexture();
+ //@}
+ //@{
+ //! Management of view mode
void SetRankView(int rank);
int GetRankView();
+ //@}
+ //! Whether to draw the world
void SetDrawWorld(bool draw);
+
+ //! Whether to draw the world on the interface
void SetDrawFront(bool draw);
- void SetAmbientColor(const Gfx::Color &color, int rank = 0);
+ //@{
+ //! Ambient color management
+ void SetAmbientColor(const Gfx::Color& color, int rank = 0);
Gfx::Color GetAmbientColor(int rank = 0);
+ //@}
- void SetWaterAddColor(const Gfx::Color &color);
+ //@{
+ //! Color management under water
+ void SetWaterAddColor(const Gfx::Color& color);
Gfx::Color GetWaterAddColor();
+ //@}
- void SetFogColor(const Gfx::Color &color, int rank = 0);
+ //@{
+ //! Management of the fog color
+ void SetFogColor(const Gfx::Color& color, int rank = 0);
Gfx::Color GetFogColor(int rank = 0);
+ //@}
+ //@{
+ //! Management of the depth of field.
+ /** Beyond this distance, nothing is visible.
+ Shortly (according SetFogStart), one enters the fog. */
void SetDeepView(float length, int rank = 0, bool ref=false);
float GetDeepView(int rank = 0);
+ //@}
+
+ //@{
+ //! Management the start of fog.
+ /** With 0.0, the fog from the point of view (fog max).
+ With 1.0, the fog from the depth of field (no fog). */
void SetFogStart(float start, int rank = 0);
float GetFogStart(int rank = 0);
+ //@}
- void SetBackground(const std::string &name, Gfx::Color up = Gfx::Color(), Gfx::Color down = Gfx::Color(),
+ //@{
+ //! Management of the background image to use
+ void SetBackground(const std::string& name, Gfx::Color up = Gfx::Color(), Gfx::Color down = Gfx::Color(),
Gfx::Color cloudUp = Gfx::Color(), Gfx::Color cloudDown = Gfx::Color(),
bool full = false, bool quarter = false);
- void GetBackground(const std::string &name, Gfx::Color &up, Gfx::Color &down,
- Gfx::Color &cloudUp, Gfx::Color &cloudDown,
- bool &full, bool &quarter);
- void SetFrontsizeName(char *name);
+ void GetBackground(std::string& name, Gfx::Color& up, Gfx::Color& down,
+ Gfx::Color& cloudUp, Gfx::Color& cloudDown,
+ bool& full, bool& quarter);
+ //@}
+
+ //! Specifies the name of foreground texture
+ void SetForegroundName(const std::string& name);
+ //! Specifies whether to draw the foreground
void SetOverFront(bool front);
- void SetOverColor(const Gfx::Color &color = Gfx::Color(), int mode = ENG_RSTATE_TCOLOR_BLACK);
+ //! Sets the foreground overlay color
+ void SetOverColor(const Gfx::Color& color = Gfx::Color(), int mode = ENG_RSTATE_TCOLOR_BLACK);
+ //@{
+ //! Management of the particle density
void SetParticleDensity(float value);
float GetParticleDensity();
+ //@}
+
+ //! Adapts particle factor according to particle density
float ParticleAdapt(float factor);
+ //@{
+ //! Management of the distance of clipping.
void SetClippingDistance(float value);
float GetClippingDistance();
+ //@}
+ //@{
+ //! Management of objects detals.
void SetObjectDetail(float value);
float GetObjectDetail();
+ //@}
+ //@{
+ //! The amount of management objects gadgets
void SetGadgetQuantity(float value);
float GetGadgetQuantity();
+ //@}
+ //@{
+ //! Management the quality of textures
void SetTextureQuality(int value);
int GetTextureQuality();
+ //@}
+ //@{
+ //! Management mode of toto
void SetTotoMode(bool present);
bool GetTotoMode();
+ //@}
+ //@{
+ //! Management the mode of foreground
void SetLensMode(bool present);
bool GetLensMode();
+ //@}
+ //@{
+ //! Management the mode of water
void SetWaterMode(bool present);
bool GetWaterMode();
+ //@}
void SetLightingMode(bool present);
bool GetLightingMode();
+ //@{
+ //! Management the mode of sky
void SetSkyMode(bool present);
bool GetSkyMode();
+ //@}
+ //@{
+ //! Management the mode of background
void SetBackForce(bool present);
bool GetBackForce();
+ //@}
+ //@{
+ //! Management the mode of planets
void SetPlanetMode(bool present);
bool GetPlanetMode();
+ //@}
+ //@{
+ //! Managing the mode of dynamic lights.
void SetLightMode(bool present);
bool GetLightMode();
+ //@}
+ //@{
+ // TODO: move to more appropriate class ?
+ //! Management of the indentation mode while editing (CEdit)
void SetEditIndentMode(bool autoIndent);
bool GetEditIndentMode();
+ //@}
+ //@{
+ // TODO: move to more appropriate class ?
+ //! Management of tab indent when editing (CEdit)
void SetEditIndentValue(int value);
int GetEditIndentValue();
+ //@}
+ //@{
+ //! Management of game speed
void SetSpeed(float speed);
float GetSpeed();
+ //@{
+ //! Management of precision of robot tracks
void SetTracePrecision(float factor);
float GetTracePrecision();
+ //@}
- void SetFocus(float focus);
- float GetFocus();
- Math::Vector GetEyePt();
- Math::Vector GetLookatPt();
- float GetEyeDirH();
- float GetEyeDirV();
- Math::Point GetDim();
- void UpdateMatProj();
-
- void ApplyChange();
-
- void FlushPressKey();
- void ResetKey();
- void SetKey(int keyRank, int option, int key);
- int GetKey(int keyRank, int option);
-
- void SetJoystick(bool enable);
- bool GetJoystick();
-
- void SetDebugMode(bool mode);
- bool GetDebugMode();
- bool GetSetupMode();
-
- bool IsVisiblePoint(const Math::Vector &pos);
-
- int DetectObject(Math::Point mouse);
- void SetState(int state, Gfx::Color color = Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f));
- void SetTexture(const std::string &name, int stage = 0);
- void SetMaterial(const Gfx::Material &mat);
-
+ //@{
+ //! Management of mouse cursor visibility
void SetMouseVisible(bool show);
bool GetMouseVisible();
+ //@}
+
+ //@{
+ //! Management of mouse cursor position
void SetMousePos(Math::Point pos);
Math::Point GetMousePos();
+ //@}
+
+ //@{
+ //! Management of mouse cursor type
void SetMouseType(Gfx::EngineMouseType type);
Gfx::EngineMouseType GetMouseType();
+ //@}
- CText* GetText();
+ //! Returns the view matrix
+ const Math::Matrix& GetMatView();
+ //! Returns the camera center point
+ Math::Vector GetEyePt();
+ //! Returns the camera target point
+ Math::Vector GetLookatPt();
+ //! Returns the horizontal direction angle of view
+ float GetEyeDirH();
+ //! Returns the vertical direction angle of view
+ float GetEyeDirV();
+ //! Indicates whether a point is visible
+ bool IsVisiblePoint(const Math::Vector& pos);
+
+ //! Resets the projection matrix after changes
+ void UpdateMatProj();
- bool ChangeColor(char *name, Gfx::Color colorRef1, Gfx::Color colorNew1,
- Gfx::Color colorRef2, Gfx::Color colorNew2,
- float tolerance1, float tolerance2,
- Math::Point ts, Math::Point ti,
- Math::Point *pExclu=0, float shift=0.0f, bool hSV=false);
- bool OpenImage(char *name);
- bool CopyImage();
- bool LoadImage();
- bool ScrollImage(int dx, int dy);
- bool SetDot(int x, int y, Gfx::Color color);
- bool CloseImage();
- bool WriteScreenShot(char *filename, int width, int height);
- //bool GetRenderDC(HDC &hDC);
- //bool ReleaseRenderDC(HDC &hDC);
- //PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp);
- //bool CreateBMPFile(LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC);
+ //! Updates the scene after a change of parameters
+ void ApplyChange();
protected:
+ //! Prepares the interface for 3D scene
+ void Draw3DScene();
+ //! Draws the user interface over the scene
+ void DrawInterface();
- void SetUp3DView();
- bool Draw3DScene();
-
- void SetUpInterfaceView();
- bool DrawInterface();
+ //! Updates the textures used for drawing ground spot
+ void UpdateGroundSpotTextures();
- void DrawGroundSpot();
+ //! Draws shadows
void DrawShadow();
+ //! Draws the gradient background
void DrawBackground();
- void DrawBackgroundGradient(Gfx::Color up, Gfx::Color down);
- void DrawBackgroundImageQuarter(Math::Point p1, Math::Point p2, char *name);
+ //! Draws the gradient background
+ void DrawBackgroundGradient(const Gfx::Color& up, const Gfx::Color& down);
+ //! Draws a portion of the image background
+ void DrawBackgroundImageQuarter(Math::Point p1, Math::Point p2, const std::string& name);
+ //! Draws the image background
void DrawBackgroundImage();
+ //! Draws all the planets
void DrawPlanet();
- void DrawFrontsize();
+ //! Draws the image foreground
+ void DrawForegroundImage();
+ //! Draws the foreground color
void DrawOverColor();
- void DrawHilite();
+ //! Draws the rectangle of the object highlighted
+ void DrawHighlight();
+ //! Draws the mouse cursor
void DrawMouse();
+ //! Draw part of mouse cursor sprite
void DrawMouseSprite(Math::Point pos, Math::Point dim, int icon);
- /*
- Gfx::ObjLevel2* AddLevel1(Gfx::ObjLevel1 *&p1, char* texName1, char* texName2);
- Gfx::ObjLevel3* AddLevel2(Gfx::ObjLevel2 *&p2, int objRank);
- Gfx::ObjLevel4* AddLevel3(Gfx::ObjLevel3 *&p3, float min, float max);
- Gfx::ObjLevel5* AddLevel4(Gfx::ObjLevel4 *&p4, int reserve);
- Gfx::ObjLevel6* AddLevel5(Gfx::ObjLevel5 *&p5, Gfx::TriangleType type, const Gfx::Material &mat, int state, int nb);*/
-
+ //! Tests whether the given object is visible
bool IsVisible(int objRank);
+
+ //! Detects whether an object is affected by the mouse
bool DetectBBox(int objRank, Math::Point mouse);
- bool GetBBox2D(int objRank, Math::Point &min, Math::Point &max);
- bool DetectTriangle(Math::Point mouse, Gfx::VertexTex2 *triangle, int objRank, float &dist);
- bool TransformPoint(Math::Vector &p2D, int objRank, Math::Vector p3D);
+
+ //! Compute and return the 2D box on screen of any object
+ bool GetBBox2D(int objRank, Math::Point& min, Math::Point& max);
+
+ //! Detects the target object that is selected with the mouse
+ /** Returns the rank of the object or -1. */
+ int DetectObject(Math::Point mouse);
+
+ //! Detects whether the mouse is in a triangle.
+ bool DetectTriangle(Math::Point mouse, Gfx::VertexTex2* triangle, int objRank, float& dist);
+
+ //! Transforms a 3D point (x, y, z) in 2D space (x, y, -) of the window
+ /** The coordinated p2D.z gives the distance. */
+ bool TransformPoint(Math::Vector& p2D, int objRank, Math::Vector p3D);
+
+ //! Calculates the distances between the viewpoint and the origin of different objects
void ComputeDistance();
+
+ //! Updates all the geometric parameters of objects
void UpdateGeometry();
protected:
CInstanceManager* m_iMan;
CApplication* m_app;
- CSound* m_sound;
+ CSoundInterface* m_sound;
Gfx::CDevice* m_device;
Gfx::CText* m_text;
Gfx::CLightManager* m_lightMan;
@@ -869,51 +1061,54 @@ protected:
Gfx::CPlanet* m_planet;
Gfx::CTerrain* m_terrain;
- bool m_wasInit;
+ //! Last encountered error
std::string m_error;
//! Whether to show stats (FPS, etc)
bool m_showStats;
- int m_blackSrcBlend[2];
- int m_blackDestBlend[2];
- int m_whiteSrcBlend[2];
- int m_whiteDestBlend[2];
- int m_diffuseSrcBlend[2];
- int m_diffuseDestBlend[2];
- int m_alphaSrcBlend[2];
- int m_alphaDestBlend[2];
+ //! Speed of animation
+ float m_speed;
+ //! Pause mode
+ bool m_pause;
+ //! Rendering enabled?
+ bool m_render;
+ //! Lock for duration of movie?
+ bool m_movieLock;
+ //! Projection matrix for 3D scene
Math::Matrix m_matProj;
- Math::Matrix m_matLeftView;
- Math::Matrix m_matRightView;
+ //! View matrix for 3D scene
Math::Matrix m_matView;
+ //! Camera angle for 3D scene
float m_focus;
+ //! World matrix for 2D interface
Math::Matrix m_matWorldInterface;
+ //! Projection matrix for 2D interface
Math::Matrix m_matProjInterface;
+ //! View matrix for 2D interface
Math::Matrix m_matViewInterface;
- long m_baseTime;
- long m_stopTime;
- float m_absTime;
- float m_lastTime;
- float m_speed;
- bool m_pause;
- bool m_render;
- bool m_movieLock;
-
- //! Current size of window
- Math::IntSize m_size;
- Math::IntSize m_lastSize;
+ //! Current size of viewport window
+ Math::IntPoint m_size;
+ //! Previous size of viewport window
+ Math::IntPoint m_lastSize;
+ //! Root of tree object structure (level 1 list)
std::vector<Gfx::EngineObjLevel1> m_objectTree;
+ //! Object parameters
std::vector<Gfx::EngineObject> m_objects;
- std::vector<Gfx::EngineShadow> m_shadow;
- std::vector<Gfx::EngineGroundSpot> m_groundSpot;
+ //! Shadow list
+ std::vector<Gfx::EngineShadow> m_shadows;
+ //! Ground spot list
+ std::vector<Gfx::EngineGroundSpot> m_groundSpots;
+ //! Ground mark
Gfx::EngineGroundMark m_groundMark;
+ //! Location of camera
Math::Vector m_eyePt;
+ //! Camera target
Math::Vector m_lookatPt;
float m_eyeDirH;
float m_eyeDirV;
@@ -926,7 +1121,6 @@ protected:
Gfx::Color m_waterAddColor;
int m_statisticTriangle;
bool m_updateGeometry;
- //char m_infoText[10][200];
int m_alphaMode;
bool m_stateColor;
bool m_forceStateColor;
@@ -946,11 +1140,11 @@ protected:
bool m_overFront;
Gfx::Color m_overColor;
int m_overMode;
- std::string m_frontsizeName;
+ std::string m_foregroundName;
bool m_drawWorld;
bool m_drawFront;
float m_limitLOD[2];
- float m_particuleDensity;
+ float m_particleDensity;
float m_clippingDistance;
float m_lastClippingDistance;
float m_objectDetail;
@@ -969,34 +1163,53 @@ protected:
int m_editIndentValue;
float m_tracePrecision;
- int m_hiliteRank[100];
- bool m_hilite;
- Math::Point m_hiliteP1;
- Math::Point m_hiliteP2;
-
- int m_lastState;
- Gfx::Color m_lastColor;
- char m_lastTexture[2][50];
- Gfx::Material m_lastMaterial;
-
+ //! Ranks of highlighted objects
+ int m_highlightRank[100];
+ //! Highlight visible?
+ bool m_highlight;
+ //! Time counter for highlight animation
+ float m_highlightTime;
+ //@{
+ //! Highlight rectangle points
+ Math::Point m_highlightP1;
+ Math::Point m_highlightP2;
+ //@}
+
+ //! Texture directory name
std::string m_texPath;
+ //! Default texture create params
Gfx::TextureCreateParams m_defaultTexParams;
+ //! Map of loaded textures (by name)
std::map<std::string, Gfx::Texture> m_texNameMap;
+ //! Reverse map of loaded textures (by texture)
std::map<Gfx::Texture, std::string> m_revTexNameMap;
+ //! Blacklist map of textures
+ /** Textures on this list were not successful in first loading,
+ * so are disabled for subsequent load calls. */
+ std::set<std::string> m_texBlacklist;
+ //! Mouse cursor definitions
Gfx::EngineMouse m_mice[Gfx::ENG_MOUSE_COUNT];
+ //! Texture with mouse cursors
Gfx::Texture m_miceTexture;
+ //! Size of mouse cursor
Math::Point m_mouseSize;
+ //! Type of mouse cursor
Gfx::EngineMouseType m_mouseType;
+ //! Position of mouse in interface coords
Math::Point m_mousePos;
+ //! Is mouse visible?
bool m_mouseVisible;
- //LPDIRECTDRAWSURFACE7 m_imageSurface;
- //DDSURFACEDESC2 m_imageDDSD;
- //WORD* m_imageCopy;
- //int m_imageDX;
- //int m_imageDY;
+ //! Last engine render state (-1 at the beginning of frame)
+ int m_lastState;
+ //! Last color set with render state
+ Gfx::Color m_lastColor;
+ //! Last texture names for 2 used texture stages
+ std::string m_lastTexture[2];
+ //! Last material
+ Gfx::Material m_lastMaterial;
};
}; // namespace Gfx
diff --git a/src/graphics/engine/lightman.h b/src/graphics/engine/lightman.h
index 8272125..52058c8 100644
--- a/src/graphics/engine/lightman.h
+++ b/src/graphics/engine/lightman.h
@@ -15,7 +15,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// lightman.h
+/**
+ * \file graphics/engine/lightman.h
+ * \brief Dynamic light manager - Gfx::CLightManager class
+ */
#pragma once
diff --git a/src/graphics/engine/lightning.cpp b/src/graphics/engine/lightning.cpp
index 4db5511..4ecdb3c 100644
--- a/src/graphics/engine/lightning.cpp
+++ b/src/graphics/engine/lightning.cpp
@@ -19,5 +19,71 @@
#include "graphics/engine/lightning.h"
+#include "common/logger.h"
-// TODO implementation
+
+Gfx::CLightning::CLightning(CInstanceManager* iMan, Gfx::CEngine* engine)
+{
+ GetLogger()->Info("CLightning::CLightning() stub!\n");
+ // TODO!
+}
+
+Gfx::CLightning::~CLightning()
+{
+ GetLogger()->Info("CLightning::~CLightning() stub!\n");
+ // TODO!
+}
+
+void Gfx::CLightning::Flush()
+{
+ GetLogger()->Info("CLightning::Flush() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CLightning::EventProcess(const Event &event)
+{
+ GetLogger()->Info("CLightning::EventProcess() stub!\n");
+ // TODO!
+ return true;
+}
+
+bool Gfx::CLightning::Create(float sleep, float delay, float magnetic)
+{
+ GetLogger()->Info("CLightning::Create() stub!\n");
+ // TODO!
+ return true;
+}
+
+bool Gfx::CLightning::GetStatus(float &sleep, float &delay, float &magnetic, float &progress)
+{
+ GetLogger()->Info("CLightning::GetStatus() stub!\n");
+ // TODO!
+ return true;
+}
+
+bool Gfx::CLightning::SetStatus(float sleep, float delay, float magnetic, float progress)
+{
+ GetLogger()->Info("CLightning::SetStatus() stub!\n");
+ // TODO!
+ return true;
+}
+
+void Gfx::CLightning::Draw()
+{
+ GetLogger()->Info("CLightning::Draw() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CLightning::EventFrame(const Event &event)
+{
+ GetLogger()->Info("CLightning::EventFrame() stub!\n");
+ // TODO!
+ return true;
+}
+
+CObject* Gfx::CLightning::SearchObject(Math::Vector pos)
+{
+ GetLogger()->Info("CLightning::SearchObject() stub!\n");
+ // TODO!
+ return nullptr;
+}
diff --git a/src/graphics/engine/lightning.h b/src/graphics/engine/lightning.h
index 957344c..3b4e2cf 100644
--- a/src/graphics/engine/lightning.h
+++ b/src/graphics/engine/lightning.h
@@ -15,7 +15,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// lightning.h (aka blitz.h)
+/**
+ * \file graphics/engine/lightning.h
+ * \brief Lightning rendering - Gfx::CLightning class (aka blitz)
+ */
#pragma once
@@ -73,7 +76,7 @@ protected:
float m_sleep;
float m_delay;
float m_magnetic;
- BlitzPhase m_phase;
+ Gfx::BlitzPhase m_phase;
float m_time;
float m_speed;
float m_progress;
diff --git a/src/graphics/engine/modelfile.h b/src/graphics/engine/modelfile.h
index 6a30487..fab190f 100644
--- a/src/graphics/engine/modelfile.h
+++ b/src/graphics/engine/modelfile.h
@@ -15,7 +15,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// modelfile.h (aka modfile.h)
+/**
+ * \file graphics/engine/modelfile.h
+ * \brief Model loading - Gfx::CModelFile class (aka modfile)
+ */
#include "graphics/engine/engine.h"
#include "graphics/core/vertex.h"
diff --git a/src/graphics/engine/particle.cpp b/src/graphics/engine/particle.cpp
index 84e2f9d..9a21fe0 100644
--- a/src/graphics/engine/particle.cpp
+++ b/src/graphics/engine/particle.cpp
@@ -19,5 +19,284 @@
#include "graphics/engine/particle.h"
+#include "common/logger.h"
-// TODO implementation
+
+Gfx::CParticle::CParticle(CInstanceManager* iMan, Gfx::CEngine* engine)
+{
+ GetLogger()->Info("CParticle::CParticle() stub!\n");
+ // TODO!
+}
+
+Gfx::CParticle::~CParticle()
+{
+ GetLogger()->Info("CParticle::~CParticle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetDevice(Gfx::CDevice* device)
+{
+ GetLogger()->Info("CParticle::SetDevice() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::FlushParticle()
+{
+ GetLogger()->Info("CParticle::FlushParticle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::FlushParticle(int sheet)
+{
+ GetLogger()->Info("CParticle::FlushParticle() stub!\n");
+ // TODO!
+}
+
+int Gfx::CParticle::CreateParticle(Math::Vector pos, Math::Vector speed, Math::Point dim,
+ Gfx::ParticleType type, float duration, float mass,
+ float windSensitivity, int sheet)
+{
+ GetLogger()->Info("CParticle::CreateParticle() stub!\n");
+ // TODO!
+ return 0;
+}
+
+int Gfx::CParticle::CreateFrag(Math::Vector pos, Math::Vector speed, Gfx::EngineTriangle *triangle,
+ Gfx::ParticleType type, float duration, float mass,
+ float windSensitivity, int sheet)
+{
+ GetLogger()->Info("CParticle::CreateFrag() stub!\n");
+ // TODO!
+ return 0;
+}
+
+int Gfx::CParticle::CreatePart(Math::Vector pos, Math::Vector speed, Gfx::ParticleType type,
+ float duration, float mass, float weight,
+ float windSensitivity, int sheet)
+{
+ GetLogger()->Info("CParticle::CreatePart() stub!\n");
+ // TODO!
+ return 0;
+}
+
+int Gfx::CParticle::CreateRay(Math::Vector pos, Math::Vector goal, Gfx::ParticleType type, Math::Point dim,
+ float duration, int sheet)
+{
+ GetLogger()->Info("CParticle::CreateRay() stub!\n");
+ // TODO!
+ return 0;
+}
+
+int Gfx::CParticle::CreateTrack(Math::Vector pos, Math::Vector speed, Math::Point dim, Gfx::ParticleType type,
+ float duration, float mass, float length, float width)
+{
+ GetLogger()->Info("CParticle::CreateTrack() stub!\n");
+ // TODO!
+ return 0;
+}
+
+void Gfx::CParticle::CreateWheelTrace(const Math::Vector &p1, const Math::Vector &p2, const Math::Vector &p3,
+ const Math::Vector &p4, Gfx::ParticleType type)
+{
+ GetLogger()->Info("CParticle::CreateWheelTrace() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DeleteParticle(Gfx::ParticleType type)
+{
+ GetLogger()->Info("CParticle::DeleteParticle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DeleteParticle(int channel)
+{
+ GetLogger()->Info("CParticle::DeleteParticle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetObjectLink(int channel, CObject *object)
+{
+ GetLogger()->Info("CParticle::SetObjectLink() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetObjectFather(int channel, CObject *object)
+{
+ GetLogger()->Info("CParticle::SetObjectFather() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetPosition(int channel, Math::Vector pos)
+{
+ GetLogger()->Info("CParticle::SetPosition() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetDimension(int channel, Math::Point dim)
+{
+ GetLogger()->Info("CParticle::SetDimension() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetZoom(int channel, float zoom)
+{
+ GetLogger()->Info("CParticle::SetZoom() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetAngle(int channel, float angle)
+{
+ GetLogger()->Info("CParticle::SetAngle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetIntensity(int channel, float intensity)
+{
+ GetLogger()->Info("CParticle::SetIntensity() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetParam(int channel, Math::Vector pos, Math::Point dim, float zoom, float angle, float intensity)
+{
+ GetLogger()->Info("CParticle::SetParam() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetPhase(int channel, Gfx::ParticlePhase phase, float duration)
+{
+ GetLogger()->Info("CParticle::SetPhase() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CParticle::GetPosition(int channel, Math::Vector &pos)
+{
+ GetLogger()->Info("CParticle::GetPosition() stub!\n");
+ // TODO!
+ return true;
+}
+
+Gfx::Color Gfx::CParticle::GetFogColor(Math::Vector pos)
+{
+ GetLogger()->Info("CParticle::GetFogColor() stub!\n");
+ // TODO!
+ return Gfx::Color();
+}
+
+void Gfx::CParticle::SetFrameUpdate(int sheet, bool update)
+{
+ GetLogger()->Info("CParticle::SetFrameUpdate() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::FrameParticle(float rTime)
+{
+ GetLogger()->Info("CParticle::FrameParticle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticle(int sheet)
+{
+ GetLogger()->Info("CParticle::DrawParticle() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CParticle::WriteWheelTrace(char *filename, int width, int height, Math::Vector dl, Math::Vector ur)
+{
+ GetLogger()->Info("CParticle::WriteWheelTrace() stub!\n");
+ // TODO!
+ return true;
+}
+
+void Gfx::CParticle::DeleteRank(int rank)
+{
+ GetLogger()->Info("CParticle::DeleteRank() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CParticle::CheckChannel(int &channel)
+{
+ GetLogger()->Info("CParticle::CheckChannel() stub!\n");
+ // TODO!
+ return true;
+}
+
+void Gfx::CParticle::DrawParticleTriangle(int i)
+{
+ GetLogger()->Info("CParticle::DrawParticleTriangle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleNorm(int i)
+{
+ GetLogger()->Info("CParticle::DrawParticleNorm() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleFlat(int i)
+{
+ GetLogger()->Info("CParticle::DrawParticleFlat() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleFog(int i)
+{
+ GetLogger()->Info("CParticle::DrawParticleFog() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleRay(int i)
+{
+ GetLogger()->Info("CParticle::DrawParticleRay() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleSphere(int i)
+{
+ GetLogger()->Info("CParticle::DrawParticleSphere() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleCylinder(int i)
+{
+ GetLogger()->Info("CParticle::DrawParticleCylinder() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleWheel(int i)
+{
+ GetLogger()->Info("CParticle::DrawParticleWheel() stub!\n");
+ // TODO!
+}
+
+CObject* Gfx::CParticle::SearchObjectGun(Math::Vector old, Math::Vector pos, Gfx::ParticleType type, CObject *father)
+{
+ GetLogger()->Info("CParticle::SearchObjectGun() stub!\n");
+ // TODO!
+ return nullptr;
+}
+
+CObject* Gfx::CParticle::SearchObjectRay(Math::Vector pos, Math::Vector goal, Gfx::ParticleType type, CObject *father)
+{
+ GetLogger()->Info("CParticle::SearchObjectRay() stub!\n");
+ // TODO!
+ return nullptr;
+}
+
+void Gfx::CParticle::Play(Sound sound, Math::Vector pos, float amplitude)
+{
+ GetLogger()->Info("CParticle::Play() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CParticle::TrackMove(int i, Math::Vector pos, float progress)
+{
+ GetLogger()->Info("CParticle::TrackMove() stub!\n");
+ // TODO!
+ return true;
+}
+
+void Gfx::CParticle::TrackDraw(int i, Gfx::ParticleType type)
+{
+ GetLogger()->Info("CParticle::TrackDraw() stub!\n");
+ // TODO!
+}
diff --git a/src/graphics/engine/particle.h b/src/graphics/engine/particle.h
index bd9741f..45396d2 100644
--- a/src/graphics/engine/particle.h
+++ b/src/graphics/engine/particle.h
@@ -15,7 +15,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// particle.h (aka particule.h)
+/**
+ * \file graphics/engine/particle.h
+ * \brief Particle rendering - Gfx::CParticle class (aka particule)
+ */
#pragma once
@@ -202,26 +205,26 @@ enum ParticlePhase
struct Particle
{
- char bUsed; // TRUE -> particle used
- char bRay; // TRUE -> ray with goal
- unsigned short uniqueStamp; // unique mark
+ char used; // TRUE -> particle used
+ char ray; // TRUE -> ray with goal
+ unsigned short uniqueStamp; // unique mark
short sheet; // sheet (0..n)
- ParticleType type; // type PARTI*
- ParticlePhase phase; // phase PARPH*
+ ParticleType type; // type PARTI*
+ ParticlePhase phase; // phase PARPH*
float mass; // mass of the particle (in rebounding)
float weight; // weight of the particle (for noise)
float duration; // length of life
- Math::Vector pos; // absolute position (relative if object links)
- Math::Vector goal; // goal position (if bRay)
- Math::Vector speed; // speed of displacement
+ Math::Vector pos; // absolute position (relative if object links)
+ Math::Vector goal; // goal position (if ray)
+ Math::Vector speed; // speed of displacement
float windSensitivity;
short bounce; // number of rebounds
- Math::Point dim; // dimensions of the rectangle
+ Math::Point dim; // dimensions of the rectangle
float zoom; // zoom (0..1)
float angle; // angle of rotation
float intensity; // intensity
- Math::Point texSup; // coordinated upper texture
- Math::Point texInf; // coordinated lower texture
+ Math::Point texSup; // coordinated upper texture
+ Math::Point texInf; // coordinated lower texture
float time; // age of the particle (0..n)
float phaseTime; // age at the beginning of phase
float testTime; // time since last test
@@ -233,22 +236,22 @@ struct Particle
struct Track
{
- char bUsed; // TRUE -> drag used
- char bDrawParticle;
+ char used; // TRUE -> drag used
+ char drawParticle;
float step; // duration of not
float last; // increase last not memorized
float intensity; // intensity at starting (0..1)
float width; // tail width
- int used; // number of positions in "pos"
- int head; // head to write index
- Math::Vector pos[MAXTRACKLEN];
+ int posUsed; // number of positions in "pos"
+ int head; // head to write index
+ Math::Vector pos[MAXTRACKLEN];
float len[MAXTRACKLEN];
};
struct WheelTrace
{
- ParticleType type; // type PARTI*
- Math::Vector pos[4]; // rectangle positions
+ ParticleType type; // type PARTI*
+ Math::Vector pos[4]; // rectangle positions
float startTime; // beginning of life
};
@@ -257,20 +260,29 @@ struct WheelTrace
class CParticle
{
public:
- CParticle(CInstanceManager* iMan, CEngine* engine);
+ CParticle(CInstanceManager* iMan, Gfx::CEngine* engine);
~CParticle();
- void SetGLDevice(CDevice device);
+ void SetDevice(Gfx::CDevice* device);
void FlushParticle();
void FlushParticle(int sheet);
- int CreateParticle(Math::Vector pos, Math::Vector speed, Math::Point dim, ParticleType type, float duration=1.0f, float mass=0.0f, float windSensitivity=1.0f, int sheet=0);
- int CreateFrag(Math::Vector pos, Math::Vector speed, Gfx::EngineTriangle *triangle, ParticleType type, float duration=1.0f, float mass=0.0f, float windSensitivity=1.0f, int sheet=0);
- int CreatePart(Math::Vector pos, Math::Vector speed, ParticleType type, float duration=1.0f, float mass=0.0f, float weight=0.0f, float windSensitivity=1.0f, int sheet=0);
- int CreateRay(Math::Vector pos, Math::Vector goal, ParticleType type, Math::Point dim, float duration=1.0f, int sheet=0);
- int CreateTrack(Math::Vector pos, Math::Vector speed, Math::Point dim, ParticleType type, float duration=1.0f, float mass=0.0f, float length=10.0f, float width=1.0f);
- void CreateWheelTrace(const Math::Vector &p1, const Math::Vector &p2, const Math::Vector &p3, const Math::Vector &p4, ParticleType type);
- void DeleteParticle(ParticleType type);
+ int CreateParticle(Math::Vector pos, Math::Vector speed, Math::Point dim,
+ Gfx::ParticleType type, float duration=1.0f, float mass=0.0f,
+ float windSensitivity=1.0f, int sheet=0);
+ int CreateFrag(Math::Vector pos, Math::Vector speed, Gfx::EngineTriangle *triangle,
+ Gfx::ParticleType type, float duration=1.0f, float mass=0.0f,
+ float windSensitivity=1.0f, int sheet=0);
+ int CreatePart(Math::Vector pos, Math::Vector speed, Gfx::ParticleType type,
+ float duration=1.0f, float mass=0.0f, float weight=0.0f,
+ float windSensitivity=1.0f, int sheet=0);
+ int CreateRay(Math::Vector pos, Math::Vector goal, Gfx::ParticleType type, Math::Point dim,
+ float duration=1.0f, int sheet=0);
+ int CreateTrack(Math::Vector pos, Math::Vector speed, Math::Point dim, Gfx::ParticleType type,
+ float duration=1.0f, float mass=0.0f, float length=10.0f, float width=1.0f);
+ void CreateWheelTrace(const Math::Vector &p1, const Math::Vector &p2, const Math::Vector &p3,
+ const Math::Vector &p4, Gfx::ParticleType type);
+ void DeleteParticle(Gfx::ParticleType type);
void DeleteParticle(int channel);
void SetObjectLink(int channel, CObject *object);
void SetObjectFather(int channel, CObject *object);
@@ -280,12 +292,12 @@ public:
void SetAngle(int channel, float angle);
void SetIntensity(int channel, float intensity);
void SetParam(int channel, Math::Vector pos, Math::Point dim, float zoom, float angle, float intensity);
- void SetPhase(int channel, ParticlePhase phase, float duration);
+ void SetPhase(int channel, Gfx::ParticlePhase phase, float duration);
bool GetPosition(int channel, Math::Vector &pos);
- Gfx::Color RetFogColor(Math::Vector pos);
+ Gfx::Color GetFogColor(Math::Vector pos);
- void SetFrameUpdate(int sheet, bool bUpdate);
+ void SetFrameUpdate(int sheet, bool update);
void FrameParticle(float rTime);
void DrawParticle(int sheet);
@@ -302,29 +314,29 @@ protected:
void DrawParticleSphere(int i);
void DrawParticleCylinder(int i);
void DrawParticleWheel(int i);
- CObject* SearchObjectGun(Math::Vector old, Math::Vector pos, ParticleType type, CObject *father);
- CObject* SearchObjectRay(Math::Vector pos, Math::Vector goal, ParticleType type, CObject *father);
+ CObject* SearchObjectGun(Math::Vector old, Math::Vector pos, Gfx::ParticleType type, CObject *father);
+ CObject* SearchObjectRay(Math::Vector pos, Math::Vector goal, Gfx::ParticleType type, CObject *father);
void Play(Sound sound, Math::Vector pos, float amplitude);
bool TrackMove(int i, Math::Vector pos, float progress);
- void TrackDraw(int i, ParticleType type);
+ void TrackDraw(int i, Gfx::ParticleType type);
protected:
CInstanceManager* m_iMan;
- CEngine* m_engine;
- CDevice* m_pDevice;
- CRobotMain* m_main;
- CTerrain* m_terrain;
- CWater* m_water;
- CSound* m_sound;
+ Gfx::CEngine* m_engine;
+ Gfx::CDevice* m_device;
+ Gfx::CTerrain* m_terrain;
+ Gfx::CWater* m_water;
+ CRobotMain* m_main;
+ CSound* m_sound;
Gfx::Particle m_particule[MAXPARTICULE*MAXPARTITYPE];
Gfx::EngineTriangle m_triangle[MAXPARTICULE]; // triangle if PartiType == 0
- Track m_track[MAXTRACK];
+ Gfx::Track m_track[MAXTRACK];
int m_wheelTraceTotal;
int m_wheelTraceIndex;
- WheelTrace m_wheelTrace[MAXWHEELTRACE];
+ Gfx::WheelTrace m_wheelTrace[MAXWHEELTRACE];
int m_totalInterface[MAXPARTITYPE][SH_MAX];
- bool m_bFrameUpdate[SH_MAX];
+ bool m_frameUpdate[SH_MAX];
int m_fogTotal;
int m_fog[MAXPARTIFOG];
int m_uniqueStamp;
diff --git a/src/graphics/engine/planet.cpp b/src/graphics/engine/planet.cpp
index 4f1f614..022de66 100644
--- a/src/graphics/engine/planet.cpp
+++ b/src/graphics/engine/planet.cpp
@@ -19,5 +19,167 @@
#include "graphics/engine/planet.h"
+#include "common/iman.h"
+#include "graphics/core/device.h"
+#include "graphics/engine/engine.h"
-// TODO implementation
+
+const int PLANET_PREALLOCATE_COUNT = 10;
+
+
+Gfx::CPlanet::CPlanet(CInstanceManager* iMan, Gfx::CEngine* engine)
+{
+ m_iMan = iMan;
+ m_iMan->AddInstance(CLASS_PLANET, this);
+
+ m_planet[0].reserve(PLANET_PREALLOCATE_COUNT);
+ m_planet[1].reserve(PLANET_PREALLOCATE_COUNT);
+
+ m_engine = engine;
+ Flush();
+
+}
+
+Gfx::CPlanet::~CPlanet()
+{
+ m_iMan = nullptr;
+}
+
+void Gfx::CPlanet::Flush()
+{
+ for (int j = 0; j < 2; j++)
+ m_planet[j].clear();
+
+ m_planetExist = false;
+ m_mode = 0;
+ m_time = 0.0f;
+}
+
+bool Gfx::CPlanet::EventProcess(const Event &event)
+{
+ if (event.type == EVENT_FRAME)
+ return EventFrame(event);
+
+ return true;
+}
+
+bool Gfx::CPlanet::EventFrame(const Event &event)
+{
+ if (m_engine->GetPause()) return true;
+
+ m_time += event.rTime;
+
+ for (int i = 0; i < static_cast<int>( m_planet[m_mode].size() ); i++)
+ {
+ float a = m_time*m_planet[m_mode][i].speed;
+ if (a < 0.0f)
+ a += Math::PI*1000.0f;
+
+ m_planet[m_mode][i].angle.x = a+m_planet[m_mode][i].start.x;
+ m_planet[m_mode][i].angle.y = sinf(a)*sinf(m_planet[m_mode][i].dir)+m_planet[m_mode][i].start.y;
+ }
+
+ return true;
+}
+
+void Gfx::CPlanet::LoadTexture()
+{
+ for (int j = 0; j < 2; j++)
+ {
+ for (int i = 0; i < static_cast<int>( m_planet[j].size() ); i++)
+ {
+ m_engine->LoadTexture(m_planet[j][i].name);
+ }
+ }
+}
+
+void Gfx::CPlanet::Draw()
+{
+ Gfx::CDevice* device = m_engine->GetDevice();
+ float eyeDirH = m_engine->GetEyeDirH();
+ float eyeDirV = m_engine->GetEyeDirV();
+
+ Math::Vector n = Math::Vector(0.0f, 0.0f, -1.0f); // normal
+ float dp = 0.5f/256.0f;
+
+ for (int i = 0; i < static_cast<int>( m_planet[m_mode].size() ); i++)
+ {
+ m_engine->SetTexture(m_planet[m_mode][i].name);
+
+ if (m_planet[m_mode][i].transparent)
+ m_engine->SetState(Gfx::ENG_RSTATE_WRAP | Gfx::ENG_RSTATE_ALPHA);
+ else
+ m_engine->SetState(Gfx::ENG_RSTATE_WRAP | Gfx::ENG_RSTATE_TTEXTURE_BLACK);
+
+ Math::Point p1, p2;
+
+ float a = eyeDirH + m_planet[m_mode][i].angle.x;
+ p1.x = Math::Mod(a, Math::PI*2.0f)-0.5f;
+
+ a = eyeDirV + m_planet[m_mode][i].angle.y;
+ p1.y = 0.4f+(Math::Mod(a+Math::PI, Math::PI*2.0f)-Math::PI)*(2.0f/Math::PI);
+
+ p1.x -= m_planet[m_mode][i].dim/2.0f*0.75f;
+ p1.y -= m_planet[m_mode][i].dim/2.0f;
+ p2.x = p1.x+m_planet[m_mode][i].dim*0.75f;
+ p2.y = p1.y+m_planet[m_mode][i].dim;
+
+ float u1 = m_planet[m_mode][i].uv1.x + dp;
+ float v1 = m_planet[m_mode][i].uv1.y + dp;
+ float u2 = m_planet[m_mode][i].uv2.x - dp;
+ float v2 = m_planet[m_mode][i].uv2.y - dp;
+
+ Gfx::Vertex quad[4] =
+ {
+ Gfx::Vertex(Math::Vector(p1.x, p1.y, 0.0f), n, Math::Point(u1, v2)),
+ Gfx::Vertex(Math::Vector(p1.x, p2.y, 0.0f), n, Math::Point(u1, v1)),
+ Gfx::Vertex(Math::Vector(p2.x, p1.y, 0.0f), n, Math::Point(u2, v2)),
+ Gfx::Vertex(Math::Vector(p2.x, p2.y, 0.0f), n, Math::Point(u2, v1))
+ };
+
+ device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, quad, 4);
+ m_engine->AddStatisticTriangle(2);
+ }
+}
+
+void Gfx::CPlanet::Create(int mode, Math::Point start, float dim, float speed,
+ float dir, const std::string& name, Math::Point uv1, Math::Point uv2)
+{
+ if (mode < 0) mode = 0;
+ if (mode > 1) mode = 1;
+
+ Gfx::Planet planet;
+
+ planet.start = start;
+ planet.angle = start;
+ planet.dim = dim;
+ planet.speed = speed;
+ planet.dir = dir;
+
+ planet.name = name;
+ planet.uv1 = uv1;
+ planet.uv2 = uv2;
+
+ planet.transparent = planet.name.find("planet") != std::string::npos;
+
+ m_planet[mode].push_back(planet);
+
+ m_planetExist = true;
+}
+
+bool Gfx::CPlanet::PlanetExist()
+{
+ return m_planetExist;
+}
+
+void Gfx::CPlanet::SetMode(int mode)
+{
+ if (mode < 0) mode = 0;
+ if (mode > 1) mode = 1;
+ m_mode = mode;
+}
+
+int Gfx::CPlanet::GetMode()
+{
+ return m_mode;
+}
diff --git a/src/graphics/engine/planet.h b/src/graphics/engine/planet.h
index 264d05c..54d8b55 100644
--- a/src/graphics/engine/planet.h
+++ b/src/graphics/engine/planet.h
@@ -15,13 +15,18 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// planet.h
+/**
+ * \file graphics/engine/planet.h
+ * \brief Planet rendering - Gfx::CPlanet class
+ */
#pragma once
#include "common/event.h"
#include "math/point.h"
+#include <vector>
+
class CInstanceManager;
@@ -30,51 +35,69 @@ namespace Gfx {
class CEngine;
-
-const short MAXPLANET = 10;
-
struct Planet
{
- char bUsed; // TRUE -> planet exists
- Math::Point start; // initial position in degrees
- Math::Point angle; // current position in degrees
- float dim; // dimensions (0..1)
- float speed; // speed
- float dir; // direction in the sky
- char name[20]; // name of the texture
- Math::Point uv1, uv2; // texture mapping
- char bTGA; // texture .TGA
+ //! Initial position in degrees
+ Math::Point start;
+ //! Current position in degrees
+ Math::Point angle;
+ //! Dimensions (0..1)
+ float dim;
+ //! Speed
+ float speed;
+ //! Direction in the sky
+ float dir;
+ //! Name of the texture
+ std::string name;
+ //! Texture mapping
+ Math::Point uv1, uv2;
+ //! Transparent texture
+ bool transparent;
+
+ Planet()
+ {
+ dim = speed = dir = 0.0f;
+ transparent = false;
+ }
};
-
-
-
-class CPlanet {
+class CPlanet
+{
public:
- CPlanet(CInstanceManager* iMan, CEngine* engine);
+ CPlanet(CInstanceManager* iMan, Gfx::CEngine* engine);
~CPlanet();
+ //! Removes all the planets
void Flush();
+ //! Management of an event
bool EventProcess(const Event &event);
- bool Create(int mode, Math::Point start, float dim, float speed, float dir, char *name, Math::Point uv1, Math::Point uv2);
+ //! Creates a new planet
+ void Create(int mode, Math::Point start, float dim, float speed, float dir,
+ const std::string& name, Math::Point uv1, Math::Point uv2);
+ //! Indicates if there is at least one planet
bool PlanetExist();
+ //! Load all the textures for the planets
void LoadTexture();
+ //! Draws all the planets
void Draw();
+ //@{
+ //! Choice of mode
void SetMode(int mode);
- int RetMode();
+ int GetMode();
+ //@}
protected:
+ //! Makes the planets evolve
bool EventFrame(const Event &event);
protected:
- CInstanceManager* m_iMan;
- CEngine* m_engine;
+ CInstanceManager* m_iMan;
+ Gfx::CEngine* m_engine;
- float m_time;
- int m_mode;
- Planet m_planet[2][MAXPLANET];
- bool m_bPlanetExist;
+ float m_time;
+ int m_mode;
+ std::vector<Gfx::Planet> m_planet[2];
+ bool m_planetExist;
};
-
}; // namespace Gfx
diff --git a/src/graphics/engine/pyro.cpp b/src/graphics/engine/pyro.cpp
index e699db2..327befa 100644
--- a/src/graphics/engine/pyro.cpp
+++ b/src/graphics/engine/pyro.cpp
@@ -19,5 +19,161 @@
#include "graphics/engine/pyro.h"
+#include "common/logger.h"
-// TODO implementation
+
+Gfx::CPyro::CPyro(CInstanceManager* iMan)
+{
+ GetLogger()->Info("CParticle::CPyro() stub!\n");
+ // TODO!
+}
+
+Gfx::CPyro::~CPyro()
+{
+ GetLogger()->Info("CPyro::~CPyro() stub!");
+ // TODO!
+}
+
+void Gfx::CPyro::DeleteObject(bool all)
+{
+ GetLogger()->Info("CPyro::DeleteObject() stub!");
+ // TODO!
+}
+
+bool Gfx::CPyro::Create(Gfx::PyroType type, CObject* pObj, float force)
+{
+ GetLogger()->Info("CPyro::Create() stub!");
+ // TODO!
+ return true;
+}
+
+bool Gfx::CPyro::EventProcess(const Event &event)
+{
+ GetLogger()->Info("CPyro::EventProcess() stub!\n");
+ // TODO!
+ return true;
+}
+
+Error Gfx::CPyro::IsEnded()
+{
+ GetLogger()->Info("CPyro::IsEnded() stub!\n");
+ // TODO!
+ return ERR_OK;
+}
+
+void Gfx::CPyro::CutObjectLink(CObject* pObj)
+{
+ GetLogger()->Info("CPyro::CutObjectLink() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::DisplayError(PyroType type, CObject* pObj)
+{
+ GetLogger()->Info("CPyro::DisplayError() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CPyro::CreateLight(Math::Vector pos, float height)
+{
+ GetLogger()->Info("CPyro::CreateLight() stub!\n");
+ // TODO!
+ return true;
+}
+
+void Gfx::CPyro::DeleteObject(bool primary, bool secondary)
+{
+ GetLogger()->Info("CPyro::DeleteObject() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::CreateTriangle(CObject* pObj, ObjectType oType, int part)
+{
+ GetLogger()->Info("CPyro::CreateTriangle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::ExploStart()
+{
+ GetLogger()->Info("CPyro::ExploStart() stub!\n");
+ // TODO!
+}
+void Gfx::CPyro::ExploTerminate()
+{
+ GetLogger()->Info("CPyro::ExploTerminate() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::BurnStart()
+{
+ GetLogger()->Info("CPyro::BurnStart() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::BurnAddPart(int part, Math::Vector pos, Math::Vector angle)
+{
+ GetLogger()->Info("CPyro::BurnAddPart() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::BurnProgress()
+{
+ GetLogger()->Info("CPyro::BurnProgress() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CPyro::BurnIsKeepPart(int part)
+{
+ GetLogger()->Info("CPyro::BurnIsKeepPart() stub!\n");
+ // TODO!
+ return true;
+}
+
+void Gfx::CPyro::BurnTerminate()
+{
+ GetLogger()->Info("CPyro::BurnTerminate() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::FallStart()
+{
+ GetLogger()->Info("CPyro::FallStart() stub!\n");
+ // TODO!
+}
+
+CObject* Gfx::CPyro::FallSearchBeeExplo()
+{
+ GetLogger()->Info("CPyro::FallSearchBeeExplo() stub!\n");
+ // TODO!
+ return nullptr;
+}
+
+void Gfx::CPyro::FallProgress(float rTime)
+{
+ GetLogger()->Info("CPyro::FallProgress() stub!\n");
+ // TODO!
+}
+
+Error Gfx::CPyro::FallIsEnded()
+{
+ GetLogger()->Info("CPyro::FallIsEnded() stub!\n");
+ // TODO!
+ return ERR_OK;
+}
+
+void Gfx::CPyro::LightOperFlush()
+{
+ GetLogger()->Info("CPyro::LightOperFlush() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::LightOperAdd(float progress, float intensity, float r, float g, float b)
+{
+ GetLogger()->Info("CPyro::LightOperAdd() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::LightOperFrame(float rTime)
+{
+ GetLogger()->Info("CPyro::LightOperFrame() stub!\n");
+ // TODO!
+}
diff --git a/src/graphics/engine/pyro.h b/src/graphics/engine/pyro.h
index d663ca5..768abf8 100644
--- a/src/graphics/engine/pyro.h
+++ b/src/graphics/engine/pyro.h
@@ -15,15 +15,16 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// pyro.h
+/**
+ * \file graphics/engine/pyro.h
+ * \brief Fire effect rendering - Gfx::CPyro class
+ */
#pragma once
#include "common/misc.h"
#include "graphics/engine/engine.h"
-//#include "object/object.h"
-// TEMPORARILY!
-enum ObjectType {};
+#include "object/object.h"
class CInstanceManager;
@@ -90,13 +91,14 @@ struct PyroLightOper
-class CPyro {
+class CPyro
+{
public:
CPyro(CInstanceManager* iMan);
~CPyro();
- void DeleteObject(bool bAll=false);
- bool Create(PyroType type, CObject* pObj, float force=1.0f);
+ void DeleteObject(bool all=false);
+ bool Create(Gfx::PyroType type, CObject* pObj, float force=1.0f);
bool EventProcess(const Event &event);
Error IsEnded();
void CutObjectLink(CObject* pObj);
@@ -104,7 +106,7 @@ public:
protected:
void DisplayError(PyroType type, CObject* pObj);
bool CreateLight(Math::Vector pos, float height);
- void DeleteObject(bool bPrimary, bool bSecondary);
+ void DeleteObject(bool primary, bool secondary);
void CreateTriangle(CObject* pObj, ObjectType oType, int part);
@@ -127,21 +129,21 @@ protected:
void LightOperFrame(float rTime);
protected:
- CInstanceManager* m_iMan;
- CEngine* m_engine;
- CTerrain* m_terrain;
- CCamera* m_camera;
- CParticle* m_particule;
- CLight* m_light;
- CObject* m_object;
- CDisplayText* m_displayText;
- CRobotMain* m_main;
- CSound* m_sound;
-
- Math::Vector m_pos; // center of the effect
- Math::Vector m_posPower; // center of the battery
- bool m_bPower; // battery exists?
- PyroType m_type;
+ CInstanceManager* m_iMan;
+ Gfx::CEngine* m_engine;
+ Gfx::CTerrain* m_terrain;
+ Gfx::CCamera* m_camera;
+ Gfx::CParticle* m_particule;
+ Gfx::CLightManager* m_lightMan;
+ CObject* m_object;
+ CDisplayText* m_displayText;
+ CRobotMain* m_main;
+ CSound* m_sound;
+
+ Math::Vector m_pos; // center of the effect
+ Math::Vector m_posPower; // center of the battery
+ bool m_power; // battery exists?
+ Gfx::PyroType m_type;
float m_force;
float m_size;
float m_progress;
@@ -153,22 +155,22 @@ protected:
int m_lightRank;
int m_lightOperTotal;
- PyroLightOper m_lightOper[10];
+ Gfx::PyroLightOper m_lightOper[10];
float m_lightHeight;
ObjectType m_burnType;
int m_burnPartTotal;
- PyroBurnPart m_burnPart[10];
+ Gfx::PyroBurnPart m_burnPart[10];
int m_burnKeepPart[10];
float m_burnFall;
float m_fallFloor;
float m_fallSpeed;
float m_fallBulletTime;
- bool m_bFallEnding;
+ bool m_fallEnding;
int m_crashSphereUsed; // number of spheres used
- Math::Vector m_crashSpherePos[50];
+ Math::Vector m_crashSpherePos[50];
float m_crashSphereRadius[50];
};
diff --git a/src/graphics/engine/terrain.cpp b/src/graphics/engine/terrain.cpp
index c489321..6d4fcd3 100644
--- a/src/graphics/engine/terrain.cpp
+++ b/src/graphics/engine/terrain.cpp
@@ -19,5 +19,1805 @@
#include "graphics/engine/terrain.h"
+#include "app/app.h"
+#include "common/iman.h"
+#include "common/image.h"
+#include "common/logger.h"
+#include "graphics/engine/engine.h"
+#include "graphics/engine/water.h"
+#include "math/geometry.h"
-// TODO implementation
+#include <sstream>
+
+#include <SDL/SDL.h>
+
+const int LEVEL_MAT_PREALLOCATE_COUNT = 101;
+const int FLYING_LIMIT_PREALLOCATE_COUNT = 10;
+const int BUILDING_LEVEL_PREALLOCATE_COUNT = 101;
+
+
+Gfx::CTerrain::CTerrain(CInstanceManager* iMan)
+{
+ m_iMan = iMan;
+ m_iMan->AddInstance(CLASS_TERRAIN, this);
+
+ m_engine = static_cast<Gfx::CEngine*>( m_iMan->SearchInstance(CLASS_ENGINE) );
+ m_water = static_cast<Gfx::CWater*>( m_iMan->SearchInstance(CLASS_WATER) );
+
+ m_mosaic = 20;
+ m_brick = 1 << 4;
+ m_size = 10.0f;
+ m_vision = 200.0f;
+ m_scaleMapping = 0.01f;
+ m_scaleRelief = 1.0f;
+ m_subdivMapping = 1;
+ m_depth = 2;
+ m_levelMatMax = 0;
+ m_multiText = true;
+ m_levelText = false;
+ m_wind = Math::Vector(0.0f, 0.0f, 0.0f);
+ m_defHardness = 0.5f;
+
+ m_levelMat.reserve(LEVEL_MAT_PREALLOCATE_COUNT);
+ m_flyingLimits.reserve(FLYING_LIMIT_PREALLOCATE_COUNT);
+ m_buildingLevels.reserve(BUILDING_LEVEL_PREALLOCATE_COUNT);
+}
+
+Gfx::CTerrain::~CTerrain()
+{
+}
+
+/**
+ The terrain is composed of mosaics, themselves composed of bricks.
+ Each brick is composed of two triangles.
+ mosaic: number of mosaics along the axes X and Z
+ brick: number of bricks (power of 2)
+ size: size of a brick along the axes X and Z
+ vision: vision before a change of resolution
+ scaleMapping: scale textures for mapping
+
+\verbatim
+ ^ z
+ | <---> brick*size
+ +---+---+---+---+
+ | | | |_|_| mosaic = 4
+ | | | | | | brick = 2 (brickP2=1)
+ +---+---+---+---+
+ |\ \| | | |
+ |\ \| | | |
+ +---+---o---+---+---> x
+ | | | | |
+ | | | | |
+ +---+---+---+---+
+ | | | | | The land is viewed from above here.
+ | | | | |
+ +---+---+---+---+
+ <---------------> mosaic*brick*size
+\endverbatim */
+bool Gfx::CTerrain::Generate(int mosaic, int brickPow2, float size, float vision,
+ int depth, float hardness)
+{
+ m_mosaic = mosaic;
+ m_brick = 1 << brickPow2;
+ m_size = size;
+ m_vision = vision;
+ m_depth = depth;
+ m_defHardness = hardness;
+
+ m_engine->SetTerrainVision(vision);
+
+ m_multiText = true;
+ m_levelText = false;
+ m_scaleMapping = 1.0f / (m_brick*m_size);
+ m_subdivMapping = 1;
+
+ int dim = 0;
+
+ dim = (m_mosaic*m_brick+1)*(m_mosaic*m_brick+1);
+ std::vector<float>(dim).swap(m_relief);
+
+ dim = m_mosaic*m_subdivMapping*m_mosaic*m_subdivMapping;
+ std::vector<int>(dim).swap(m_texture);
+
+ dim = m_mosaic*m_mosaic;
+ std::vector<int>(dim).swap(m_objRank);
+
+ return true;
+}
+
+
+int Gfx::CTerrain::GetMosaic()
+{
+ return m_mosaic;
+}
+
+int Gfx::CTerrain::GetBrick()
+{
+ return m_brick;
+}
+
+float Gfx::CTerrain::GetSize()
+{
+ return m_size;
+}
+
+float Gfx::CTerrain::GetScaleRelief()
+{
+ return m_scaleRelief;
+}
+
+bool Gfx::CTerrain::InitTextures(const std::string& baseName, int* table, int dx, int dy)
+{
+ m_levelText = false;
+ m_texBaseName = baseName;
+ size_t pos = baseName.find('.');
+ if (pos == baseName.npos)
+ {
+ m_texBaseExt = ".png";
+ }
+ else
+ {
+ m_texBaseName = m_texBaseName.substr(0, pos);
+ m_texBaseExt = m_texBaseName.substr(pos);
+ }
+
+ for (int y = 0; y < m_mosaic*m_subdivMapping; y++)
+ {
+ for (int x = 0; x < m_mosaic*m_subdivMapping; x++)
+ {
+ m_texture[x+y*m_mosaic] = table[(x%dx)+(y%dy)*dx];
+ }
+ }
+ return true;
+}
+
+
+void Gfx::CTerrain::LevelFlush()
+{
+ m_levelMat.clear();
+ m_levelMatMax = 0;
+ m_levelID = 1000;
+ LevelCloseTable();
+}
+
+void Gfx::CTerrain::LevelMaterial(int id, std::string& baseName, float u, float v,
+ int up, int right, int down, int left,
+ float hardness)
+{
+ LevelOpenTable();
+
+ if (id == 0)
+ id = m_levelID++; // puts an ID internal standard
+
+ Gfx::TerrainMaterial tm;
+ tm.texName = baseName;
+ tm.id = id;
+ tm.u = u;
+ tm.v = v;
+ tm.mat[0] = up;
+ tm.mat[1] = right;
+ tm.mat[2] = down;
+ tm.mat[3] = left;
+ tm.hardness = hardness;
+
+ m_levelMat.push_back(tm);
+
+ if (m_levelMatMax < up+1 ) m_levelMatMax = up+1;
+ if (m_levelMatMax < right+1) m_levelMatMax = right+1;
+ if (m_levelMatMax < down+1 ) m_levelMatMax = down+1;
+ if (m_levelMatMax < left+1 ) m_levelMatMax = left+1;
+
+ m_levelText = true;
+ m_subdivMapping = 4;
+}
+
+
+/**
+ The size of the image must be dimension dx and dy with dx=dy=(mosaic*brick)+1.
+ The image must be 24 bits/pixel
+
+ Converts coordinated image (x;y) -> world (x;-;z) :
+ Wx = 5*Ix-400
+ Wz = -(5*Iy-400)
+
+ Converts coordinated world (x;-;z) -> image (x;y) :
+ Ix = (400+Wx)/5
+ Iy = (400-Wz)/5 */
+bool Gfx::CTerrain::ResFromPNG(const std::string& fileName)
+{
+ CImage img;
+ if (! img.Load(CApplication::GetInstance().GetDataFilePath(m_engine->GetTextureDir(), fileName)))
+ return false;
+
+ ImageData *data = img.GetData();
+
+ int size = (m_mosaic*m_brick)+1;
+
+ m_resources.clear();
+
+ std::vector<unsigned char>(3*size*size).swap(m_resources);
+
+ if ( (data->surface->w != size) || (data->surface->h != size) ||
+ (data->surface->format->BytesPerPixel != 3) )
+ return false;
+
+ // Assuming the data format is compatible
+ memcpy(&m_resources[0], data->surface->pixels, 3*size*size);
+
+ return true;
+}
+
+Gfx::TerrainRes Gfx::CTerrain::GetResource(const Math::Vector &p)
+{
+ if (m_resources.empty())
+ return Gfx::TR_NULL;
+
+ int x = static_cast<int>((p.x + (m_mosaic*m_brick*m_size)/2.0f)/m_size);
+ int y = static_cast<int>((p.z + (m_mosaic*m_brick*m_size)/2.0f)/m_size);
+
+ if ( x < 0 || x > m_mosaic*m_brick ||
+ y < 0 || y > m_mosaic*m_brick )
+ return Gfx::TR_NULL;
+
+ int size = (m_mosaic*m_brick)+1;
+
+ int resR = m_resources[3*x+3*size*(size-y-1)];
+ int resG = m_resources[3*x+3*size*(size-y-1)+1];
+ int resB = m_resources[3*x+3*size*(size-y-1)+2];
+
+ if (resR == 255 && resG == 0 && resB == 0) return Gfx::TR_STONE;
+ if (resR == 255 && resG == 255 && resB == 0) return Gfx::TR_URANIUM;
+ if (resR == 0 && resG == 255 && resB == 0) return Gfx::TR_POWER;
+
+ // TODO key res values
+ //if (ress == 24) return Gfx::TR_KEY_A; // ~green?
+ //if (ress == 25) return Gfx::TR_KEY_B; // ~green?
+ //if (ress == 26) return Gfx::TR_KEY_C; // ~green?
+ //if (ress == 27) return Gfx::TR_KEY_D; // ~green?
+
+ return TR_NULL;
+}
+
+void Gfx::CTerrain::FlushRelief()
+{
+ m_relief.clear();
+}
+
+/**
+ The size of the image must be dimension dx and dy with dx=dy=(mosaic*brick)+1.
+ The image must be 24 bits/pixel, but gray scale:
+ white = ground (y=0)
+ black = mountain (y=255*scaleRelief)
+
+ Converts coordinated image(x;y) -> world (x;-;z) :
+ Wx = 5*Ix-400
+ Wz = -(5*Iy-400)
+
+ Converts coordinated world (x;-;z) -> image (x;y) :
+ Ix = (400+Wx)/5
+ Iy = (400-Wz)/5 */
+bool Gfx::CTerrain::ReliefFromPNG(const std::string &fileName, float scaleRelief,
+ bool adjustBorder)
+{
+ m_scaleRelief = scaleRelief;
+
+ CImage img;
+ if (! img.Load(CApplication::GetInstance().GetDataFilePath(m_engine->GetTextureDir(), fileName)))
+ return false;
+
+ ImageData *data = img.GetData();
+
+ int size = (m_mosaic*m_brick)+1;
+
+ if ( (data->surface->w != size) || (data->surface->h != size) ||
+ (data->surface->format->BytesPerPixel != 3) )
+ return false;
+
+ unsigned char* pixels = static_cast<unsigned char*>(data->surface->pixels);
+
+ float limit = 0.9f;
+ for (int y = 0; y < size; y++)
+ {
+ for (int x = 0; x < size; x++)
+ {
+ float level = (255 - pixels[3*x+3*size*(size-y-1)]) * scaleRelief;
+
+ float dist = Math::Max(fabs(static_cast<float>(x-size/2)),
+ fabs(static_cast<float>(y-size/2)));
+ dist = dist/ static_cast<float>(size / 2);
+ if (dist > limit && adjustBorder)
+ {
+ dist = (dist-limit)/(1.0f-limit); // 0..1
+ if (dist > 1.0f) dist = 1.0f;
+ float border = 300.0f+Math::Rand()*20.0f;
+ level = level+dist*(border-level);
+ }
+
+ m_relief[x+y*size] = level;
+ }
+ }
+
+ return true;
+}
+
+bool Gfx::CTerrain::ReliefAddDot(Math::Vector pos, float scaleRelief)
+{
+ float dim = (m_mosaic*m_brick*m_size)/2.0f;
+ int size = (m_mosaic*m_brick)+1;
+
+ pos.x = (pos.x+dim)/m_size;
+ pos.z = (pos.z+dim)/m_size;
+
+ int x = static_cast<int>(pos.x);
+ int y = static_cast<int>(pos.z);
+
+ if ( x < 0 || x >= size ||
+ y < 0 || y >= size ) return false;
+
+ if (m_relief[x+y*size] < pos.y*scaleRelief)
+ m_relief[x+y*size] = pos.y*scaleRelief;
+
+ return true;
+}
+
+void Gfx::CTerrain::LimitPos(Math::Vector &pos)
+{
+// TODO: #if _TEEN
+// dim = (m_mosaic*m_brick*m_size)/2.0f*0.98f;
+
+ float dim = (m_mosaic*m_brick*m_size)/2.0f*0.92f;
+
+ if (pos.x < -dim) pos.x = -dim;
+ if (pos.x > dim) pos.x = dim;
+ if (pos.z < -dim) pos.z = -dim;
+ if (pos.z > dim) pos.z = dim;
+}
+
+void Gfx::CTerrain::AdjustRelief()
+{
+ if (m_depth == 1) return;
+
+ int ii = m_mosaic*m_brick+1;
+ int b = 1 << (m_depth-1);
+
+ for (int y = 0; y < m_mosaic*m_brick; y += b)
+ {
+ for (int x = 0; x < m_mosaic*m_brick; x += b)
+ {
+ int xx = 0;
+ int yy = 0;
+ if ((y+yy)%m_brick == 0)
+ {
+ float level1 = m_relief[(x+0)+(y+yy)*ii];
+ float level2 = m_relief[(x+b)+(y+yy)*ii];
+ for (xx = 1; xx < b; xx++)
+ {
+ m_relief[(x+xx)+(y+yy)*ii] = ((level2-level1)/b)*xx+level1;
+ }
+ }
+
+ yy = b;
+ if ((y+yy)%m_brick == 0)
+ {
+ float level1 = m_relief[(x+0)+(y+yy)*ii];
+ float level2 = m_relief[(x+b)+(y+yy)*ii];
+ for (xx = 1; xx < b; xx++)
+ {
+ m_relief[(x+xx)+(y+yy)*ii] = ((level2-level1)/b)*xx+level1;
+ }
+ }
+
+ xx = 0;
+ if ((x+xx)%m_brick == 0)
+ {
+ float level1 = m_relief[(x+xx)+(y+0)*ii];
+ float level2 = m_relief[(x+xx)+(y+b)*ii];
+ for (yy = 1; yy < b; yy++)
+ {
+ m_relief[(x+xx)+(y+yy)*ii] = ((level2-level1)/b)*yy+level1;
+ }
+ }
+
+ xx = b;
+ if ((x+xx)%m_brick == 0)
+ {
+ float level1 = m_relief[(x+xx)+(y+0)*ii];
+ float level2 = m_relief[(x+xx)+(y+b)*ii];
+ for (yy = 1; yy < b; yy++)
+ {
+ m_relief[(x+xx)+(y+yy)*ii] = ((level2-level1)/b)*yy+level1;
+ }
+ }
+ }
+ }
+}
+
+Math::Vector Gfx::CTerrain::GetVector(int x, int y)
+{
+ Math::Vector p;
+ p.x = x*m_size - (m_mosaic*m_brick*m_size)/2;
+ p.z = y*m_size - (m_mosaic*m_brick*m_size)/2;
+
+ if ( !m_relief.empty() &&
+ x >= 0 && x <= m_mosaic*m_brick &&
+ y >= 0 && y <= m_mosaic*m_brick )
+ {
+ p.y = m_relief[x+y*(m_mosaic*m_brick+1)];
+ }
+ else
+ {
+ p.y = 0.0f;
+ }
+
+ return p;
+}
+
+/** Calculates a normal soft, taking into account the six adjacent triangles:
+
+\verbatim
+ ^ y
+ |
+ b---c---+
+ |\ |\ |
+ | \| \|
+ a---o---d
+ |\ |\ |
+ | \| \|
+ +---f---e--> x
+\endverbatim */
+Gfx::VertexTex2 Gfx::CTerrain::GetVertex(int x, int y, int step)
+{
+ Gfx::VertexTex2 v;
+
+ Math::Vector o = GetVector(x, y);
+ v.coord = o;
+
+ Math::Vector a = GetVector(x-step, y );
+ Math::Vector b = GetVector(x-step, y+step);
+ Math::Vector c = GetVector(x, y+step);
+ Math::Vector d = GetVector(x+step, y );
+ Math::Vector e = GetVector(x+step, y-step);
+ Math::Vector f = GetVector(x, y-step);
+
+ Math::Vector s(0.0f, 0.0f, 0.0f);
+
+ if (x-step >= 0 && y+step <= m_mosaic*m_brick+1)
+ {
+ s += Math::NormalToPlane(b,a,o);
+ s += Math::NormalToPlane(c,b,o);
+ }
+
+ if (x+step <= m_mosaic*m_brick+1 && y+step <= m_mosaic*m_brick+1)
+ s += Math::NormalToPlane(d,c,o);
+
+ if (x+step <= m_mosaic*m_brick+1 && y-step >= 0)
+ {
+ s += Math::NormalToPlane(e,d,o);
+ s += Math::NormalToPlane(f,e,o);
+ }
+
+ if (x-step >= 0 && y-step >= 0)
+ s += Math::NormalToPlane(a,f,o);
+
+ s = Normalize(s);
+ v.normal = s;
+
+ if (m_multiText)
+ {
+ int brick = m_brick/m_subdivMapping;
+ Math::Vector oo = GetVector((x/brick)*brick, (y/brick)*brick);
+ o = GetVector(x, y);
+ v.texCoord.x = (o.x-oo.x)*m_scaleMapping*m_subdivMapping;
+ v.texCoord.y = 1.0f - (o.z-oo.z)*m_scaleMapping*m_subdivMapping;
+ }
+ else
+ {
+ v.texCoord.x = o.x*m_scaleMapping;
+ v.texCoord.y = o.z*m_scaleMapping;
+ }
+
+ return v;
+}
+
+/** The origin of mosaic is its center.
+\verbatim
+ ^ z
+ |
+ | 2---4---6--
+ | |\ |\ |\
+ | | \| \|
+ | 1---3---5--- ...
+ |
+ +-------------------> x
+\endverbatim */
+bool Gfx::CTerrain::CreateMosaic(int ox, int oy, int step, int objRank,
+ const Gfx::Material &mat,
+ float min, float max)
+{
+ std::string texName1;
+ std::string texName2;
+
+ if ( step == 1 && m_engine->GetGroundSpot() )
+ {
+ int i = (ox/5) + (oy/5)*(m_mosaic/5);
+ std::stringstream s;
+ s << "shadow";
+ s.width(2);
+ s.fill('0');
+ s << i;
+ s << ".png";
+ texName2 = s.str();
+ }
+
+ int brick = m_brick/m_subdivMapping;
+
+ Gfx::VertexTex2 o = GetVertex(ox*m_brick+m_brick/2, oy*m_brick+m_brick/2, step);
+ int total = ((brick/step)+1)*2;
+
+ float pixel = 1.0f/256.0f; // 1 pixel cover (*)
+ float dp = 1.0f/512.0f;
+
+ Math::Point uv;
+
+ for (int my = 0; my < m_subdivMapping; my++)
+ {
+ for (int mx = 0; mx < m_subdivMapping; mx++)
+ {
+ if (m_levelText)
+ {
+ int xx = ox*m_brick + mx*(m_brick/m_subdivMapping);
+ int yy = oy*m_brick + my*(m_brick/m_subdivMapping);
+ LevelTextureName(xx, yy, texName1, uv);
+ }
+ else
+ {
+ int i = (ox*m_subdivMapping+mx)+(oy*m_subdivMapping+my)*m_mosaic;
+ std::stringstream s;
+ s << m_texBaseName;
+ s.width(3);
+ s.fill('0');
+ s << m_texture[i];
+ s << m_texBaseExt;
+ texName1 = s.str();
+ }
+
+ for (int y = 0; y < brick; y += step)
+ {
+ Gfx::EngineObjLevel5 buffer;
+ buffer.vertices.reserve(total);
+
+ buffer.type = Gfx::ENG_TRIANGLE_TYPE_6S;
+ buffer.material = mat;
+
+ buffer.state = Gfx::ENG_RSTATE_WRAP;
+
+ buffer.state |= Gfx::ENG_RSTATE_SECOND;
+ if (step == 1)
+ buffer.state |= Gfx::ENG_RSTATE_DUAL_BLACK;
+
+ for (int x = 0; x <= brick; x += step)
+ {
+ Gfx::VertexTex2 p1 = GetVertex(ox*m_brick+mx*brick+x, oy*m_brick+my*brick+y+0 , step);
+ Gfx::VertexTex2 p2 = GetVertex(ox*m_brick+mx*brick+x, oy*m_brick+my*brick+y+step, step);
+ p1.coord.x -= o.coord.x; p1.coord.z -= o.coord.z;
+ p2.coord.x -= o.coord.x; p2.coord.z -= o.coord.z;
+
+ if (m_multiText)
+ {
+ if (x == 0)
+ {
+ p1.texCoord.x = 0.0f+(0.5f/256.0f);
+ p2.texCoord.x = 0.0f+(0.5f/256.0f);
+ }
+ if (x == brick)
+ {
+ p1.texCoord.x = 1.0f-(0.5f/256.0f);
+ p2.texCoord.x = 1.0f-(0.5f/256.0f);
+ }
+ if (y == 0)
+ p1.texCoord.y = 1.0f-(0.5f/256.0f);
+
+ if (y == brick - step)
+ p2.texCoord.y = 0.0f+(0.5f/256.0f);
+ }
+
+ if (m_levelText)
+ {
+ p1.texCoord.x /= m_subdivMapping; // 0..1 -> 0..0.25
+ p1.texCoord.y /= m_subdivMapping;
+ p2.texCoord.x /= m_subdivMapping;
+ p2.texCoord.y /= m_subdivMapping;
+
+ if (x == 0)
+ {
+ p1.texCoord.x = 0.0f+dp;
+ p2.texCoord.x = 0.0f+dp;
+ }
+ if (x == brick)
+ {
+ p1.texCoord.x = (1.0f/m_subdivMapping)-dp;
+ p2.texCoord.x = (1.0f/m_subdivMapping)-dp;
+ }
+ if (y == 0)
+ p1.texCoord.y = (1.0f/m_subdivMapping)-dp;
+
+ if (y == brick - step)
+ p2.texCoord.y = 0.0f+dp;
+
+ p1.texCoord.x += uv.x;
+ p1.texCoord.y += uv.y;
+ p2.texCoord.x += uv.x;
+ p2.texCoord.y += uv.y;
+ }
+
+ int xx = mx*(m_brick/m_subdivMapping) + x;
+ int yy = my*(m_brick/m_subdivMapping) + y;
+ p1.texCoord2.x = (static_cast<float>(ox%5)*m_brick+xx+0.0f)/(m_brick*5);
+ p1.texCoord2.y = (static_cast<float>(oy%5)*m_brick+yy+0.0f)/(m_brick*5);
+ p2.texCoord2.x = (static_cast<float>(ox%5)*m_brick+xx+0.0f)/(m_brick*5);
+ p2.texCoord2.y = (static_cast<float>(oy%5)*m_brick+yy+1.0f)/(m_brick*5);
+
+// Correction for 1 pixel cover
+// There is 1 pixel cover around each of the 16 surfaces:
+//
+// |<--------------256-------------->|
+// | |<----------254---------->| |
+// |---|---|---|-- ... --|---|---|---|
+// | 0.0 1.0 |
+// | | | |
+// 0.0 min max 1.0
+//
+// The uv coordinates used for texturing are between min and max (instead of 0 and 1)
+// This allows to exclude the pixels situated in a margin of a pixel around the surface
+
+ p1.texCoord2.x = (p1.texCoord2.x+pixel)*(1.0f-pixel)/(1.0f+pixel);
+ p1.texCoord2.y = (p1.texCoord2.y+pixel)*(1.0f-pixel)/(1.0f+pixel);
+ p2.texCoord2.x = (p2.texCoord2.x+pixel)*(1.0f-pixel)/(1.0f+pixel);
+ p2.texCoord2.y = (p2.texCoord2.y+pixel)*(1.0f-pixel)/(1.0f+pixel);
+
+
+ buffer.vertices.push_back(p1);
+ buffer.vertices.push_back(p2);
+ }
+ m_engine->AddQuick(objRank, buffer, texName1, texName2, min, max, true);
+ }
+ }
+ }
+
+ Math::Matrix transform;
+ transform.LoadIdentity();
+ transform.Set(1, 4, o.coord.x);
+ transform.Set(3, 4, o.coord.z);
+ m_engine->SetObjectTransform(objRank, transform);
+
+ return true;
+}
+
+Gfx::TerrainMaterial* Gfx::CTerrain::LevelSearchMat(int id)
+{
+ for (int i = 0; i < static_cast<int>( m_levelMat.size() ); i++)
+ {
+ if (id == m_levelMat[i].id)
+ return &m_levelMat[i];
+ }
+
+ return nullptr;
+}
+
+void Gfx::CTerrain::LevelTextureName(int x, int y, std::string& name, Math::Point &uv)
+{
+ x /= m_brick/m_subdivMapping;
+ y /= m_brick/m_subdivMapping;
+
+ TerrainMaterial* tm = LevelSearchMat(m_levelDot[x+y*m_levelDotSize].id);
+ if (tm == nullptr)
+ {
+ name = "xxx.png";
+ uv.x = 0.0f;
+ uv.y = 0.0f;
+ }
+ else
+ {
+ name = tm->texName;
+ uv.x = tm->u;
+ uv.y = tm->v;
+ }
+}
+
+float Gfx::CTerrain::LevelGetHeight(int x, int y)
+{
+ int size = (m_mosaic*m_brick+1);
+
+ if (x < 0 ) x = 0;
+ if (x >= size) x = size-1;
+ if (y < 0 ) y = 0;
+ if (y >= size) y = size-1;
+
+ return m_relief[x+y*size];
+}
+
+bool Gfx::CTerrain::LevelGetDot(int x, int y, float min, float max, float slope)
+{
+ float hc = LevelGetHeight(x, y);
+ float h[4] =
+ {
+ LevelGetHeight(x+0, y+1),
+ LevelGetHeight(x+1, y+0),
+ LevelGetHeight(x+0, y-1),
+ LevelGetHeight(x-1, y+0)
+ };
+
+ if (hc < min || hc > max)
+ return false;
+
+ if (slope == 0.0f)
+ return true;
+
+ if (slope > 0.0f)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ if (fabs(hc - h[i]) >= slope)
+ return false;
+ }
+ return true;
+ }
+
+ if (slope < 0.0f)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ if (fabs(hc - h[i]) < -slope)
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+
+/** Returns the index within m_levelMat or -1 if there is not.
+ m_levelMat[i].id gives the identifier. */
+int Gfx::CTerrain::LevelTestMat(char *mat)
+{
+ for (int i = 0; i < static_cast<int>( m_levelMat.size() ); i++)
+ {
+ if ( m_levelMat[i].mat[0] == mat[0] &&
+ m_levelMat[i].mat[1] == mat[1] &&
+ m_levelMat[i].mat[2] == mat[2] &&
+ m_levelMat[i].mat[3] == mat[3] ) return i;
+ }
+
+ return -1;
+}
+
+void Gfx::CTerrain::LevelSetDot(int x, int y, int id, char *mat)
+{
+ TerrainMaterial* tm = LevelSearchMat(id);
+ if (tm == nullptr) return;
+
+ if ( tm->mat[0] != mat[0] ||
+ tm->mat[1] != mat[1] ||
+ tm->mat[2] != mat[2] ||
+ tm->mat[3] != mat[3] ) // id incompatible with mat?
+ {
+ int ii = LevelTestMat(mat);
+ if (ii == -1) return;
+ id = m_levelMat[ii].id; // looking for a id compatible with mat
+ }
+
+ // Changes the point
+ m_levelDot[x+y*m_levelDotSize].id = id;
+ m_levelDot[x+y*m_levelDotSize].mat[0] = mat[0];
+ m_levelDot[x+y*m_levelDotSize].mat[1] = mat[1];
+ m_levelDot[x+y*m_levelDotSize].mat[2] = mat[2];
+ m_levelDot[x+y*m_levelDotSize].mat[3] = mat[3];
+
+ // Changes the lower neighbor
+ if ( (x+0) >= 0 && (x+0) < m_levelDotSize &&
+ (y-1) >= 0 && (y-1) < m_levelDotSize )
+ {
+ int i = (x+0)+(y-1)*m_levelDotSize;
+ if (m_levelDot[i].mat[0] != mat[2])
+ {
+ m_levelDot[i].mat[0] = mat[2];
+ int ii = LevelTestMat(m_levelDot[i].mat);
+ if (ii != -1)
+ m_levelDot[i].id = m_levelMat[ii].id;
+ }
+ }
+
+ // Modifies the left neighbor
+ if ( (x-1) >= 0 && (x-1) < m_levelDotSize &&
+ (y+0) >= 0 && (y+0) < m_levelDotSize )
+ {
+ int i = (x-1)+(y+0)*m_levelDotSize;
+ if (m_levelDot[i].mat[1] != mat[3])
+ {
+ m_levelDot[i].mat[1] = mat[3];
+ int ii = LevelTestMat(m_levelDot[i].mat);
+ if (ii != -1)
+ m_levelDot[i].id = m_levelMat[ii].id;
+ }
+ }
+
+ // Changes the upper neighbor
+ if ( (x+0) >= 0 && (x+0) < m_levelDotSize &&
+ (y+1) >= 0 && (y+1) < m_levelDotSize )
+ {
+ int i = (x+0)+(y+1)*m_levelDotSize;
+ if (m_levelDot[i].mat[2] != mat[0])
+ {
+ m_levelDot[i].mat[2] = mat[0];
+ int ii = LevelTestMat(m_levelDot[i].mat);
+ if (ii != -1)
+ m_levelDot[i].id = m_levelMat[ii].id;
+ }
+ }
+
+ // Changes the right neighbor
+ if ( (x+1) >= 0 && (x+1) < m_levelDotSize &&
+ (y+0) >= 0 && (y+0) < m_levelDotSize )
+ {
+ int i = (x+1)+(y+0)*m_levelDotSize;
+ if ( m_levelDot[i].mat[3] != mat[1] )
+ {
+ m_levelDot[i].mat[3] = mat[1];
+ int ii = LevelTestMat(m_levelDot[i].mat);
+ if (ii != -1)
+ m_levelDot[i].id = m_levelMat[ii].id;
+ }
+ }
+}
+
+bool Gfx::CTerrain::LevelIfDot(int x, int y, int id, char *mat)
+{
+ char test[4];
+
+ // Compatible with lower neighbor?
+ if ( x+0 >= 0 && x+0 < m_levelDotSize &&
+ y-1 >= 0 && y-1 < m_levelDotSize )
+ {
+ test[0] = mat[2];
+ test[1] = m_levelDot[(x+0)+(y-1)*m_levelDotSize].mat[1];
+ test[2] = m_levelDot[(x+0)+(y-1)*m_levelDotSize].mat[2];
+ test[3] = m_levelDot[(x+0)+(y-1)*m_levelDotSize].mat[3];
+
+ if ( LevelTestMat(test) == -1 ) return false;
+ }
+
+ // Compatible with left neighbor?
+ if ( x-1 >= 0 && x-1 < m_levelDotSize &&
+ y+0 >= 0 && y+0 < m_levelDotSize )
+ {
+ test[0] = m_levelDot[(x-1)+(y+0)*m_levelDotSize].mat[0];
+ test[1] = mat[3];
+ test[2] = m_levelDot[(x-1)+(y+0)*m_levelDotSize].mat[2];
+ test[3] = m_levelDot[(x-1)+(y+0)*m_levelDotSize].mat[3];
+
+ if ( LevelTestMat(test) == -1 ) return false;
+ }
+
+ // Compatible with upper neighbor?
+ if ( x+0 >= 0 && x+0 < m_levelDotSize &&
+ y+1 >= 0 && y+1 < m_levelDotSize )
+ {
+ test[0] = m_levelDot[(x+0)+(y+1)*m_levelDotSize].mat[0];
+ test[1] = m_levelDot[(x+0)+(y+1)*m_levelDotSize].mat[1];
+ test[2] = mat[0];
+ test[3] = m_levelDot[(x+0)+(y+1)*m_levelDotSize].mat[3];
+
+ if ( LevelTestMat(test) == -1 ) return false;
+ }
+
+ // Compatible with right neighbor?
+ if ( x+1 >= 0 && x+1 < m_levelDotSize &&
+ y+0 >= 0 && y+0 < m_levelDotSize )
+ {
+ test[0] = m_levelDot[(x+1)+(y+0)*m_levelDotSize].mat[0];
+ test[1] = m_levelDot[(x+1)+(y+0)*m_levelDotSize].mat[1];
+ test[2] = m_levelDot[(x+1)+(y+0)*m_levelDotSize].mat[2];
+ test[3] = mat[1];
+
+ if ( LevelTestMat(test) == -1 ) return false;
+ }
+
+ LevelSetDot(x, y, id, mat); // puts the point
+ return true;
+}
+
+bool Gfx::CTerrain::LevelPutDot(int x, int y, int id)
+{
+ char mat[4];
+
+ x /= m_brick/m_subdivMapping;
+ y /= m_brick/m_subdivMapping;
+
+ if ( x < 0 || x >= m_levelDotSize ||
+ y < 0 || y >= m_levelDotSize ) return false;
+
+ TerrainMaterial* tm = LevelSearchMat(id);
+ if (tm == nullptr) return false;
+
+ // Tries without changing neighbors.
+ if ( LevelIfDot(x, y, id, tm->mat) ) return true;
+
+ // Tries changing a single neighbor (4x).
+ for (int up = 0; up < m_levelMatMax; up++)
+ {
+ mat[0] = up;
+ mat[1] = tm->mat[1];
+ mat[2] = tm->mat[2];
+ mat[3] = tm->mat[3];
+
+ if (LevelIfDot(x, y, id, mat)) return true;
+ }
+
+ for (int right = 0; right < m_levelMatMax; right++)
+ {
+ mat[0] = tm->mat[0];
+ mat[1] = right;
+ mat[2] = tm->mat[2];
+ mat[3] = tm->mat[3];
+
+ if (LevelIfDot(x, y, id, mat)) return true;
+ }
+
+ for (int down = 0; down < m_levelMatMax; down++)
+ {
+ mat[0] = tm->mat[0];
+ mat[1] = tm->mat[1];
+ mat[2] = down;
+ mat[3] = tm->mat[3];
+
+ if (LevelIfDot(x, y, id, mat)) return true;
+ }
+
+ for (int left = 0; left < m_levelMatMax; left++)
+ {
+ mat[0] = tm->mat[0];
+ mat[1] = tm->mat[1];
+ mat[2] = tm->mat[2];
+ mat[3] = left;
+
+ if (LevelIfDot(x, y, id, mat)) return true;
+ }
+
+ // Tries changing two neighbors (6x).
+ for (int up = 0; up < m_levelMatMax; up++)
+ {
+ for (int down = 0; down < m_levelMatMax; down++)
+ {
+ mat[0] = up;
+ mat[1] = tm->mat[1];
+ mat[2] = down;
+ mat[3] = tm->mat[3];
+
+ if (LevelIfDot(x, y, id, mat)) return true;
+ }
+ }
+
+ for (int right = 0; right < m_levelMatMax; right++)
+ {
+ for (int left = 0; left < m_levelMatMax; left++)
+ {
+ mat[0] = tm->mat[0];
+ mat[1] = right;
+ mat[2] = tm->mat[2];
+ mat[3] = left;
+
+ if (LevelIfDot(x, y, id, mat)) return true;
+ }
+ }
+
+ for (int up = 0; up < m_levelMatMax; up++)
+ {
+ for (int right = 0; right < m_levelMatMax; right++)
+ {
+ mat[0] = up;
+ mat[1] = right;
+ mat[2] = tm->mat[2];
+ mat[3] = tm->mat[3];
+
+ if (LevelIfDot(x, y, id, mat)) return true;
+ }
+ }
+
+ for (int right = 0; right < m_levelMatMax; right++)
+ {
+ for (int down = 0; down < m_levelMatMax; down++)
+ {
+ mat[0] = tm->mat[0];
+ mat[1] = right;
+ mat[2] = down;
+ mat[3] = tm->mat[3];
+
+ if (LevelIfDot(x, y, id, mat)) return true;
+ }
+ }
+
+ for (int down = 0; down < m_levelMatMax; down++)
+ {
+ for (int left = 0; left < m_levelMatMax; left++)
+ {
+ mat[0] = tm->mat[0];
+ mat[1] = tm->mat[1];
+ mat[2] = down;
+ mat[3] = left;
+
+ if (LevelIfDot(x, y, id, mat)) return true;
+ }
+ }
+
+ for (int up = 0; up < m_levelMatMax; up++)
+ {
+ for (int left = 0; left < m_levelMatMax; left++)
+ {
+ mat[0] = up;
+ mat[1] = tm->mat[1];
+ mat[2] = tm->mat[2];
+ mat[3] = left;
+
+ if (LevelIfDot(x, y, id, mat)) return true;
+ }
+ }
+
+ // Tries changing all the neighbors.
+ for (int up = 0; up < m_levelMatMax; up++)
+ {
+ for (int right = 0; right < m_levelMatMax; right++)
+ {
+ for (int down = 0; down < m_levelMatMax; down++)
+ {
+ for (int left = 0; left < m_levelMatMax; left++)
+ {
+ mat[0] = up;
+ mat[1] = right;
+ mat[2] = down;
+ mat[3] = left;
+
+ if (LevelIfDot(x, y, id, mat)) return true;
+ }
+ }
+ }
+ }
+
+ GetLogger()->Error("LevelPutDot error\n");
+ return false;
+}
+
+bool Gfx::CTerrain::LevelInit(int id)
+{
+ TerrainMaterial* tm = LevelSearchMat(id);
+ if (tm == nullptr) return false;
+
+ for (int i = 0; i < m_levelDotSize*m_levelDotSize; i++)
+ {
+ m_levelDot[i].id = id;
+
+ for (int j = 0; j < 4; j++)
+ m_levelDot[i].mat[j] = tm->mat[j];
+ }
+
+ return true;
+}
+
+bool Gfx::CTerrain::LevelGenerate(int *id, float min, float max,
+ float slope, float freq,
+ Math::Vector center, float radius)
+{
+ static char random[100] =
+ {
+ 84,25,12, 6,34,52,85,38,97,16,
+ 21,31,65,19,62,40,72,22,48,61,
+ 56,47, 8,53,73,77, 4,91,26,88,
+ 76, 1,44,93,39,11,71,17,98,95,
+ 88,83,18,30, 3,57,28,49,74, 9,
+ 32,13,96,66,15,70,36,10,59,94,
+ 45,86, 2,29,63,42,51, 0,79,27,
+ 54, 7,20,69,89,23,64,43,81,92,
+ 90,33,46,14,67,35,50, 5,87,60,
+ 68,55,24,78,41,75,58,80,37,82,
+ };
+
+ TerrainMaterial* tm = nullptr;
+
+ int i = 0;
+ while ( id[i] != 0 )
+ {
+ tm = LevelSearchMat(id[i++]);
+ if (tm == nullptr) return false;
+ }
+ int numID = i;
+
+ int group = m_brick / m_subdivMapping;
+
+ if (radius > 0.0f && radius < 5.0f) // just a square?
+ {
+ float dim = (m_mosaic*m_brick*m_size)/2.0f;
+
+ int xx = static_cast<int>((center.x+dim)/m_size);
+ int yy = static_cast<int>((center.z+dim)/m_size);
+
+ int x = xx/group;
+ int y = yy/group;
+
+ tm = LevelSearchMat(id[0]);
+ if (tm != nullptr)
+ LevelSetDot(x, y, id[0], tm->mat); // puts the point
+ }
+ else
+ {
+ for (int y = 0; y < m_levelDotSize; y++)
+ {
+ for (int x = 0; x < m_levelDotSize; x++)
+ {
+ if (radius != 0.0f)
+ {
+ Math::Vector pos;
+ pos.x = (static_cast<float>(x)-m_levelDotSize/2.0f)*group*m_size;
+ pos.z = (static_cast<float>(y)-m_levelDotSize/2.0f)*group*m_size;
+ if (Math::DistanceProjected(pos, center) > radius) continue;
+ }
+
+ if (freq < 100.0f)
+ {
+ int rnd = random[(x%10)+(y%10)*10];
+ if ( static_cast<float>(rnd) > freq ) continue;
+ }
+
+ int xx = x*group + group/2;
+ int yy = y*group + group/2;
+
+ if (LevelGetDot(xx, yy, min, max, slope))
+ {
+ int rnd = random[(x%10)+(y%10)*10];
+ int ii = rnd % numID;
+ LevelPutDot(xx, yy, id[ii]);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+void Gfx::CTerrain::LevelOpenTable()
+{
+ if (! m_levelText) return;
+ if (! m_levelDot.empty()) return; // already allocated
+
+ m_levelDotSize = (m_mosaic*m_brick)/(m_brick/m_subdivMapping)+1;
+ std::vector<Gfx::DotLevel>(m_levelDotSize*m_levelDotSize).swap(m_levelDot);
+
+ for (int i = 0; i < m_levelDotSize * m_levelDotSize; i++)
+ {
+ for (int j = 0; j < 4; j++)
+ m_levelDot[i].mat[j] = 0;
+ }
+}
+
+void Gfx::CTerrain::LevelCloseTable()
+{
+ m_levelDot.clear();
+}
+
+bool Gfx::CTerrain::CreateSquare(bool multiRes, int x, int y)
+{
+ Gfx::Material mat;
+ mat.diffuse = Gfx::Color(1.0f, 1.0f, 1.0f);
+ mat.ambient = Gfx::Color(0.0f, 0.0f, 0.0f);
+
+ int objRank = m_engine->CreateObject();
+ m_engine->SetObjectType(objRank, Gfx::ENG_OBJTYPE_TERRAIN); // it is a terrain
+
+ m_objRank[x+y*m_mosaic] = objRank;
+
+ if (multiRes)
+ {
+ float min = 0.0f;
+ float max = m_vision;
+ max *= m_engine->GetClippingDistance();
+ for (int step = 0; step < m_depth; step++)
+ {
+ CreateMosaic(x, y, 1 << step, objRank, mat, min, max);
+ min = max;
+ max *= 2;
+ if (step == m_depth-1) max = Math::HUGE_NUM;
+ }
+ }
+ else
+ {
+ CreateMosaic(x, y, 1, objRank, mat, 0.0f, Math::HUGE_NUM);
+ }
+
+ return true;
+}
+
+bool Gfx::CTerrain::CreateObjects(bool multiRes)
+{
+ AdjustRelief();
+
+ for (int y = 0; y < m_mosaic; y++)
+ {
+ for (int x = 0; x < m_mosaic; x++)
+ CreateSquare(multiRes, x, y);
+ }
+
+ return true;
+}
+
+/** ATTENTION: ok only with m_depth = 2! */
+bool Gfx::CTerrain::Terraform(const Math::Vector &p1, const Math::Vector &p2, float height)
+{
+ float dim = (m_mosaic*m_brick*m_size)/2.0f;
+
+ Math::IntPoint tp1, tp2;
+ tp1.x = static_cast<int>((p1.x+dim+m_size/2.0f)/m_size);
+ tp1.y = static_cast<int>((p1.z+dim+m_size/2.0f)/m_size);
+ tp2.x = static_cast<int>((p2.x+dim+m_size/2.0f)/m_size);
+ tp2.y = static_cast<int>((p2.z+dim+m_size/2.0f)/m_size);
+
+ if (tp1.x > tp2.x)
+ {
+ int x = tp1.x;
+ tp1.x = tp2.x;
+ tp2.x = x;
+ }
+
+ if (tp1.y > tp2.y)
+ {
+ int y = tp1.y;
+ tp1.y = tp2.y;
+ tp2.y = y;
+ }
+
+ int size = (m_mosaic*m_brick)+1;
+
+ // Calculates the current average height
+ float avg = 0.0f;
+ int nb = 0;
+ for (int y = tp1.y; y <= tp2.y; y++)
+ {
+ for (int x = tp1.x; x <= tp2.x; x++)
+ {
+ avg += m_relief[x+y*size];
+ nb ++;
+ }
+ }
+ avg /= static_cast<float>(nb);
+
+ // Changes the description of the relief
+ for (int y = tp1.y; y <= tp2.y; y++)
+ {
+ for (int x = tp1.x; x <= tp2.x; x++)
+ {
+ m_relief[x+y*size] = avg+height;
+
+ if (x % m_brick == 0 && y % m_depth != 0)
+ {
+ m_relief[(x+0)+(y-1)*size] = avg+height;
+ m_relief[(x+0)+(y+1)*size] = avg+height;
+ }
+
+ if (y % m_brick == 0 && x % m_depth != 0)
+ {
+ m_relief[(x-1)+(y+0)*size] = avg+height;
+ m_relief[(x+1)+(y+0)*size] = avg+height;
+ }
+ }
+ }
+ AdjustRelief();
+
+ Math::IntPoint pp1, pp2;
+ pp1.x = (tp1.x-2)/m_brick;
+ pp1.y = (tp1.y-2)/m_brick;
+ pp2.x = (tp2.x+1)/m_brick;
+ pp2.y = (tp2.y+1)/m_brick;
+
+ if (pp1.x < 0 ) pp1.x = 0;
+ if (pp1.x >= m_mosaic) pp1.x = m_mosaic-1;
+ if (pp1.y < 0 ) pp1.y = 0;
+ if (pp1.y >= m_mosaic) pp1.y = m_mosaic-1;
+
+ for (int y = pp1.y; y <= pp2.y; y++)
+ {
+ for (int x = pp1.x; x <= pp2.x; x++)
+ {
+ m_engine->DeleteObject(m_objRank[x+y*m_mosaic]);
+ CreateSquare(m_multiText, x, y); // recreates the square
+ }
+ }
+ m_engine->Update();
+
+ return true;
+}
+
+void Gfx::CTerrain::SetWind(Math::Vector speed)
+{
+ m_wind = speed;
+}
+
+Math::Vector Gfx::CTerrain::GetWind()
+{
+ return m_wind;
+}
+
+float Gfx::CTerrain::GetFineSlope(const Math::Vector &pos)
+{
+ Math::Vector n;
+ if (! GetNormal(n, pos)) return 0.0f;
+ return fabs(Math::RotateAngle(Math::Point(n.x, n.z).Length(), n.y) - Math::PI/2.0f);
+}
+
+float Gfx::CTerrain::GetCoarseSlope(const Math::Vector &pos)
+{
+ if (m_relief.empty()) return 0.0f;
+
+ float dim = (m_mosaic*m_brick*m_size)/2.0f;
+
+ int x = static_cast<int>((pos.x+dim)/m_size);
+ int y = static_cast<int>((pos.z+dim)/m_size);
+
+ if ( x < 0 || x >= m_mosaic*m_brick ||
+ y < 0 || y >= m_mosaic*m_brick ) return 0.0f;
+
+ float level[4] =
+ {
+ m_relief[(x+0)+(y+0)*(m_mosaic*m_brick+1)],
+ m_relief[(x+1)+(y+0)*(m_mosaic*m_brick+1)],
+ m_relief[(x+0)+(y+1)*(m_mosaic*m_brick+1)],
+ m_relief[(x+1)+(y+1)*(m_mosaic*m_brick+1)],
+ };
+
+ float min = Math::Min(level[0], level[1], level[2], level[3]);
+ float max = Math::Max(level[0], level[1], level[2], level[3]);
+
+ return atanf((max-min)/m_size);
+}
+
+bool Gfx::CTerrain::GetNormal(Math::Vector &n, const Math::Vector &p)
+{
+ float dim = (m_mosaic*m_brick*m_size)/2.0f;
+
+ int x = static_cast<int>((p.x+dim)/m_size);
+ int y = static_cast<int>((p.z+dim)/m_size);
+
+ if ( x < 0 || x > m_mosaic*m_brick ||
+ y < 0 || y > m_mosaic*m_brick ) return false;
+
+ Math::Vector p1 = GetVector(x+0, y+0);
+ Math::Vector p2 = GetVector(x+1, y+0);
+ Math::Vector p3 = GetVector(x+0, y+1);
+ Math::Vector p4 = GetVector(x+1, y+1);
+
+ if ( fabs(p.z - p2.z) < fabs(p.x - p2.x) )
+ n = Math::NormalToPlane(p1,p2,p3);
+ else
+ n = Math::NormalToPlane(p2,p4,p3);
+
+ return true;
+}
+
+float Gfx::CTerrain::GetFloorLevel(const Math::Vector &p, bool brut, bool water)
+{
+ float dim = (m_mosaic*m_brick*m_size)/2.0f;
+
+ int x = static_cast<int>((p.x+dim)/m_size);
+ int y = static_cast<int>((p.z+dim)/m_size);
+
+ if ( x < 0 || x > m_mosaic*m_brick ||
+ y < 0 || y > m_mosaic*m_brick ) return false;
+
+ Math::Vector p1 = GetVector(x+0, y+0);
+ Math::Vector p2 = GetVector(x+1, y+0);
+ Math::Vector p3 = GetVector(x+0, y+1);
+ Math::Vector p4 = GetVector(x+1, y+1);
+
+ Math::Vector ps = p;
+ if ( fabs(p.z-p2.z) < fabs(p.x-p2.x) )
+ {
+ if ( !IntersectY(p1, p2, p3, ps) ) return 0.0f;
+ }
+ else
+ {
+ if ( !IntersectY(p2, p4, p3, ps) ) return 0.0f;
+ }
+
+ if (! brut) AdjustBuildingLevel(ps);
+
+ if (water) // not going underwater?
+ {
+ float level = m_water->GetLevel();
+ if (ps.y < level) ps.y = level; // not under water
+ }
+
+ return ps.y;
+}
+
+
+/** This height is positive when you are above the ground */
+float Gfx::CTerrain::GetFloorHeight(const Math::Vector &p, bool brut, bool water)
+{
+ float dim = (m_mosaic*m_brick*m_size)/2.0f;
+
+ int x = static_cast<int>((p.x+dim)/m_size);
+ int y = static_cast<int>((p.z+dim)/m_size);
+
+ if ( x < 0 || x > m_mosaic*m_brick ||
+ y < 0 || y > m_mosaic*m_brick ) return false;
+
+ Math::Vector p1 = GetVector(x+0, y+0);
+ Math::Vector p2 = GetVector(x+1, y+0);
+ Math::Vector p3 = GetVector(x+0, y+1);
+ Math::Vector p4 = GetVector(x+1, y+1);
+
+ Math::Vector ps = p;
+ if ( fabs(p.z-p2.z) < fabs(p.x-p2.x) )
+ {
+ if ( !IntersectY(p1, p2, p3, ps) ) return 0.0f;
+ }
+ else
+ {
+ if ( !IntersectY(p2, p4, p3, ps) ) return 0.0f;
+ }
+
+ if (! brut) AdjustBuildingLevel(ps);
+
+ if (water) // not going underwater?
+ {
+ float level = m_water->GetLevel();
+ if ( ps.y < level ) ps.y = level; // not under water
+ }
+
+ return p.y-ps.y;
+}
+
+bool Gfx::CTerrain::MoveOnFloor(Math::Vector &p, bool brut, bool water)
+{
+ float dim = (m_mosaic*m_brick*m_size)/2.0f;
+
+ int x = static_cast<int>((p.x + dim) / m_size);
+ int y = static_cast<int>((p.z + dim) / m_size);
+
+ if ( x < 0 || x > m_mosaic*m_brick ||
+ y < 0 || y > m_mosaic*m_brick ) return false;
+
+ Math::Vector p1 = GetVector(x+0, y+0);
+ Math::Vector p2 = GetVector(x+1, y+0);
+ Math::Vector p3 = GetVector(x+0, y+1);
+ Math::Vector p4 = GetVector(x+1, y+1);
+
+ if (fabs(p.z - p2.z) < fabs(p.x - p2.x))
+ {
+ if (! Math::IntersectY(p1, p2, p3, p)) return false;
+ }
+ else
+ {
+ if (! Math::IntersectY(p2, p4, p3, p)) return false;
+ }
+
+ if (! brut) AdjustBuildingLevel(p);
+
+ if (water) // not going underwater?
+ {
+ float level = m_water->GetLevel();
+ if (p.y < level) p.y = level; // not under water
+ }
+
+ return true;
+}
+
+
+/** Returns false if the initial coordinate was too far */
+bool Gfx::CTerrain::ValidPosition(Math::Vector &p, float marging)
+{
+ bool ok = true;
+
+ float limit = m_mosaic*m_brick*m_size/2.0f - marging;
+
+ if (p.x < -limit)
+ {
+ p.x = -limit;
+ ok = false;
+ }
+
+ if (p.z < -limit)
+ {
+ p.z = -limit;
+ ok = false;
+ }
+
+ if (p.x > limit)
+ {
+ p.x = limit;
+ ok = false;
+ }
+
+ if (p.z > limit)
+ {
+ p.z = limit;
+ ok = false;
+ }
+
+ return ok;
+}
+
+void Gfx::CTerrain::FlushBuildingLevel()
+{
+ m_buildingLevels.clear();
+}
+
+bool Gfx::CTerrain::AddBuildingLevel(Math::Vector center, float min, float max,
+ float height, float factor)
+{
+ int i = 0;
+ for ( ; i < static_cast<int>( m_buildingLevels.size() ); i++)
+ {
+ if ( center.x == m_buildingLevels[i].center.x &&
+ center.z == m_buildingLevels[i].center.z )
+ {
+ break;
+ }
+ }
+
+ if (i == static_cast<int>( m_buildingLevels.size() ))
+ m_buildingLevels.push_back(Gfx::BuildingLevel());
+
+ m_buildingLevels[i].center = center;
+ m_buildingLevels[i].min = min;
+ m_buildingLevels[i].max = max;
+ m_buildingLevels[i].level = GetFloorLevel(center, true);
+ m_buildingLevels[i].height = height;
+ m_buildingLevels[i].factor = factor;
+ m_buildingLevels[i].bboxMinX = center.x-max;
+ m_buildingLevels[i].bboxMaxX = center.x+max;
+ m_buildingLevels[i].bboxMinZ = center.z-max;
+ m_buildingLevels[i].bboxMaxZ = center.z+max;
+
+ return true;
+}
+
+bool Gfx::CTerrain::UpdateBuildingLevel(Math::Vector center)
+{
+ for (int i = 0; i < static_cast<int>( m_buildingLevels.size() ); i++)
+ {
+ if ( center.x == m_buildingLevels[i].center.x &&
+ center.z == m_buildingLevels[i].center.z )
+ {
+ m_buildingLevels[i].center = center;
+ m_buildingLevels[i].level = GetFloorLevel(center, true);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Gfx::CTerrain::DeleteBuildingLevel(Math::Vector center)
+{
+ for (int i = 0; i < static_cast<int>( m_buildingLevels.size() ); i++)
+ {
+ if ( center.x == m_buildingLevels[i].center.x &&
+ center.z == m_buildingLevels[i].center.z )
+ {
+ for (int j = i+1; j < static_cast<int>( m_buildingLevels.size() ); j++)
+ m_buildingLevels[j-1] = m_buildingLevels[j];
+
+ m_buildingLevels.pop_back();
+ return true;
+ }
+ }
+ return false;
+}
+
+float Gfx::CTerrain::GetBuildingFactor(const Math::Vector &p)
+{
+ for (int i = 0; i < static_cast<int>( m_buildingLevels.size() ); i++)
+ {
+ if ( p.x < m_buildingLevels[i].bboxMinX ||
+ p.x > m_buildingLevels[i].bboxMaxX ||
+ p.z < m_buildingLevels[i].bboxMinZ ||
+ p.z > m_buildingLevels[i].bboxMaxZ ) continue;
+
+ float dist = Math::DistanceProjected(p, m_buildingLevels[i].center);
+
+ if (dist <= m_buildingLevels[i].max)
+ return m_buildingLevels[i].factor;
+ }
+ return 1.0f; // it is normal on the ground
+}
+
+void Gfx::CTerrain::AdjustBuildingLevel(Math::Vector &p)
+{
+ for (int i = 0; i < static_cast<int>( m_buildingLevels.size() ); i++)
+ {
+ if ( p.x < m_buildingLevels[i].bboxMinX ||
+ p.x > m_buildingLevels[i].bboxMaxX ||
+ p.z < m_buildingLevels[i].bboxMinZ ||
+ p.z > m_buildingLevels[i].bboxMaxZ ) continue;
+
+ float dist = Math::DistanceProjected(p, m_buildingLevels[i].center);
+
+ if (dist > m_buildingLevels[i].max) continue;
+
+ if (dist < m_buildingLevels[i].min)
+ {
+ p.y = m_buildingLevels[i].level + m_buildingLevels[i].height;
+ return;
+ }
+
+ Math::Vector border;
+ border.x = ((p.x - m_buildingLevels[i].center.x) * m_buildingLevels[i].max) /
+ dist + m_buildingLevels[i].center.x;
+ border.z = ((p.z - m_buildingLevels[i].center.z) * m_buildingLevels[i].max) /
+ dist + m_buildingLevels[i].center.z;
+
+ float base = GetFloorLevel(border, true);
+
+ p.y = (m_buildingLevels[i].max - dist) /
+ (m_buildingLevels[i].max - m_buildingLevels[i].min) *
+ (m_buildingLevels[i].level + m_buildingLevels[i].height-base) +
+ base;
+
+ return;
+ }
+}
+
+
+// returns the hardness of the ground in a given place.
+// The hardness determines the noise (SOUND_STEP and SOUND_BOUM).
+
+float Gfx::CTerrain::GetHardness(const Math::Vector &p)
+{
+ float factor = GetBuildingFactor(p);
+ if (factor != 1.0f) return 1.0f; // on building
+
+ if (m_levelDot.empty()) return m_defHardness;
+
+ float dim = (m_mosaic*m_brick*m_size)/2.0f;
+
+ int x, y;
+
+ x = static_cast<int>((p.x+dim)/m_size);
+ y = static_cast<int>((p.z+dim)/m_size);
+
+ if ( x < 0 || x > m_mosaic*m_brick ||
+ y < 0 || y > m_mosaic*m_brick ) return m_defHardness;
+
+ x /= m_brick/m_subdivMapping;
+ y /= m_brick/m_subdivMapping;
+
+ if ( x < 0 || x >= m_levelDotSize ||
+ y < 0 || y >= m_levelDotSize ) return m_defHardness;
+
+ int id = m_levelDot[x+y*m_levelDotSize].id;
+ TerrainMaterial* tm = LevelSearchMat(id);
+ if (tm == nullptr) return m_defHardness;
+
+ return tm->hardness;
+}
+
+void Gfx::CTerrain::GroundFlat(Math::Vector pos)
+{
+ static char table[41*41];
+
+
+ float rapport = 3200.0f/1024.0f;
+
+ for (int y = 0; y <= 40; y++)
+ {
+ for (int x = 0; x <= 40; x++)
+ {
+ int i = x + y*41;
+ table[i] = 0;
+
+ Math::Vector p;
+ p.x = (x-20)*rapport;
+ p.z = (y-20)*rapport;
+ p.y = 0.0f;
+
+ if (Math::Point(p.x, p.y).Length() > 20.0f*rapport)
+ continue;
+
+ float angle = GetFineSlope(pos+p);
+
+ if (angle < FLATLIMIT)
+ table[i] = 1;
+ else
+ table[i] = 2;
+ }
+ }
+
+ m_engine->GroundMarkCreate(pos, 40.0f, 0.001f, 15.0f, 0.001f, 41, 41, table);
+}
+
+float Gfx::CTerrain::GetFlatZoneRadius(Math::Vector center, float max)
+{
+ float angle = GetFineSlope(center);
+ if (angle >= Gfx::FLATLIMIT)
+ return 0.0f;
+
+ float ref = GetFloorLevel(center, true);
+
+ float radius = 1.0f;
+ while (radius <= max)
+ {
+ angle = 0.0f;
+ int nb = static_cast<int>(2.0f*Math::PI*radius);
+ if (nb < 8) nb = 8;
+
+ for (int i = 0; i < nb; i++)
+ {
+ Math::Point c(center.x, center.z);
+ Math::Point p (center.x+radius, center.z);
+ p = Math::RotatePoint(c, angle, p);
+ Math::Vector pos;
+ pos.x = p.x;
+ pos.z = p.y;
+ float h = GetFloorLevel(pos, true);
+ if ( fabs(h-ref) > 1.0f ) return radius;
+
+ angle += Math::PI*2.0f/8.0f;
+ }
+ radius += 1.0f;
+ }
+ return max;
+}
+
+void Gfx::CTerrain::SetFlyingMaxHeight(float height)
+{
+ m_flyingMaxHeight = height;
+}
+
+float Gfx::CTerrain::GetFlyingMaxHeight()
+{
+ return m_flyingMaxHeight;
+}
+
+void Gfx::CTerrain::FlushFlyingLimit()
+{
+ m_flyingMaxHeight = 280.0f;
+ m_flyingLimits.clear();
+}
+
+void Gfx::CTerrain::AddFlyingLimit(Math::Vector center,
+ float extRadius, float intRadius,
+ float maxHeight)
+{
+ Gfx::FlyingLimit fl;
+ fl.center = center;
+ fl.extRadius = extRadius;
+ fl.intRadius = intRadius;
+ fl.maxHeight = maxHeight;
+ m_flyingLimits.push_back(fl);
+}
+
+float Gfx::CTerrain::GetFlyingLimit(Math::Vector pos, bool noLimit)
+{
+ if (noLimit)
+ return 280.0f;
+
+ if (m_flyingLimits.empty())
+ return m_flyingMaxHeight;
+
+ for (int i = 0; i < static_cast<int>( m_flyingLimits.size() ); i++)
+ {
+ float dist = Math::DistanceProjected(pos, m_flyingLimits[i].center);
+
+ if (dist >= m_flyingLimits[i].extRadius)
+ continue;
+
+ if (dist <= m_flyingLimits[i].intRadius)
+ return m_flyingLimits[i].maxHeight;
+
+ dist -= m_flyingLimits[i].intRadius;
+
+ float h = dist * (m_flyingMaxHeight - m_flyingLimits[i].maxHeight) /
+ (m_flyingLimits[i].extRadius - m_flyingLimits[i].intRadius);
+
+ return h + m_flyingLimits[i].maxHeight;
+ }
+
+ return m_flyingMaxHeight;
+}
diff --git a/src/graphics/engine/terrain.h b/src/graphics/engine/terrain.h
index 8d8b165..41d4bbb 100644
--- a/src/graphics/engine/terrain.h
+++ b/src/graphics/engine/terrain.h
@@ -15,7 +15,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// terrain.h
+/**
+ * \file graphics/engine/terrain.h
+ * \brief Terrain rendering - Gfx::CTerrain class
+ */
#pragma once
@@ -40,56 +43,72 @@ enum TerrainRes
TR_STONE = 1,
TR_URANIUM = 2,
TR_POWER = 3,
- TR_KEYa = 4,
- TR_KEYb = 5,
- TR_KEYc = 6,
- TR_KEYd = 7,
+ TR_KEY_A = 4,
+ TR_KEY_B = 5,
+ TR_KEY_C = 6,
+ TR_KEY_D = 7,
};
-
-const short MAXBUILDINGLEVEL = 100;
-
struct BuildingLevel
{
- Math::Vector center;
- float factor;
- float min;
- float max;
- float level;
- float height;
- float bboxMinX;
- float bboxMaxX;
- float bboxMinZ;
- float bboxMaxZ;
+ Math::Vector center;
+ float factor;
+ float min;
+ float max;
+ float level;
+ float height;
+ float bboxMinX;
+ float bboxMaxX;
+ float bboxMinZ;
+ float bboxMaxZ;
+
+ BuildingLevel()
+ {
+ factor = min = max = level = height = 0.0f;
+ bboxMinX = bboxMaxX = bboxMinZ = bboxMaxZ = 0.0f;
+ }
};
-
-const short MAXMATTERRAIN = 100;
-
struct TerrainMaterial
{
short id;
- char texName[20];
+ std::string texName;
float u,v;
float hardness;
char mat[4]; // up, right, down, left
+
+ TerrainMaterial()
+ {
+ id = 0;
+ u = v = 0.0f;
+ hardness = 0.0f;
+ mat[0] = mat[1] = mat[2] = mat[3] = 0;
+ }
};
struct DotLevel
{
short id;
char mat[4]; // up, right, down, left
-};
-
-const short MAXFLYINGLIMIT = 10;
+ DotLevel()
+ {
+ id = 0;
+ mat[0] = mat[1] = mat[2] = mat[3] = 0;
+ }
+};
struct FlyingLimit
{
- Math::Vector center;
- float extRadius;
- float intRadius;
- float maxHeight;
+ Math::Vector center;
+ float extRadius;
+ float intRadius;
+ float maxHeight;
+
+ FlyingLimit()
+ {
+ extRadius = intRadius = maxHeight = 0.0f;
+ }
};
@@ -100,72 +119,124 @@ public:
CTerrain(CInstanceManager* iMan);
~CTerrain();
- bool Generate(int mosaic, int brickP2, float size, float vision, int depth, float hardness);
- bool InitTextures(char* baseName, int* table, int dx, int dy);
+ //! Generates a new flat terrain
+ bool Generate(int mosaic, int brickPow2, float size, float vision, int depth, float hardness);
+ //! Initializes the names of textures to use for the land
+ bool InitTextures(const std::string& baseName, int* table, int dx, int dy);
+ //! Empties level
void LevelFlush();
- bool LevelMaterial(int id, char* baseName, float u, float v, int up, int right, int down, int left, float hardness);
+ //! Initializes the names of textures to use for the land
+ void LevelMaterial(int id, std::string& baseName, float u, float v, int up, int right, int down, int left, float hardness);
+ //! Initializes all the ground with a material
bool LevelInit(int id);
+ //! Generates a level in the terrain
bool LevelGenerate(int *id, float min, float max, float slope, float freq, Math::Vector center, float radius);
+ //! Initializes a completely flat terrain
void FlushRelief();
- bool ReliefFromBMP(const char* filename, float scaleRelief, bool adjustBorder);
- bool ReliefFromDXF(const char* filename, float scaleRelief);
- bool ResFromBMP(const char* filename);
- bool CreateObjects(bool bMultiRes);
- bool Terraform(const Math::Vector &p1, const Math::Vector &p2, float height);
-
- void SetWind(Math::Vector speed);
- Math::Vector RetWind();
-
- float RetFineSlope(const Math::Vector &pos);
- float RetCoarseSlope(const Math::Vector &pos);
- bool GetNormal(Math::Vector &n, const Math::Vector &p);
- float RetFloorLevel(const Math::Vector &p, bool bBrut=false, bool bWater=false);
- float RetFloorHeight(const Math::Vector &p, bool bBrut=false, bool bWater=false);
- bool MoveOnFloor(Math::Vector &p, bool bBrut=false, bool bWater=false);
- bool ValidPosition(Math::Vector &p, float marging);
- TerrainRes RetResource(const Math::Vector &p);
+ //! Load relief from a PNG file
+ bool ReliefFromPNG(const std::string& filename, float scaleRelief, bool adjustBorder);
+ //! Load resources from a PNG file
+ bool ResFromPNG(const std::string& filename);
+ //! Creates all objects of the terrain within the 3D engine
+ bool CreateObjects(bool multiRes);
+ //! Modifies the terrain's relief
+ bool Terraform(const Math::Vector& p1, const Math::Vector& p2, float height);
+
+ //@{
+ //! Management of the wind
+ void SetWind(Math::Vector speed);
+ Math::Vector GetWind();
+ //@}
+
+ //! Gives the exact slope of the terrain of a place given
+ float GetFineSlope(const Math::Vector& pos);
+ //! Gives the approximate slope of the terrain of a specific location
+ float GetCoarseSlope(const Math::Vector& pos);
+ //! Gives the normal vector at the position p (x,-,z) of the ground
+ bool GetNormal(Math::Vector& n, const Math::Vector &p);
+ //! returns the height of the ground
+ float GetFloorLevel(const Math::Vector& p, bool brut=false, bool water=false);
+ //! Returns the height to the ground
+ float GetFloorHeight(const Math::Vector& p, bool brut=false, bool water=false);
+ //! Modifies the coordinate "y" of point "p" to rest on the ground floor
+ bool MoveOnFloor(Math::Vector& p, bool brut=false, bool water=false);
+ //! Modifies a coordinate so that it is on the ground
+ bool ValidPosition(Math::Vector& p, float marging);
+ //! Returns the resource type available underground
+ Gfx::TerrainRes GetResource(const Math::Vector& p);
+ //! Adjusts a position so that it does not exceed the boundaries
void LimitPos(Math::Vector &pos);
+ //! Empty the table of elevations
void FlushBuildingLevel();
+ //! Adds a new elevation for a building
bool AddBuildingLevel(Math::Vector center, float min, float max, float height, float factor);
+ //! Updates the elevation for a building when it was moved up (after a terraforming)
bool UpdateBuildingLevel(Math::Vector center);
+ //! Removes the elevation for a building when it was destroyed
bool DeleteBuildingLevel(Math::Vector center);
- float RetBuildingFactor(const Math::Vector &p);
- float RetHardness(const Math::Vector &p);
+ //! Returns the influence factor whether a position is on a possible rise
+ float GetBuildingFactor(const Math::Vector& p);
+ float GetHardness(const Math::Vector& p);
- int RetMosaic();
- int RetBrick();
- float RetSize();
- float RetScaleRelief();
+ int GetMosaic();
+ int GetBrick();
+ float GetSize();
+ float GetScaleRelief();
+ //! Shows the flat areas on the ground
void GroundFlat(Math::Vector pos);
- float RetFlatZoneRadius(Math::Vector center, float max);
+ //! Calculates the radius of the largest flat area available
+ float GetFlatZoneRadius(Math::Vector center, float max);
+ //@{
+ //! Management of the global max flying height
void SetFlyingMaxHeight(float height);
- float RetFlyingMaxHeight();
+ float GetFlyingMaxHeight();
+ //@}
+ //! Empty the table of flying limits
void FlushFlyingLimit();
- bool AddFlyingLimit(Math::Vector center, float extRadius, float intRadius, float maxHeight);
- float RetFlyingLimit(Math::Vector pos, bool bNoLimit);
+ //! Adds a new flying limit
+ void AddFlyingLimit(Math::Vector center, float extRadius, float intRadius, float maxHeight);
+ //! Returns the maximum height of flight
+ float GetFlyingLimit(Math::Vector pos, bool noLimit);
protected:
+ //! Adds a point of elevation in the buffer of relief
bool ReliefAddDot(Math::Vector pos, float scaleRelief);
+ //! Adjust the edges of each mosaic to be compatible with all lower resolutions
void AdjustRelief();
- Math::Vector RetVector(int x, int y);
- Gfx::VertexTex2 RetVertex(int x, int y, int step);
- bool CreateMosaic(int ox, int oy, int step, int objRank, const Gfx::Material &mat, float min, float max);
- bool CreateSquare(bool bMultiRes, int x, int y);
-
- TerrainMaterial* LevelSearchMat(int id);
- void LevelTextureName(int x, int y, char *name, Math::Point &uv);
- float LevelRetHeight(int x, int y);
+ //! Calculates a vector of the terrain
+ Math::Vector GetVector(int x, int y);
+ //! Calculates a vertex of the terrain
+ Gfx::VertexTex2 GetVertex(int x, int y, int step);
+ //! Creates all objects of a mosaic
+ bool CreateMosaic(int ox, int oy, int step, int objRank, const Gfx::Material& mat, float min, float max);
+ //! Creates all objects in a mesh square ground
+ bool CreateSquare(bool multiRes, int x, int y);
+
+ //! Seeks a materials based on theirs identifier
+ Gfx::TerrainMaterial* LevelSearchMat(int id);
+ //! Chooses texture to use for a given square
+ void LevelTextureName(int x, int y, std::string& name, Math::Point &uv);
+ //! Returns the height of the terrain
+ float LevelGetHeight(int x, int y);
+ //! Decide whether a point is using the materials
bool LevelGetDot(int x, int y, float min, float max, float slope);
+ //! Seeks if material exists
int LevelTestMat(char *mat);
+ //! Modifies the state of a point and its four neighbors, without testing if possible
void LevelSetDot(int x, int y, int id, char *mat);
+ //! Tests if a material can give a place, according to its four neighbors. If yes, puts the point.
bool LevelIfDot(int x, int y, int id, char *mat);
+ //! Modifies the state of a point
bool LevelPutDot(int x, int y, int id);
+ //! Initializes a table with empty levels
void LevelOpenTable();
+ //! Closes the level table
void LevelCloseTable();
+ //! Adjusts a position according to a possible rise
void AdjustBuildingLevel(Math::Vector &p);
protected:
@@ -173,39 +244,49 @@ protected:
CEngine* m_engine;
CWater* m_water;
- int m_mosaic; // number of mosaics
- int m_brick; // number of bricks per mosaics
- float m_size; // size of an item in an brick
- float m_vision; // vision before a change of resolution
- float* m_relief; // table of the relief
- int* m_texture; // table of textures
- int* m_objRank; // table of rows of objects
- bool m_bMultiText;
- bool m_bLevelText;
- float m_scaleMapping; // scale of the mapping
+ //! Number of mosaics
+ int m_mosaic;
+ //! Number of bricks per mosaics
+ int m_brick;
+ int m_levelDotSize;
+ //! Size of an item in a brick
+ float m_size;
+ //! Vision before a change of resolution
+ float m_vision;
+ //! Table of the relief
+ std::vector<float> m_relief;
+ //! Table of textures
+ std::vector<int> m_texture;
+ //! Table of rows of objects
+ std::vector<int> m_objRank;
+ //! Table of resources
+ std::vector<unsigned char> m_resources;
+ bool m_multiText;
+ bool m_levelText;
+ //! Scale of the mapping
+ float m_scaleMapping;
float m_scaleRelief;
- int m_subdivMapping;
- int m_depth; // number of different resolutions (1,2,3,4)
- char m_texBaseName[20];
- char m_texBaseExt[10];
+ int m_subdivMapping;
+ //! Number of different resolutions (1,2,3,4)
+ int m_depth;
+ std::string m_texBaseName;
+ std::string m_texBaseExt;
float m_defHardness;
- TerrainMaterial m_levelMat[MAXMATTERRAIN+1];
- int m_levelMatTotal;
+ std::vector<TerrainMaterial> m_levelMat;
+ std::vector<Gfx::DotLevel> m_levelDot;
int m_levelMatMax;
- int m_levelDotSize;
- DotLevel* m_levelDot;
int m_levelID;
- int m_buildingUsed;
- BuildingLevel m_buildingTable[MAXBUILDINGLEVEL];
+ std::vector<Gfx::BuildingLevel> m_buildingLevels;
- unsigned char* m_resources;
- Math::Vector m_wind; // wind speed
+ //! Wind speed
+ Math::Vector m_wind;
+ //! Global flying height limit
float m_flyingMaxHeight;
- int m_flyingLimitTotal;
- FlyingLimit m_flyingLimit[MAXFLYINGLIMIT];
+ //! List of local flight limits
+ std::vector<Gfx::FlyingLimit> m_flyingLimits;
};
}; // namespace Gfx
diff --git a/src/graphics/engine/text.cpp b/src/graphics/engine/text.cpp
index 2a9543c..82abd62 100644
--- a/src/graphics/engine/text.cpp
+++ b/src/graphics/engine/text.cpp
@@ -19,5 +19,784 @@
#include "graphics/engine/text.h"
+#include "app/app.h"
+#include "common/image.h"
+#include "common/iman.h"
+#include "common/logger.h"
+#include "common/stringutils.h"
+#include "math/func.h"
-// TODO implementation
+#include <SDL/SDL.h>
+#include <SDL/SDL_ttf.h>
+
+
+namespace Gfx
+{
+
+/**
+ \struct CachedFont
+ \brief Base TTF font with UTF-8 char cache */
+struct CachedFont
+{
+ TTF_Font* font;
+ std::map<Gfx::UTF8Char, Gfx::CharTexture> cache;
+
+ CachedFont() : font(nullptr) {}
+};
+
+};
+
+
+
+Gfx::CText::CText(CInstanceManager *iMan, Gfx::CEngine* engine)
+{
+ m_iMan = iMan;
+ m_iMan->AddInstance(CLASS_TEXT, this);
+
+ m_device = nullptr;
+ m_engine = engine;
+
+ m_defaultSize = 12.0f;
+ m_fontPath = "fonts";
+
+ m_lastFontType = Gfx::FONT_COLOBOT;
+ m_lastFontSize = 0;
+ m_lastCachedFont = nullptr;
+}
+
+Gfx::CText::~CText()
+{
+ m_iMan->DeleteInstance(CLASS_TEXT, this);
+
+ m_iMan = nullptr;
+ m_device = nullptr;
+ m_engine = nullptr;
+}
+
+bool Gfx::CText::Create()
+{
+ if (TTF_Init() != 0)
+ {
+ m_error = std::string("TTF_Init error: ") + std::string(TTF_GetError());
+ return false;
+ }
+
+ m_fonts[Gfx::FONT_COLOBOT] = new MultisizeFont("dvu_sans.ttf");
+ m_fonts[Gfx::FONT_COLOBOT_BOLD] = new MultisizeFont("dvu_sans_bold.ttf");
+ m_fonts[Gfx::FONT_COLOBOT_ITALIC] = new MultisizeFont("dvu_sans_italic.ttf");
+
+ m_fonts[Gfx::FONT_COURIER] = new MultisizeFont("dvu_sans_mono.ttf");
+ m_fonts[Gfx::FONT_COURIER_BOLD] = new MultisizeFont("dvu_sans_mono_bold.ttf");
+
+ for (auto it = m_fonts.begin(); it != m_fonts.end(); ++it)
+ {
+ Gfx::FontType type = (*it).first;
+ CachedFont* cf = GetOrOpenFont(type, m_defaultSize);
+ if (cf == nullptr || cf->font == nullptr)
+ return false;
+ }
+
+ return true;
+}
+
+void Gfx::CText::Destroy()
+{
+ for (auto it = m_fonts.begin(); it != m_fonts.end(); ++it)
+ {
+ MultisizeFont* mf = (*it).second;
+
+ for (auto jt = mf->fonts.begin(); jt != mf->fonts.end(); ++jt)
+ {
+ CachedFont* cf = (*jt).second;
+
+ TTF_CloseFont(cf->font);
+
+ cf->font = nullptr;
+ delete cf;
+ }
+
+ mf->fonts.clear();
+ delete mf;
+ }
+
+ m_fonts.clear();
+
+ m_lastCachedFont = nullptr;
+
+ TTF_Quit();
+}
+
+void Gfx::CText::SetDevice(Gfx::CDevice* device)
+{
+ m_device = device;
+}
+
+std::string Gfx::CText::GetError()
+{
+ return m_error;
+}
+
+void Gfx::CText::FlushCache()
+{
+ for (auto it = m_fonts.begin(); it != m_fonts.end(); ++it)
+ {
+ MultisizeFont *mf = (*it).second;
+ for (auto jt = mf->fonts.begin(); jt != mf->fonts.end(); ++jt)
+ {
+ CachedFont *f = (*jt).second;
+ f->cache.clear();
+ }
+ }
+}
+
+void Gfx::CText::DrawText(const std::string &text, const std::vector<FontMetaChar> &format,
+ float size, Math::Point pos, float width, Gfx::TextAlign align,
+ int eol)
+{
+ float sw = 0.0f;
+
+ if (align == Gfx::TEXT_ALIGN_CENTER)
+ {
+ sw = GetStringWidth(text, format, size);
+ if (sw > width) sw = width;
+ pos.x -= sw / 2.0f;
+ }
+ else if (align == Gfx::TEXT_ALIGN_RIGHT)
+ {
+ sw = GetStringWidth(text, format, size);
+ if (sw > width) sw = width;
+ pos.x -= sw;
+ }
+
+ DrawString(text, format, size, pos, width, eol);
+}
+
+void Gfx::CText::DrawText(const std::string &text, Gfx::FontType font,
+ float size, Math::Point pos, float width, Gfx::TextAlign align,
+ int eol)
+{
+ float sw = 0.0f;
+
+ if (align == Gfx::TEXT_ALIGN_CENTER)
+ {
+ sw = GetStringWidth(text, font, size);
+ if (sw > width) sw = width;
+ pos.x -= sw / 2.0f;
+ }
+ else if (align == Gfx::TEXT_ALIGN_RIGHT)
+ {
+ sw = GetStringWidth(text, font, size);
+ if (sw > width) sw = width;
+ pos.x -= sw;
+ }
+
+ DrawString(text, font, size, pos, width, eol);
+}
+
+void Gfx::CText::SizeText(const std::string &text, const std::vector<FontMetaChar> &format,
+ float size, Math::Point pos, Gfx::TextAlign align,
+ Math::Point &start, Math::Point &end)
+{
+ start = end = pos;
+
+ float sw = GetStringWidth(text, format, size);
+ end.x += sw;
+ if (align == Gfx::TEXT_ALIGN_CENTER)
+ {
+ start.x -= sw/2.0f;
+ end.x -= sw/2.0f;
+ }
+ else if (align == Gfx::TEXT_ALIGN_RIGHT)
+ {
+ start.x -= sw;
+ end.x -= sw;
+ }
+
+ start.y -= GetDescent(Gfx::FONT_COLOBOT, size);
+ end.y += GetAscent(Gfx::FONT_COLOBOT, size);
+}
+
+void Gfx::CText::SizeText(const std::string &text, Gfx::FontType font,
+ float size, Math::Point pos, Gfx::TextAlign align,
+ Math::Point &start, Math::Point &end)
+{
+ start = end = pos;
+
+ float sw = GetStringWidth(text, font, size);
+ end.x += sw;
+ if (align == Gfx::TEXT_ALIGN_CENTER)
+ {
+ start.x -= sw/2.0f;
+ end.x -= sw/2.0f;
+ }
+ else if (align == Gfx::TEXT_ALIGN_RIGHT)
+ {
+ start.x -= sw;
+ end.x -= sw;
+ }
+
+ start.y -= GetDescent(font, size);
+ end.y += GetAscent(font, size);
+}
+
+float Gfx::CText::GetAscent(Gfx::FontType font, float size)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ Gfx::CachedFont* cf = GetOrOpenFont(font, size);
+ assert(cf != nullptr);
+ Math::IntPoint wndSize;
+ wndSize.y = TTF_FontAscent(cf->font);
+ Math::Point ifSize = m_engine->WindowToInterfaceSize(wndSize);
+ return ifSize.y;
+}
+
+float Gfx::CText::GetDescent(Gfx::FontType font, float size)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ Gfx::CachedFont* cf = GetOrOpenFont(font, size);
+ assert(cf != nullptr);
+ Math::IntPoint wndSize;
+ wndSize.y = TTF_FontDescent(cf->font);
+ Math::Point ifSize = m_engine->WindowToInterfaceSize(wndSize);
+ return ifSize.y;
+}
+
+float Gfx::CText::GetHeight(Gfx::FontType font, float size)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ Gfx::CachedFont* cf = GetOrOpenFont(font, size);
+ assert(cf != nullptr);
+ Math::IntPoint wndSize;
+ wndSize.y = TTF_FontHeight(cf->font);
+ Math::Point ifSize = m_engine->WindowToInterfaceSize(wndSize);
+ return ifSize.y;
+}
+
+
+float Gfx::CText::GetStringWidth(const std::string &text,
+ const std::vector<FontMetaChar> &format, float size)
+{
+ assert(StrUtils::Utf8StringLength(text) == format.size());
+
+ float width = 0.0f;
+ unsigned int index = 0;
+ unsigned int fmtIndex = 0;
+ while (index < text.length())
+ {
+ Gfx::FontType font = static_cast<Gfx::FontType>(format[fmtIndex] & Gfx::FONT_MASK_FONT);
+
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ width += GetCharWidth(ch, font, size, width);
+
+ index += len;
+ fmtIndex++;
+ }
+
+ return width;
+}
+
+float Gfx::CText::GetStringWidth(const std::string &text, Gfx::FontType font, float size)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ // TODO: special chars?
+
+ Gfx::CachedFont* cf = GetOrOpenFont(font, size);
+ assert(cf != nullptr);
+ Math::IntPoint wndSize;
+ TTF_SizeUTF8(cf->font, text.c_str(), &wndSize.x, &wndSize.y);
+ Math::Point ifSize = m_engine->WindowToInterfaceSize(wndSize);
+ return ifSize.x;
+}
+
+float Gfx::CText::GetCharWidth(Gfx::UTF8Char ch, Gfx::FontType font, float size, float offset)
+{
+ // TODO: if (font == Gfx::FONT_BUTTON)
+ if (font == Gfx::FONT_BUTTON) return 0.0f;
+
+ // TODO: special chars?
+ // TODO: tab sizing
+
+ Gfx::CachedFont* cf = GetOrOpenFont(font, size);
+ assert(cf != nullptr);
+
+ Gfx::CharTexture tex;
+ auto it = cf->cache.find(ch);
+ if (it != cf->cache.end())
+ tex = (*it).second;
+ else
+ tex = CreateCharTexture(ch, cf);
+
+ return tex.charSize.x;
+}
+
+
+int Gfx::CText::Justify(const std::string &text, const std::vector<FontMetaChar> &format,
+ float size, float width)
+{
+ assert(StrUtils::Utf8StringLength(text) == format.size());
+
+ float pos = 0.0f;
+ int cut = 0;
+ unsigned int index = 0;
+ unsigned int fmtIndex = 0;
+ while (index < text.length())
+ {
+ Gfx::FontType font = static_cast<Gfx::FontType>(format[fmtIndex] & Gfx::FONT_MASK_FONT);
+
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ if (font != Gfx::FONT_BUTTON)
+ {
+ if (ch.c1 == '\n')
+ return index+1;
+ if (ch.c1 == ' ')
+ cut = index+1;
+ }
+
+ pos += GetCharWidth(ch, font, size, pos);
+ if (pos > width)
+ {
+ if (cut == 0) return index;
+ else return cut;
+ }
+
+ index += len;
+ fmtIndex++;
+ }
+
+ return index;
+}
+
+int Gfx::CText::Justify(const std::string &text, Gfx::FontType font, float size, float width)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ float pos = 0.0f;
+ int cut = 0;
+ unsigned int index = 0;
+ while (index < text.length())
+ {
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ index += len;
+
+ if (ch.c1 == '\n')
+ return index+1;
+
+ if (ch.c1 == ' ' )
+ cut = index+1;
+
+ pos += GetCharWidth(ch, font, size, pos);
+ if (pos > width)
+ {
+ if (cut == 0) return index;
+ else return cut;
+ }
+ }
+
+ return index;
+}
+
+int Gfx::CText::Detect(const std::string &text, const std::vector<FontMetaChar> &format,
+ float size, float offset)
+{
+ assert(StrUtils::Utf8StringLength(text) == format.size());
+
+ float pos = 0.0f;
+ unsigned int index = 0;
+ unsigned int fmtIndex = 0;
+ while (index < text.length())
+ {
+ Gfx::FontType font = static_cast<Gfx::FontType>(format[fmtIndex] & Gfx::FONT_MASK_FONT);
+
+ // TODO: if (font == Gfx::FONT_BUTTON)
+ if (font == Gfx::FONT_BUTTON) continue;
+
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ if (ch.c1 == '\n')
+ return index;
+
+ float width = GetCharWidth(ch, font, size, pos);
+ if (offset <= pos + width/2.0f)
+ return index;
+
+ pos += width;
+ index += len;
+ fmtIndex++;
+ }
+
+ return index;
+}
+
+int Gfx::CText::Detect(const std::string &text, Gfx::FontType font, float size, float offset)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ float pos = 0.0f;
+ unsigned int index = 0;
+ while (index < text.length())
+ {
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ index += len;
+
+ if (ch.c1 == '\n')
+ return index;
+
+ float width = GetCharWidth(ch, font, size, pos);
+ if (offset <= pos + width/2.0f)
+ return index;
+
+ pos += width;
+ }
+
+ return index;
+}
+
+void Gfx::CText::DrawString(const std::string &text, const std::vector<FontMetaChar> &format,
+ float size, Math::Point pos, float width, int eol)
+{
+ assert(StrUtils::Utf8StringLength(text) == format.size());
+
+ m_engine->SetState(Gfx::ENG_RSTATE_TEXT);
+
+ Gfx::FontType font = Gfx::FONT_COLOBOT;
+ float start = pos.x;
+
+ unsigned int index = 0;
+ unsigned int fmtIndex = 0;
+ while (index < text.length())
+ {
+ font = static_cast<Gfx::FontType>(format[fmtIndex] & Gfx::FONT_MASK_FONT);
+
+ // TODO: if (font == Gfx::FONT_BUTTON)
+ if (font == Gfx::FONT_BUTTON) continue;
+
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ float offset = pos.x - start;
+ float cw = GetCharWidth(ch, font, size, offset);
+ if (offset + cw > width) // exceeds the maximum width?
+ {
+ // TODO: special end-of-line char
+ break;
+ }
+
+ Gfx::FontHighlight hl = static_cast<Gfx::FontHighlight>(format[fmtIndex] & Gfx::FONT_MASK_HIGHLIGHT);
+ if (hl != Gfx::FONT_HIGHLIGHT_NONE)
+ {
+ Math::Point charSize;
+ charSize.x = GetCharWidth(ch, font, size, offset);
+ charSize.y = GetHeight(font, size);
+ DrawHighlight(hl, pos, charSize);
+ }
+
+ DrawChar(ch, font, size, pos);
+
+ index += len;
+ fmtIndex++;
+ }
+
+ // TODO: eol
+}
+
+void Gfx::CText::DrawString(const std::string &text, Gfx::FontType font,
+ float size, Math::Point pos, float width, int eol)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ m_engine->SetState(Gfx::ENG_RSTATE_TEXT);
+
+ unsigned int index = 0;
+ while (index < text.length())
+ {
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ index += len;
+
+ DrawChar(ch, font, size, pos);
+ }
+}
+
+void Gfx::CText::DrawHighlight(Gfx::FontHighlight hl, Math::Point pos, Math::Point size)
+{
+ // Gradient colors
+ Gfx::Color grad[4];
+
+ // TODO: switch to alpha factors
+
+ switch (hl)
+ {
+ case Gfx::FONT_HIGHLIGHT_LINK:
+ grad[0] = grad[1] = grad[2] = grad[3] = Gfx::Color(0.0f, 0.0f, 1.0f, 0.5f);
+ break;
+
+ case Gfx::FONT_HIGHLIGHT_TOKEN:
+ grad[0] = grad[1] = Gfx::Color(248.0f / 256.0f, 248.0f / 256.0f, 248.0f / 256.0f, 0.5f);
+ grad[2] = grad[3] = Gfx::Color(248.0f / 256.0f, 220.0f / 256.0f, 188.0f / 256.0f, 0.5f);
+ break;
+
+ case Gfx::FONT_HIGHLIGHT_TYPE:
+ grad[0] = grad[1] = Gfx::Color(248.0f / 256.0f, 248.0f / 256.0f, 248.0f / 256.0f, 0.5f);
+ grad[2] = grad[3] = Gfx::Color(169.0f / 256.0f, 234.0f / 256.0f, 169.0f / 256.0f, 0.5f);
+ break;
+
+ case Gfx::FONT_HIGHLIGHT_CONST:
+ grad[0] = grad[1] = Gfx::Color(248.0f / 256.0f, 248.0f / 256.0f, 248.0f / 256.0f, 0.5f);
+ grad[2] = grad[3] = Gfx::Color(248.0f / 256.0f, 176.0f / 256.0f, 169.0f / 256.0f, 0.5f);
+ break;
+
+ case Gfx::FONT_HIGHLIGHT_REM:
+ grad[0] = grad[1] = Gfx::Color(248.0f / 256.0f, 248.0f / 256.0f, 248.0f / 256.0f, 0.5f);
+ grad[2] = grad[3] = Gfx::Color(248.0f / 256.0f, 169.0f / 256.0f, 248.0f / 256.0f, 0.5f);
+ break;
+
+ case Gfx::FONT_HIGHLIGHT_KEY:
+ grad[0] = grad[1] = grad[2] = grad[3] =
+ Gfx::Color(192.0f / 256.0f, 192.0f / 256.0f, 192.0f / 256.0f, 0.5f);
+ break;
+
+ default:
+ return;
+ }
+
+ Math::IntPoint vsize = m_engine->GetWindowSize();
+ float h = 0.0f;
+ if (vsize.y <= 768.0f) // 1024x768 or less?
+ h = 1.01f / vsize.y; // 1 pixel
+ else // more than 1024x768?
+ h = 2.0f / vsize.y; // 2 pixels
+
+ Math::Point p1, p2;
+ p1.x = pos.x;
+ p2.x = pos.x + size.x;
+
+ if (hl == Gfx::FONT_HIGHLIGHT_LINK)
+ {
+ p1.y = pos.y;
+ p2.y = pos.y + h; // just emphasized
+ }
+ else
+ {
+ p1.y = pos.y;
+ p2.y = pos.y + size.y;
+ }
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, false);
+
+ Gfx::VertexCol quad[] =
+ {
+ Gfx::VertexCol(Math::Vector(p1.x, p1.y, 0.0f), grad[3]),
+ Gfx::VertexCol(Math::Vector(p1.x, p2.y, 0.0f), grad[0]),
+ Gfx::VertexCol(Math::Vector(p2.x, p1.y, 0.0f), grad[2]),
+ Gfx::VertexCol(Math::Vector(p2.x, p2.y, 0.0f), grad[1])
+ };
+
+ m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, quad, 4);
+ m_engine->AddStatisticTriangle(2);
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+}
+
+void Gfx::CText::DrawChar(Gfx::UTF8Char ch, Gfx::FontType font, float size, Math::Point &pos)
+{
+ // TODO: if (font == Gfx::FONT_BUTTON)
+ if (font == Gfx::FONT_BUTTON) return;
+
+ // TODO: special chars?
+
+ CachedFont* cf = GetOrOpenFont(font, size);
+
+ if (cf == nullptr)
+ return;
+
+ auto it = cf->cache.find(ch);
+ CharTexture tex;
+ if (it != cf->cache.end())
+ {
+ tex = (*it).second;
+ }
+ else
+ {
+ tex = CreateCharTexture(ch, cf);
+
+ if (tex.id == 0) // invalid
+ return;
+
+ cf->cache[ch] = tex;
+ }
+
+ Math::Point p1(pos.x, pos.y + tex.charSize.y - tex.texSize.y);
+ Math::Point p2(pos.x + tex.texSize.x, pos.y + tex.charSize.y);
+
+ Math::Vector n(0.0f, 0.0f, -1.0f); // normal
+
+ Gfx::Vertex quad[4] =
+ {
+ Gfx::Vertex(Math::Vector(p1.x, p1.y, 0.0f), n, Math::Point(0.0f, 1.0f)),
+ Gfx::Vertex(Math::Vector(p1.x, p2.y, 0.0f), n, Math::Point(0.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector(p2.x, p1.y, 0.0f), n, Math::Point(1.0f, 1.0f)),
+ Gfx::Vertex(Math::Vector(p2.x, p2.y, 0.0f), n, Math::Point(1.0f, 0.0f))
+ };
+
+ m_device->SetTexture(0, tex.id);
+ m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, quad, 4);
+ m_engine->AddStatisticTriangle(2);
+
+ pos.x += tex.charSize.x;
+}
+
+Gfx::CachedFont* Gfx::CText::GetOrOpenFont(Gfx::FontType font, float size)
+{
+ // TODO: sizing
+ int pointSize = static_cast<int>(size);
+
+ if (m_lastCachedFont != nullptr)
+ {
+ if (m_lastFontType == font && m_lastFontSize == pointSize)
+ return m_lastCachedFont;
+ }
+
+ auto it = m_fonts.find(font);
+ if (it == m_fonts.end())
+ {
+ m_error = std::string("Invalid font type ") + StrUtils::ToString<int>(static_cast<int>(font));
+ return nullptr;
+ }
+
+ MultisizeFont* mf = (*it).second;
+
+ auto jt = mf->fonts.find(pointSize);
+ if (jt != mf->fonts.end())
+ {
+ m_lastCachedFont = (*jt).second;
+ m_lastFontType = font;
+ m_lastFontSize = pointSize;
+ return m_lastCachedFont;
+ }
+
+ std::string path = CApplication::GetInstance().GetDataFilePath(m_fontPath, mf->fileName);
+
+ m_lastCachedFont = new CachedFont();
+ m_lastCachedFont->font = TTF_OpenFont(path.c_str(), pointSize);
+ if (m_lastCachedFont->font == nullptr)
+ m_error = std::string("TTF_OpenFont error ") + std::string(TTF_GetError());
+
+ mf->fonts[pointSize] = m_lastCachedFont;
+
+ return m_lastCachedFont;
+}
+
+Gfx::CharTexture Gfx::CText::CreateCharTexture(Gfx::UTF8Char ch, Gfx::CachedFont* font)
+{
+ CharTexture texture;
+
+ SDL_Surface* textSurface = nullptr;
+ SDL_Color white = {255, 255, 255, 0};
+ char str[] = { ch.c1, ch.c2, ch.c3, '\0' };
+ textSurface = TTF_RenderUTF8_Blended(font->font, str, white);
+
+ if (textSurface == nullptr)
+ {
+ m_error = "TTF_Render error";
+ return texture;
+ }
+
+ int w = Math::NextPowerOfTwo(textSurface->w);
+ int h = Math::NextPowerOfTwo(textSurface->h);
+
+ textSurface->flags = textSurface->flags & (~SDL_SRCALPHA);
+ SDL_Surface* textureSurface = SDL_CreateRGBSurface(0, w, h, 32, 0x00ff0000, 0x0000ff00,
+ 0x000000ff, 0xff000000);
+ SDL_BlitSurface(textSurface, NULL, textureSurface, NULL);
+
+ ImageData data;
+ data.surface = textureSurface;
+
+ Gfx::TextureCreateParams createParams;
+ createParams.format = Gfx::TEX_IMG_RGBA;
+ createParams.minFilter = Gfx::TEX_MIN_FILTER_NEAREST;
+ createParams.magFilter = Gfx::TEX_MAG_FILTER_NEAREST;
+ createParams.mipmap = false;
+
+ Gfx::Texture tex = m_device->CreateTexture(&data, createParams);
+
+ data.surface = nullptr;
+
+ SDL_FreeSurface(textSurface);
+ SDL_FreeSurface(textureSurface);
+
+ if (! tex.valid)
+ {
+ m_error = "Texture create error";
+ return texture;
+ }
+
+ texture.id = tex.id;
+ texture.texSize = m_engine->WindowToInterfaceSize(Math::IntPoint(textureSurface->w, textureSurface->h));
+ texture.charSize = m_engine->WindowToInterfaceSize(Math::IntPoint(textSurface->w, textSurface->h));
+
+ return texture;
+}
diff --git a/src/graphics/engine/text.h b/src/graphics/engine/text.h
index c2de220..24251ab 100644
--- a/src/graphics/engine/text.h
+++ b/src/graphics/engine/text.h
@@ -15,99 +15,287 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// text.h
+/**
+ * \file graphics/engine/text.h
+ * \brief Text rendering - Gfx::CText class
+ */
#pragma once
-#include "graphics/engine/engine.h"
-#include "graphics/core/device.h"
#include "math/point.h"
+#include <vector>
+#include <map>
class CInstanceManager;
-
namespace Gfx {
-const float SMALLFONT = 10.0f;
-const float BIGFONT = 15.0f;
+class CEngine;
+class CDevice;
+
+//! Standard small font size
+const float FONT_SIZE_SMALL = 10.0f;
+//! Standard big font size
+const float FONT_SIZE_BIG = 15.0f;
+
+/**
+ \enum TextAlign
+ \brief Type of text alignment */
+enum TextAlign
+{
+ TEXT_ALIGN_RIGHT,
+ TEXT_ALIGN_LEFT,
+ TEXT_ALIGN_CENTER
+};
-const float NORMSTRETCH = 0.8f;
+/* Font meta char constants */
+//! Type used for font character metainfo
+typedef short FontMetaChar;
+/**
+ \enum FontType
+ \brief Type of font
+ Bitmask in lower 4 bits (mask 0x00f) */
enum FontType
{
- FONT_COLOBOT = 0,
- FONT_COURIER = 1,
- FONT_BUTTON = 2,
+ //! Flag for bold font subtype
+ FONT_BOLD = 0x04,
+ //! Flag for italic font subtype
+ FONT_ITALIC = 0x08,
+
+ //! Default colobot font used for interface
+ FONT_COLOBOT = 0x00,
+ //! Alias for bold colobot font
+ FONT_COLOBOT_BOLD = FONT_COLOBOT | FONT_BOLD,
+ //! Alias for italic colobot font
+ FONT_COLOBOT_ITALIC = FONT_COLOBOT | FONT_ITALIC,
+
+ //! Courier (monospace) font used mainly in code editor (only regular & bold)
+ FONT_COURIER = 0x01,
+ //! Alias for bold courier font
+ FONT_COURIER_BOLD = FONT_COURIER | FONT_BOLD,
+
+ // 0x02 left for possible another font
+
+ //! Pseudo-font loaded from textures for buttons, icons, etc.
+ FONT_BUTTON = 0x03,
};
+/**
+ \enum FontTitle
+ \brief Size of font title
+
+ Used internally by CEdit
+
+ Bitmask in 2 bits left shifted 4 (mask 0x030) */
enum FontTitle
{
- TITLE_BIG = 0x04,
- TITLE_NORM = 0x08,
- TITLE_LITTLE = 0x0c,
+ FONT_TITLE_BIG = 0x01 << 4,
+ FONT_TITLE_NORM = 0x02 << 4,
+ FONT_TITLE_LITTLE = 0x03 << 4,
};
-enum FontColor
+/**
+ \enum FontHighlight
+ \brief Type of color highlight for text
+
+ Bitmask in 3 bits left shifted 6 (mask 0x1c0) */
+enum FontHighlight
+{
+ FONT_HIGHLIGHT_NONE = 0x00 << 6,
+ FONT_HIGHLIGHT_LINK = 0x01 << 6,
+ FONT_HIGHLIGHT_TOKEN = 0x02 << 6,
+ FONT_HIGHLIGHT_TYPE = 0x03 << 6,
+ FONT_HIGHLIGHT_CONST = 0x04 << 6,
+ FONT_HIGHLIGHT_REM = 0x05 << 6,
+ FONT_HIGHLIGHT_KEY = 0x06 << 6,
+ FONT_HIGHLIGHT_TABLE = 0x07 << 6,
+};
+
+/**
+ \enum FontMask
+ \brief Masks in FontMetaChar for different attributes */
+enum FontMask
{
- COLOR_LINK = 0x10,
- COLOR_TOKEN = 0x20,
- COLOR_TYPE = 0x30,
- COLOR_CONST = 0x40,
- COLOR_REM = 0x50,
- COLOR_KEY = 0x60,
- COLOR_TABLE = 0x70,
+ //! Mask for FontType
+ FONT_MASK_FONT = 0x00f,
+ //! Mask for FontTitle
+ FONT_MASK_TITLE = 0x030,
+ //! Mask for FontHighlight
+ FONT_MASK_HIGHLIGHT = 0x1c0,
+ //! Mask for image bit (TODO: not used?)
+ FONT_MASK_IMAGE = 0x200
};
-const short FONT_MASK = 0x03;
-const short TITLE_MASK = 0x0c;
-const short COLOR_MASK = 0x70;
-const short IMAGE_MASK = 0x80;
+/**
+ \struct UTF8Char
+ \brief UTF-8 character in font cache
+
+ Only 3-byte chars are supported */
+struct UTF8Char
+{
+ char c1, c2, c3;
+ explicit UTF8Char(char ch1 = '\0', char ch2 = '\0', char ch3 = '\0')
+ : c1(ch1), c2(ch2), c3(ch3) {}
+
+ inline bool operator<(const UTF8Char &other) const
+ {
+ if (c1 < other.c1)
+ return true;
+ else if (c1 > other.c1)
+ return false;
+
+ if (c2 < other.c2)
+ return true;
+ else if (c2 > other.c2)
+ return false;
+
+ return c3 < other.c3;
+ }
+
+ inline bool operator==(const UTF8Char &other) const
+ {
+ return c1 == other.c1 && c2 == other.c2 && c3 == other.c3;
+ }
+};
-class CText {
+/**
+ \struct CharTexture
+ \brief Texture of font character */
+struct CharTexture
+{
+ unsigned int id;
+ Math::Point texSize;
+ Math::Point charSize;
+
+ CharTexture() : id(0) {}
+};
+
+// Definition is private - in text.cpp
+struct CachedFont;
+
+/**
+ \struct MultisizeFont
+ \brief Font with multiple possible sizes */
+struct MultisizeFont
+{
+ std::string fileName;
+ std::map<int, CachedFont*> fonts;
+
+ MultisizeFont(const std::string &fn)
+ : fileName(fn) {}
+};
+
+/**
+ \class CText
+ \brief Text rendering engine
+
+ CText is responsible for drawing text in 2D interface. Font rendering is done using
+ textures generated by SDL_ttf from TTF font files.
+
+ All functions rendering text are divided into two types:
+ - single font - function takes a single Gfx::FontType argument that (along with size)
+ determines the font to be used for all characters,
+ - multi-font - function takes the text as one argument and a std::vector of FontMetaChar
+ with per-character formatting information (font, highlights and some other info used by CEdit)
+
+ All font rendering is done in UTF-8.
+*/
+class CText
+{
public:
CText(CInstanceManager *iMan, Gfx::CEngine* engine);
~CText();
+ //! Sets the device to be used
void SetDevice(Gfx::CDevice *device);
- void DrawText(char *string, char *format, int len, Math::Point pos, float width, int justif, float size, float stretch, int eol);
- void DrawText(char *string, char *format, Math::Point pos, float width, int justif, float size, float stretch, int eol);
- void DrawText(char *string, int len, Math::Point pos, float width, int justif, float size, float stretch, FontType font, int eol);
- void DrawText(char *string, Math::Point pos, float width, int justif, float size, float stretch, FontType font, int eol);
- void DimText(char *string, char *format, int len, Math::Point pos, int justif, float size, float stretch, Math::Point &start, Math::Point &end);
- void DimText(char *string, char *format, Math::Point pos, int justif, float size, float stretch, Math::Point &start, Math::Point &end);
- void DimText(char *string, int len, Math::Point pos, int justif, float size, float stretch, FontType font, Math::Point &start, Math::Point &end);
- void DimText(char *string, Math::Point pos, int justif, float size, float stretch, FontType font, Math::Point &start, Math::Point &end);
+ //! Returns the last encountered error
+ std::string GetError();
+
+ //! Initializes the font engine; must be called after SetDevice()
+ bool Create();
+ //! Frees resources before exit
+ void Destroy();
+
+ //! Flushes cached textures
+ void FlushCache();
+
+ //! Draws text (multi-format)
+ void DrawText(const std::string &text, const std::vector<Gfx::FontMetaChar> &format,
+ float size, Math::Point pos, float width, Gfx::TextAlign align,
+ int eol);
+ //! Draws text (one font)
+ void DrawText(const std::string &text, Gfx::FontType font,
+ float size, Math::Point pos, float width, Gfx::TextAlign align,
+ int eol);
- float RetAscent(float size, FontType font);
- float RetDescent(float size, FontType font);
- float RetHeight(float size, FontType font);
+ //! Calculates dimensions for text (multi-format)
+ void SizeText(const std::string &text, const std::vector<Gfx::FontMetaChar> &format,
+ float size, Math::Point pos, Gfx::TextAlign align,
+ Math::Point &start, Math::Point &end);
+ //! Calculates dimensions for text (one font)
+ void SizeText(const std::string &text, Gfx::FontType font,
+ float size, Math::Point pos, Gfx::TextAlign align,
+ Math::Point &start, Math::Point &end);
- float RetStringWidth(char *string, char *format, int len, float size, float stretch);
- float RetStringWidth(char *string, int len, float size, float stretch, FontType font);
- float RetCharWidth(int character, float offset, float size, float stretch, FontType font);
+ //! Returns the ascent font metric
+ float GetAscent(Gfx::FontType font, float size);
+ //! Returns the descent font metric
+ float GetDescent(Gfx::FontType font, float size);
+ //! Returns the height font metric
+ float GetHeight(Gfx::FontType font, float size);
- int Justif(char *string, char *format, int len, float width, float size, float stretch);
- int Justif(char *string, int len, float width, float size, float stretch, FontType font);
- int Detect(char *string, char *format, int len, float offset, float size, float stretch);
- int Detect(char *string, int len, float offset, float size, float stretch, FontType font);
+ //! Returns width of string (multi-format)
+ float GetStringWidth(const std::string &text,
+ const std::vector<Gfx::FontMetaChar> &format, float size);
+ //! Returns width of string (single font)
+ float GetStringWidth(const std::string &text, Gfx::FontType font, float size);
+ //! Returns width of single character
+ float GetCharWidth(Gfx::UTF8Char ch, Gfx::FontType font, float size, float offset);
+
+ //! Justifies a line of text (multi-format)
+ int Justify(const std::string &text, const std::vector<Gfx::FontMetaChar> &format,
+ float size, float width);
+ //! Justifies a line of text (one font)
+ int Justify(const std::string &text, Gfx::FontType font, float size, float width);
+
+ //! Returns the most suitable position to a given offset (multi-format)
+ int Detect(const std::string &text, const std::vector<Gfx::FontMetaChar> &format,
+ float size, float offset);
+ //! Returns the most suitable position to a given offset (one font)
+ int Detect(const std::string &text, Gfx::FontType font, float size, float offset);
protected:
- void DrawString(char *string, char *format, int len, Math::Point pos, float width, float size, float stretch, int eol);
- void DrawString(char *string, int len, Math::Point pos, float width, float size, float stretch, FontType font, int eol);
- void DrawColor(Math::Point pos, float size, float width, int color);
- void DrawChar(int character, Math::Point pos, float size, float stretch, FontType font);
+ Gfx::CachedFont* GetOrOpenFont(Gfx::FontType type, float size);
+ Gfx::CharTexture CreateCharTexture(Gfx::UTF8Char ch, Gfx::CachedFont* font);
+
+ void DrawString(const std::string &text, const std::vector<Gfx::FontMetaChar> &format,
+ float size, Math::Point pos, float width, int eol);
+ void DrawString(const std::string &text, Gfx::FontType font,
+ float size, Math::Point pos, float width, int eol);
+ void DrawHighlight(Gfx::FontHighlight hl, Math::Point pos, Math::Point size);
+ void DrawChar(Gfx::UTF8Char ch, Gfx::FontType font, float size, Math::Point &pos);
protected:
CInstanceManager* m_iMan;
Gfx::CEngine* m_engine;
Gfx::CDevice* m_device;
+ std::string m_error;
+ float m_defaultSize;
+ std::string m_fontPath;
+
+ std::map<Gfx::FontType, Gfx::MultisizeFont*> m_fonts;
+
+ Gfx::FontType m_lastFontType;
+ int m_lastFontSize;
+ Gfx::CachedFont* m_lastCachedFont;
};
}; // namespace Gfx
diff --git a/src/graphics/engine/water.cpp b/src/graphics/engine/water.cpp
index a157e82..8fbd1ae 100644
--- a/src/graphics/engine/water.cpp
+++ b/src/graphics/engine/water.cpp
@@ -19,5 +19,638 @@
#include "graphics/engine/water.h"
+#include "common/iman.h"
+#include "graphics/engine/engine.h"
+#include "graphics/engine/terrain.h"
+#include "sound/sound.h"
+
+
+const int WATERLINE_PREALLOCATE_COUNT = 500;
+
+// TODO: remove the limit?
+const int VAPOR_SIZE = 10;
+
+
+Gfx::CWater::CWater(CInstanceManager* iMan, Gfx::CEngine* engine)
+{
+ m_iMan = iMan;
+ m_iMan->AddInstance(CLASS_WATER, this);
+
+ m_engine = engine;
+ m_terrain = nullptr;
+ m_particule = nullptr;
+ m_sound = nullptr;
+
+ m_type[0] = WATER_NULL;
+ m_type[1] = WATER_NULL;
+ m_level = 0.0f;
+ m_draw = true;
+ m_lava = false;
+ m_color = 0xffffffff;
+ m_subdiv = 4;
+
+ m_line.reserve(WATERLINE_PREALLOCATE_COUNT);
+
+ std::vector<Gfx::WaterVapor>(VAPOR_SIZE).swap(m_vapor);
+}
+
+Gfx::CWater::~CWater()
+{
+ m_iMan = nullptr;
+ m_engine = nullptr;
+ m_terrain = nullptr;
+ m_particule = nullptr;
+ m_sound = nullptr;
+}
+
+
+bool Gfx::CWater::EventProcess(const Event &event)
+{
+ if (event.type == EVENT_FRAME)
+ return EventFrame(event);
+
+ return true;
+}
+
+bool Gfx::CWater::EventFrame(const Event &event)
+{
+ if (m_engine->GetPause()) return true;
+
+ m_time += event.rTime;
+
+ if (m_type[0] == WATER_NULL) return true;
+
+ if (m_lava)
+ LavaFrame(event.rTime);
+
+ return true;
+}
+
+void Gfx::CWater::LavaFrame(float rTime)
+{
+ if (m_particule == nullptr)
+ m_particule = static_cast<Gfx::CParticle*>( m_iMan->SearchInstance(CLASS_PARTICULE) );
+
+ for (int i = 0; i < static_cast<int>( m_vapor.size() ); i++)
+ VaporFrame(i, rTime);
+
+ if (m_time - m_lastLava >= 0.1f)
+ {
+ Math::Vector eye = m_engine->GetEyePt();
+ Math::Vector lookat = m_engine->GetLookatPt();
+
+ float distance = Math::Rand()*200.0f;
+ float shift = (Math::Rand()-0.5f)*200.0f;
+
+ Math::Vector dir = Normalize(lookat-eye);
+ Math::Vector pos = eye + dir*distance;
+
+ Math::Vector perp;
+ perp.x = -dir.z;
+ perp.y = dir.y;
+ perp.z = dir.x;
+ pos = pos + perp*shift;
+
+ float level = m_terrain->GetFloorLevel(pos, true);
+ if (level < m_level)
+ {
+ pos.y = m_level;
+
+ level = Math::Rand();
+ if (level < 0.8f)
+ {
+ if ( VaporCreate(Gfx::PARTIFIRE, pos, 0.02f+Math::Rand()*0.06f) )
+ m_lastLava = m_time;
+ }
+ else if (level < 0.9f)
+ {
+ if ( VaporCreate(Gfx::PARTIFLAME, pos, 0.5f+Math::Rand()*3.0f) )
+ m_lastLava = m_time;
+ }
+ else
+ {
+ if ( VaporCreate(Gfx::PARTIVAPOR, pos, 0.2f+Math::Rand()*2.0f) )
+ m_lastLava = m_time;
+ }
+ }
+ }
+}
+
+void Gfx::CWater::VaporFlush()
+{
+ m_vapor.clear();
+}
+
+bool Gfx::CWater::VaporCreate(Gfx::ParticleType type, Math::Vector pos, float delay)
+{
+ for (int i = 0; i < static_cast<int>( m_vapor.size() ); i++)
+ {
+ if (! m_vapor[i].used)
+ {
+ m_vapor[i].used = true;
+ m_vapor[i].type = type;
+ m_vapor[i].pos = pos;
+ m_vapor[i].delay = delay;
+ m_vapor[i].time = 0.0f;
+ m_vapor[i].last = 0.0f;
+
+ if (m_vapor[i].type == PARTIFIRE)
+ m_sound->Play(SOUND_BLUP, pos, 1.0f, 1.0f-Math::Rand()*0.5f);
+
+ if (m_vapor[i].type == PARTIVAPOR)
+ m_sound->Play(SOUND_PSHHH, pos, 0.3f, 2.0f);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Gfx::CWater::VaporFrame(int i, float rTime)
+{
+ m_vapor[i].time += rTime;
+
+ if (m_sound == nullptr)
+ m_sound = static_cast<CSoundInterface*>(m_iMan->SearchInstance(CLASS_SOUND));
+
+ if (m_vapor[i].time <= m_vapor[i].delay)
+ {
+ if (m_time-m_vapor[i].last >= m_engine->ParticleAdapt(0.02f))
+ {
+ m_vapor[i].last = m_time;
+
+ if (m_vapor[i].type == PARTIFIRE)
+ {
+ for (int j = 0; j < 10; j++)
+ {
+ Math::Vector pos = m_vapor[i].pos;
+ pos.x += (Math::Rand()-0.5f)*2.0f;
+ pos.z += (Math::Rand()-0.5f)*2.0f;
+ pos.y -= 1.0f;
+ Math::Vector speed;
+ speed.x = (Math::Rand()-0.5f)*6.0f;
+ speed.z = (Math::Rand()-0.5f)*6.0f;
+ speed.y = 8.0f+Math::Rand()*5.0f;
+ Math::Point dim;
+ dim.x = Math::Rand()*1.5f+1.5f;
+ dim.y = dim.x;
+ m_particule->CreateParticle(pos, speed, dim, PARTIERROR, 2.0f, 10.0f);
+ }
+ }
+ else if (m_vapor[i].type == PARTIFLAME)
+ {
+ Math::Vector pos = m_vapor[i].pos;
+ pos.x += (Math::Rand()-0.5f)*8.0f;
+ pos.z += (Math::Rand()-0.5f)*8.0f;
+ pos.y -= 2.0f;
+ Math::Vector speed;
+ speed.x = (Math::Rand()-0.5f)*2.0f;
+ speed.z = (Math::Rand()-0.5f)*2.0f;
+ speed.y = 4.0f+Math::Rand()*4.0f;
+ Math::Point dim;
+ dim.x = Math::Rand()*2.0f+2.0f;
+ dim.y = dim.x;
+ m_particule->CreateParticle(pos, speed, dim, PARTIFLAME);
+ }
+ else
+ {
+ Math::Vector pos = m_vapor[i].pos;
+ pos.x += (Math::Rand()-0.5f)*4.0f;
+ pos.z += (Math::Rand()-0.5f)*4.0f;
+ pos.y -= 2.0f;
+ Math::Vector speed;
+ speed.x = (Math::Rand()-0.5f)*2.0f;
+ speed.z = (Math::Rand()-0.5f)*2.0f;
+ speed.y = 8.0f+Math::Rand()*8.0f;
+ Math::Point dim;
+ dim.x = Math::Rand()*1.0f+1.0f;
+ dim.y = dim.x;
+ m_particule->CreateParticle(pos, speed, dim, PARTIVAPOR);
+ }
+ }
+ }
+ else
+ {
+ m_vapor[i].used = false;
+ }
+}
+
+void Gfx::CWater::AdjustLevel(Math::Vector &pos, Math::Vector &norm,
+ Math::Point &uv1, Math::Point &uv2)
+{
+ float t1 = m_time*1.5f + pos.x*0.1f * pos.z*0.2f;
+ pos.y += sinf(t1)*m_eddy.y;
+
+ t1 = m_time*1.5f;
+ uv1.x = (pos.x+10000.0f)/40.0f+sinf(t1)*m_eddy.x*0.02f;
+ uv1.y = (pos.z+10000.0f)/40.0f-cosf(t1)*m_eddy.z*0.02f;
+ uv2.x = (pos.x+10010.0f)/20.0f+cosf(-t1)*m_eddy.x*0.02f;
+ uv2.y = (pos.z+10010.0f)/20.0f-sinf(-t1)*m_eddy.z*0.02f;
+
+ t1 = m_time*0.50f + pos.x*2.1f + pos.z*1.1f;
+ float t2 = m_time*0.75f + pos.x*2.0f + pos.z*1.0f;
+ norm = Math::Vector(sinf(t1)*m_glint, 1.0f, sinf(t2)*m_glint);
+}
+
+/** This surface prevents to see the sky (background) underwater! */
+void Gfx::CWater::DrawBack()
+{
+ /* TODO!
+ LPDIRECT3DDEVICE7 device;
+ D3DVERTEX2 vertex[4]; // 2 triangles
+ D3DMATERIAL7 material;
+ Math::Matrix matrix;
+ Math::Vector eye, lookat, n, p, p1, p2;
+ Math::Point uv1, uv2;
+ float deep, dist;
+
+ if ( !m_bDraw ) return;
+ if ( m_type[0] == WATER_NULL ) return;
+ if ( m_lineUsed == 0 ) return;
+
+ eye = m_engine->GetEyePt();
+ lookat = m_engine->GetLookatPt();
+
+ ZeroMemory( &material, sizeof(D3DMATERIAL7) );
+ material.diffuse = m_diffuse;
+ material.ambient = m_ambient;
+ m_engine->SetMaterial(material);
+
+ m_engine->SetTexture("", 0);
+
+ device = m_engine->GetD3DDevice();
+ device->SetRenderState(D3DRENDERSTATE_LIGHTING, false);
+ device->SetRenderState(D3DRENDERSTATE_ZENABLE, false);
+ device->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, false);
+ m_engine->SetState(D3DSTATENORMAL);
+
+ deep = m_engine->GetDeepView(0);
+ m_engine->SetDeepView(deep*2.0f, 0);
+ m_engine->SetFocus(m_engine->GetFocus());
+ m_engine->UpdateMatProj(); // twice the depth of view
+
+ matrix.LoadIdentity();
+ {
+ D3DMATRIX mat = MAT_TO_D3DMAT(matrix);
+ device->SetTransform(D3DTRANSFORMSTATE_WORLD, &mat);
+ }
+
+ p.x = eye.x;
+ p.z = eye.z;
+ dist = Math::DistanceProjected(eye, lookat);
+ p.x = (lookat.x-eye.x)*deep*1.0f/dist + eye.x;
+ p.z = (lookat.z-eye.z)*deep*1.0f/dist + eye.z;
+
+ p1.x = (lookat.z-eye.z)*deep*2.0f/dist + p.x;
+ p1.z = -(lookat.x-eye.x)*deep*2.0f/dist + p.z;
+ p2.x = -(lookat.z-eye.z)*deep*2.0f/dist + p.x;
+ p2.z = (lookat.x-eye.x)*deep*2.0f/dist + p.z;
+
+ p1.y = -50.0f;
+ p2.y = m_level;
+
+ n.x = (lookat.x-eye.x)/dist;
+ n.z = (lookat.z-eye.z)/dist;
+ n.y = 0.0f;
+
+ uv1.x = uv1.y = 0.0f;
+ uv2.x = uv2.y = 0.0f;
+
+ vertex[0] = D3DVERTEX2(Math::Vector(p1.x, p2.y, p1.z), n, uv1.x,uv2.y);
+ vertex[1] = D3DVERTEX2(Math::Vector(p1.x, p1.y, p1.z), n, uv1.x,uv1.y);
+ vertex[2] = D3DVERTEX2(Math::Vector(p2.x, p2.y, p2.z), n, uv2.x,uv2.y);
+ vertex[3] = D3DVERTEX2(Math::Vector(p2.x, p1.y, p2.z), n, uv2.x,uv1.y);
+
+ device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, 4, NULL);
+ m_engine->AddStatisticTriangle(2);
+
+ m_engine->SetDeepView(deep, 0);
+ m_engine->SetFocus(m_engine->GetFocus());
+ m_engine->UpdateMatProj(); // gives the initial depth of view
+
+ device->SetRenderState(D3DRENDERSTATE_LIGHTING, true);
+ device->SetRenderState(D3DRENDERSTATE_ZENABLE, true);
+ device->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, false);*/
+}
+
+void Gfx::CWater::DrawSurf()
+{
+ /* TODO!
+ LPDIRECT3DDEVICE7 device;
+ D3DVERTEX2* vertex; // triangles
+ D3DMATERIAL7 material;
+ Math::Matrix matrix;
+ Math::Vector eye, lookat, n, pos, p;
+ Math::Point uv1, uv2;
+ bool bUnder;
+ DWORD flags;
+ float deep, size, sizez, radius;
+ int rankview, i, j, u;
+
+ if (! m_draw) return;
+ if (m_type[0] == Gfx::WATER_NULL) return;
+ if (m_line.empty()) return;
+
+ vertex = (D3DVERTEX2*)malloc(sizeof(D3DVERTEX2)*(m_brick+2)*2);
+
+ eye = m_engine->GetEyePt();
+ lookat = m_engine->GetLookatPt();
+
+ rankview = m_engine->GetRankView();
+ bUnder = ( rankview == 1);
+
+ device = m_engine->GetD3DDevice();
+ device->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, false);
+
+ matrix.LoadIdentity();
+ {
+ D3DMATRIX mat = MAT_TO_D3DMAT(matrix);
+ device->SetTransform(D3DTRANSFORMSTATE_WORLD, &mat);
+ }
+
+ ZeroMemory( &material, sizeof(D3DMATERIAL7) );
+ material.diffuse = m_diffuse;
+ material.ambient = m_ambient;
+ m_engine->SetMaterial(material);
+
+ m_engine->SetTexture(m_filename, 0);
+ m_engine->SetTexture(m_filename, 1);
+
+ if ( m_type[rankview] == WATER_TT )
+ {
+ m_engine->SetState(D3DSTATETTb|D3DSTATEDUALw|D3DSTATEWRAP, m_color);
+ }
+ if ( m_type[rankview] == WATER_TO )
+ {
+ m_engine->SetState(D3DSTATENORMAL|D3DSTATEDUALw|D3DSTATEWRAP);
+ }
+ if ( m_type[rankview] == WATER_CT )
+ {
+ m_engine->SetState(D3DSTATETTb);
+ }
+ if ( m_type[rankview] == WATER_CO )
+ {
+ m_engine->SetState(D3DSTATENORMAL);
+ }
+ device->SetRenderState(D3DRENDERSTATE_FOGENABLE, true);
+
+ size = m_size/2.0f;
+ if ( bUnder ) sizez = -size;
+ else sizez = size;
+
+ // Draws all the lines.
+ deep = m_engine->GetDeepView(0)*1.5f;
+
+ for ( i=0 ; i<m_lineUsed ; i++ )
+ {
+ pos.y = m_level;
+ pos.z = m_line[i].pz;
+ pos.x = m_line[i].px1;
+
+ // Visible line?
+ p = pos;
+ p.x += size*(m_line[i].len-1);
+ radius = sqrtf(powf(size, 2.0f)+powf(size*m_line[i].len, 2.0f));
+ if ( Math::Distance(p, eye) > deep+radius ) continue;
+
+ D3DVECTOR pD3D = VEC_TO_D3DVEC(p);
+ device->ComputeSphereVisibility(&pD3D, &radius, 1, 0, &flags);
+
+ if ( flags & D3DSTATUS_CLIPINTERSECTIONALL ) continue;
+
+ u = 0;
+ p.x = pos.x-size;
+ p.z = pos.z-sizez;
+ p.y = pos.y;
+ AdjustLevel(p, n, uv1, uv2);
+ if ( bUnder ) n.y = -n.y;
+ vertex[u++] = D3DVERTEX2(p, n, uv1.x,uv1.y, uv2.x,uv2.y);
+
+ p.x = pos.x-size;
+ p.z = pos.z+sizez;
+ p.y = pos.y;
+ AdjustLevel(p, n, uv1, uv2);
+ if ( bUnder ) n.y = -n.y;
+ vertex[u++] = D3DVERTEX2(p, n, uv1.x,uv1.y, uv2.x,uv2.y);
+
+ for ( j=0 ; j<m_line[i].len ; j++ )
+ {
+ p.x = pos.x+size;
+ p.z = pos.z-sizez;
+ p.y = pos.y;
+ AdjustLevel(p, n, uv1, uv2);
+ if ( bUnder ) n.y = -n.y;
+ vertex[u++] = D3DVERTEX2(p, n, uv1.x,uv1.y, uv2.x,uv2.y);
+
+ p.x = pos.x+size;
+ p.z = pos.z+sizez;
+ p.y = pos.y;
+ AdjustLevel(p, n, uv1, uv2);
+ if ( bUnder ) n.y = -n.y;
+ vertex[u++] = D3DVERTEX2(p, n, uv1.x,uv1.y, uv2.x,uv2.y);
+
+ pos.x += size*2.0f;
+ }
+
+ device->DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX2, vertex, u, NULL);
+ m_engine->AddStatisticTriangle(u-2);
+ }
+
+ free(vertex);*/
+}
+
+bool Gfx::CWater::GetWater(int x, int y)
+{
+ x *= m_subdiv;
+ y *= m_subdiv;
+
+ float size = m_size/m_subdiv;
+ float offset = m_brick*m_size/2.0f;
+
+ for (int dy = 0; dy <= m_subdiv; dy++)
+ {
+ for (int dx = 0; dx <= m_subdiv; dx++)
+ {
+ Math::Vector pos;
+ pos.x = (x+dx)*size - offset;
+ pos.z = (y+dy)*size - offset;
+ pos.y = 0.0f;
+ float level = m_terrain->GetFloorLevel(pos, true);
+ if (level < m_level+m_eddy.y)
+ return true;
+ }
+ }
+ return false;
+}
+
+void Gfx::CWater::CreateLine(int x, int y, int len)
+{
+ Gfx::WaterLine line;
+
+ line.x = x;
+ line.y = y;
+ line.len = len;
+
+ float offset = m_brick*m_size/2.0f - m_size/2.0f;
+
+ line.px1 = m_size* line.x - offset;
+ line.px2 = m_size*(line.x+line.len) - offset;
+ line.pz = m_size* line.y - offset;
+
+ m_line.push_back(line);
+}
+
+void Gfx::CWater::Create(Gfx::WaterType type1, Gfx::WaterType type2, const std::string& fileName,
+ Gfx::Color diffuse, Gfx::Color ambient,
+ float level, float glint, Math::Vector eddy)
+{
+ m_type[0] = type1;
+ m_type[1] = type2;
+ m_diffuse = diffuse;
+ m_ambient = ambient;
+ m_level = level;
+ m_glint = glint;
+ m_eddy = eddy;
+ m_time = 0.0f;
+ m_lastLava = 0.0f;
+ m_fileName = fileName;
+
+ VaporFlush();
+
+ if (! m_fileName.empty())
+ m_engine->LoadTexture(m_fileName);
+
+ if (m_terrain == nullptr)
+ m_terrain = static_cast<CTerrain*>(m_iMan->SearchInstance(CLASS_TERRAIN));
+
+ m_brick = m_terrain->GetBrick()*m_terrain->GetMosaic();
+ m_size = m_terrain->GetSize();
+
+ m_brick /= m_subdiv;
+ m_size *= m_subdiv;
+
+ if (m_type[0] == WATER_NULL)
+ return;
+
+ m_line.clear();
+
+ for (int y = 0; y < m_brick; y++)
+ {
+ int len = 0;
+ for (int x = 0; x < m_brick; x++)
+ {
+ if (GetWater(x,y)) // water here?
+ {
+ len ++;
+ if (len >= 5)
+ {
+ CreateLine(x-len+1, y, len);
+ len = 0;
+ }
+ }
+ else // dry?
+ {
+ if (len != 0)
+ {
+ CreateLine(x-len, y, len);
+ len = 0;
+ }
+ }
+ }
+ if (len != 0)
+ CreateLine(m_brick - len, y, len);
+ }
+}
+
+void Gfx::CWater::Flush()
+{
+ m_type[0] = Gfx::WATER_NULL;
+ m_type[1] = Gfx::WATER_NULL;
+ m_level = 0.0f;
+ m_lava = false;
+}
+
+void Gfx::CWater::SetLevel(float level)
+{
+ m_level = level;
+
+ Create(m_type[0], m_type[1], m_fileName, m_diffuse, m_ambient,
+ m_level, m_glint, m_eddy);
+}
+
+float Gfx::CWater::GetLevel()
+{
+ return m_level;
+}
+
+float Gfx::CWater::GetLevel(CObject* object)
+{
+ /* TODO!
+ ObjectType type = object->GetType();
+
+ if ( type == OBJECT_HUMAN ||
+ type == OBJECT_TECH )
+ {
+ return m_level-3.0f;
+ }
+
+ if ( type == OBJECT_MOBILEfa ||
+ type == OBJECT_MOBILEta ||
+ type == OBJECT_MOBILEwa ||
+ type == OBJECT_MOBILEia ||
+ type == OBJECT_MOBILEfc ||
+ type == OBJECT_MOBILEtc ||
+ type == OBJECT_MOBILEwc ||
+ type == OBJECT_MOBILEic ||
+ type == OBJECT_MOBILEfi ||
+ type == OBJECT_MOBILEti ||
+ type == OBJECT_MOBILEwi ||
+ type == OBJECT_MOBILEii ||
+ type == OBJECT_MOBILEfs ||
+ type == OBJECT_MOBILEts ||
+ type == OBJECT_MOBILEws ||
+ type == OBJECT_MOBILEis ||
+ type == OBJECT_MOBILErt ||
+ type == OBJECT_MOBILErc ||
+ type == OBJECT_MOBILErr ||
+ type == OBJECT_MOBILErs ||
+ type == OBJECT_MOBILEsa ||
+ type == OBJECT_MOBILEtg ||
+ type == OBJECT_MOBILEft ||
+ type == OBJECT_MOBILEtt ||
+ type == OBJECT_MOBILEwt ||
+ type == OBJECT_MOBILEit ||
+ type == OBJECT_MOBILEdr )
+ {
+ return m_level-2.0f;
+ }
+*/
+ return m_level;
+}
+
+void Gfx::CWater::SetLava(bool lava)
+{
+ m_lava = lava;
+}
+
+bool Gfx::CWater::GetLava()
+{
+ return m_lava;
+}
+
+void Gfx::CWater::AdjustEye(Math::Vector &eye)
+{
+ if (m_lava)
+ {
+ if (eye.y < m_level+2.0f)
+ eye.y = m_level+2.0f; // never under the lava
+ }
+ else
+ {
+ if (eye.y >= m_level-2.0f &&
+ eye.y <= m_level+2.0f) // close to the surface?
+ eye.y = m_level+2.0f; // bam, well above
+ }
+}
-// TODO implementation
diff --git a/src/graphics/engine/water.h b/src/graphics/engine/water.h
index 67be9dc..b051889 100644
--- a/src/graphics/engine/water.h
+++ b/src/graphics/engine/water.h
@@ -15,54 +15,71 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// water.h
+/**
+ * \file graphics/engine/water.h
+ * \brief Water rendering - Gfx::CWater class
+ */
#pragma once
-#include "graphics/engine/engine.h"
-#include "graphics/engine/particle.h"
#include "common/event.h"
+#include "graphics/engine/particle.h"
class CInstanceManager;
-class CSound;
+class CSoundInterface;
namespace Gfx {
+class CEngine;
class CTerrain;
-
-const short MAXWATERLINE = 500;
-
struct WaterLine
{
- short x, y; // beginning
- short len; // length by x
+ //! Beginning
+ short x, y;
+ //! Length by x
+ short len;
float px1, px2, pz;
-};
-
-const short MAXWATVAPOR = 10;
+ WaterLine()
+ {
+ x = y = 0;
+ len = 0;
+ px1 = px2 = pz = 0.0f;
+ }
+};
struct WaterVapor
{
- bool bUsed;
- ParticleType type;
- Math::Vector pos;
- float delay;
- float time;
- float last;
+ bool used;
+ Gfx::ParticleType type;
+ Math::Vector pos;
+ float delay;
+ float time;
+ float last;
+
+ WaterVapor()
+ {
+ used = false;
+ type = Gfx::PARTIWATER;
+ delay = time = last = 0.0f;
+ }
};
-
enum WaterType
{
- WATER_NULL = 0, // no water
- WATER_TT = 1, // transparent texture
- WATER_TO = 2, // opaque texture
- WATER_CT = 3, // transparent color
- WATER_CO = 4, // opaque color
+ //! No water
+ WATER_NULL = 0,
+ //! Transparent texture
+ WATER_TT = 1,
+ //! Opaque texture
+ WATER_TO = 2,
+ //! Transparent color
+ WATER_CT = 3,
+ //! Opaque color
+ WATER_CO = 4,
};
@@ -72,63 +89,88 @@ public:
CWater(CInstanceManager* iMan, Gfx::CEngine* engine);
~CWater();
- void SetGLDevice(Gfx::CDevice device);
+ void SetDevice(Gfx::CDevice* device);
bool EventProcess(const Event &event);
+ //! Removes all the water
void Flush();
- bool Create(WaterType type1, WaterType type2, const char *filename, Gfx::Color diffuse, Gfx::Color ambient, float level, float glint, Math::Vector eddy);
+ //! Creates all expanses of water
+ void Create(WaterType type1, WaterType type2, const std::string& fileName,
+ Gfx::Color diffuse, Gfx::Color ambient, float level, float glint, Math::Vector eddy);
+ //! Draw the back surface of the water
void DrawBack();
+ //! Draws the flat surface of the water
void DrawSurf();
- bool SetLevel(float level);
- float RetLevel();
- float RetLevel(CObject* object);
+ //! Changes the level of the water
+ void SetLevel(float level);
+ //! Returns the current level of water
+ float GetLevel();
+ //! Returns the current level of water for a given object
+ float GetLevel(CObject* object);
- void SetLava(bool bLava);
- bool RetLava();
+ //@{
+ //! Management of the mode of lava/water
+ void SetLava(bool lava);
+ bool GetLava();
+ //@}
+ //! Adjusts the eye of the camera, not to be in the water
void AdjustEye(Math::Vector &eye);
protected:
+ //! Makes water evolve
bool EventFrame(const Event &event);
+ //! Makes evolve the steam jets on the lava
void LavaFrame(float rTime);
+ //! Adjusts the position to normal, to imitate reflections on an expanse of water at rest
void AdjustLevel(Math::Vector &pos, Math::Vector &norm, Math::Point &uv1, Math::Point &uv2);
- bool RetWater(int x, int y);
- bool CreateLine(int x, int y, int len);
+ //! Indicates if there is water in a given position
+ bool GetWater(int x, int y);
+ //! Updates the positions, relative to the ground
+ void CreateLine(int x, int y, int len);
+ //! Removes all the steam jets
void VaporFlush();
+ //! Creates a new steam
bool VaporCreate(ParticleType type, Math::Vector pos, float delay);
+ //! Makes evolve a steam jet
void VaporFrame(int i, float rTime);
protected:
- CInstanceManager* m_iMan;
- CEngine* m_engine;
- CDevice* m_pDevice;
- CTerrain* m_terrain;
- CParticle* m_particule;
- CSound* m_sound;
+ CInstanceManager* m_iMan;
+ Gfx::CEngine* m_engine;
+ Gfx::CDevice* m_device;
+ Gfx::CTerrain* m_terrain;
+ Gfx::CParticle* m_particule;
+ CSoundInterface* m_sound;
WaterType m_type[2];
- char m_filename[100];
- float m_level; // overall level
- float m_glint; // amplitude of reflections
- Math::Vector m_eddy; // amplitude of swirls
- Gfx::Color m_diffuse; // diffuse color
- Gfx::Color m_ambient; // ambient color
+ std::string m_fileName;
+ //! Overall level
+ float m_level;
+ //! Amplitude of reflections
+ float m_glint;
+ //! Amplitude of swirls
+ Math::Vector m_eddy;
+ //! Diffuse color
+ Gfx::Color m_diffuse;
+ //! Ambient color
+ Gfx::Color m_ambient;
float m_time;
float m_lastLava;
- int m_subdiv;
-
- int m_brick; // number of brick*mosaics
- float m_size; // size of a item in an brick
+ int m_subdiv;
- int m_lineUsed;
- WaterLine m_line[MAXWATERLINE];
+ //! Number of brick*mosaics
+ int m_brick;
+ //! Size of a item in an brick
+ float m_size;
- WaterVapor m_vapor[MAXWATVAPOR];
+ std::vector<WaterLine> m_line;
+ std::vector<WaterVapor> m_vapor;
- bool m_bDraw;
- bool m_bLava;
- long m_color;
+ bool m_draw;
+ bool m_lava;
+ long m_color;
};
}; // namespace Gfx
diff --git a/src/graphics/opengl/README.txt b/src/graphics/opengl/README.txt
index 0aba0ed..596871d 100644
--- a/src/graphics/opengl/README.txt
+++ b/src/graphics/opengl/README.txt
@@ -1,6 +1,7 @@
-src/graphics/opengl
-
-OpenGL engine implementation
-
-Contains the concrete implementation using OpenGL of abstract CDevice class
-from src/graphics/core
+/**
+ * \dir graphics/opengl
+ * \brief OpenGL engine implementation
+ *
+ * Contains the concrete implementation using OpenGL of abstract CDevice class
+ * from src/graphics/core
+ */ \ No newline at end of file
diff --git a/src/graphics/opengl/gldevice.cpp b/src/graphics/opengl/gldevice.cpp
index 3a255f4..caa79ee 100644
--- a/src/graphics/opengl/gldevice.cpp
+++ b/src/graphics/opengl/gldevice.cpp
@@ -64,7 +64,6 @@ void Gfx::GLDeviceConfig::LoadDefault()
Gfx::CGLDevice::CGLDevice(const Gfx::GLDeviceConfig &config)
{
m_config = config;
- m_wasInit = false;
m_lighting = false;
m_texturing = false;
}
@@ -74,9 +73,11 @@ Gfx::CGLDevice::~CGLDevice()
{
}
-bool Gfx::CGLDevice::GetWasInit()
+void Gfx::CGLDevice::DebugHook()
{
- return m_wasInit;
+ /* This function is only called here, so it can be used
+ * as a breakpoint when debugging using gDEBugger */
+ glColor3i(0, 0, 0);
}
std::string Gfx::CGLDevice::GetError()
@@ -110,8 +111,6 @@ bool Gfx::CGLDevice::Create()
/* NOTE: when not using GLEW, extension testing is not performed, as it is assumed that
glext.h is up-to-date and the OpenGL shared library has the required functions present. */
- m_wasInit = true;
-
// This is mostly done in all modern hardware by default
// DirectX doesn't even allow the option to turn off perspective correction anymore
// So turn it on permanently
@@ -130,7 +129,7 @@ bool Gfx::CGLDevice::Create()
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
- glViewport(0, 0, m_config.size.w, m_config.size.h);
+ glViewport(0, 0, m_config.size.x, m_config.size.y);
m_lights = std::vector<Gfx::Light>(GL_MAX_LIGHTS, Gfx::Light());
@@ -158,8 +157,6 @@ void Gfx::CGLDevice::Destroy()
m_currentTextures.clear();
m_texturesEnabled.clear();
m_textureStageParams.clear();
-
- m_wasInit = false;
}
void Gfx::CGLDevice::ConfigChanged(const Gfx::GLDeviceConfig& newConfig)
@@ -385,18 +382,23 @@ bool Gfx::CGLDevice::GetLightEnabled(int index)
This struct must not be deleted in other way than through DeleteTexture() */
Gfx::Texture Gfx::CGLDevice::CreateTexture(CImage *image, const Gfx::TextureCreateParams &params)
{
- Gfx::Texture result;
-
ImageData *data = image->GetData();
if (data == NULL)
{
m_error = "Invalid texture data";
- return result; // invalid texture
+ return Gfx::Texture(); // invalid texture
}
+ return CreateTexture(data, params);
+}
+
+Gfx::Texture Gfx::CGLDevice::CreateTexture(ImageData *data, const Gfx::TextureCreateParams &params)
+{
+ Gfx::Texture result;
+
result.valid = true;
- result.size.w = data->surface->w;
- result.size.h = data->surface->h;
+ result.size.x = data->surface->w;
+ result.size.y = data->surface->h;
// Use & enable 1st texture stage
glActiveTexture(GL_TEXTURE0);
@@ -531,6 +533,24 @@ void Gfx::CGLDevice::SetTexture(int index, const Gfx::Texture &texture)
glDisable(GL_TEXTURE_2D);
}
+void Gfx::CGLDevice::SetTexture(int index, unsigned int textureId)
+{
+ assert(index >= 0);
+ assert(index < static_cast<int>( m_currentTextures.size() ));
+
+ // Enable the given texture stage
+ glActiveTexture(GL_TEXTURE0 + index);
+ glEnable(GL_TEXTURE_2D);
+
+ m_currentTextures[index].id = textureId;
+
+ glBindTexture(GL_TEXTURE_2D, textureId);
+
+ // Disable the stage if it is set so
+ if ( (! m_texturing) || (! m_texturesEnabled[index]) )
+ glDisable(GL_TEXTURE_2D);
+}
+
/**
Returns the previously assigned texture or invalid texture if the given stage is not enabled. */
Gfx::Texture Gfx::CGLDevice::GetTexture(int index)
@@ -1159,17 +1179,19 @@ void Gfx::CGLDevice::GetFogParams(Gfx::FogMode &mode, Gfx::Color &color, float &
void Gfx::CGLDevice::SetCullMode(Gfx::CullMode mode)
{
- if (mode == Gfx::CULL_CW) glCullFace(GL_CW);
- else if (mode == Gfx::CULL_CCW) glCullFace(GL_CCW);
+ // Cull clockwise back faces, so front face is the opposite
+ // (assuming GL_CULL_FACE is GL_BACK)
+ if (mode == Gfx::CULL_CW ) glFrontFace(GL_CCW);
+ else if (mode == Gfx::CULL_CCW) glFrontFace(GL_CW);
else assert(false);
}
Gfx::CullMode Gfx::CGLDevice::GetCullMode()
{
GLint flag = 0;
- glGetIntegerv(GL_CULL_FACE, &flag);
- if (flag == GL_CW) return Gfx::CULL_CW;
- else if (flag == GL_CCW) return Gfx::CULL_CCW;
+ glGetIntegerv(GL_FRONT_FACE, &flag);
+ if (flag == GL_CW) return Gfx::CULL_CCW;
+ else if (flag == GL_CCW) return Gfx::CULL_CW;
else assert(false);
return Gfx::CULL_CW;
}
diff --git a/src/graphics/opengl/gldevice.h b/src/graphics/opengl/gldevice.h
index 1864000..3daea8a 100644
--- a/src/graphics/opengl/gldevice.h
+++ b/src/graphics/opengl/gldevice.h
@@ -14,7 +14,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// gldevice.h
+/**
+ * \file graphics/opengl/gldevice.h
+ * \brief OpenGL implementation - Gfx::CGLDevice class
+ */
#pragma once
@@ -73,7 +76,8 @@ public:
CGLDevice(const Gfx::GLDeviceConfig &config);
virtual ~CGLDevice();
- virtual bool GetWasInit();
+ virtual void DebugHook();
+
virtual std::string GetError();
virtual bool Create();
@@ -100,11 +104,13 @@ public:
virtual bool GetLightEnabled(int index);
virtual Gfx::Texture CreateTexture(CImage *image, const Gfx::TextureCreateParams &params);
+ virtual Gfx::Texture CreateTexture(ImageData *data, const Gfx::TextureCreateParams &params);
virtual void DestroyTexture(const Gfx::Texture &texture);
virtual void DestroyAllTextures();
virtual int GetMaxTextureCount();
virtual void SetTexture(int index, const Gfx::Texture &texture);
+ virtual void SetTexture(int index, unsigned int textureId);
virtual Gfx::Texture GetTexture(int index);
virtual void SetTextureEnabled(int index, bool enabled);
virtual bool GetTextureEnabled(int index);
@@ -163,8 +169,6 @@ private:
private:
//! Current config
Gfx::GLDeviceConfig m_config;
- //! Was initialized?
- bool m_wasInit;
//! Last encountered error
std::string m_error;
diff --git a/src/math/README.txt b/src/math/README.txt
index 1a5ce93..fd34dcb 100644
--- a/src/math/README.txt
+++ b/src/math/README.txt
@@ -1,3 +1,12 @@
-src/math
+/**
+ * \dir math
+ * \brief Common mathematical structures and functions
+ */
-Contains common mathematical structures and functions.
+/**
+ * \namespace Math
+ * \brief Namespace for (new) math code
+ *
+ * This namespace was created to avoid clashing with old code, but now it still serves,
+ * defining a border between math and non-math-related code.
+ */ \ No newline at end of file
diff --git a/src/math/all.h b/src/math/all.h
index 13a9290..4ac9d55 100644
--- a/src/math/all.h
+++ b/src/math/all.h
@@ -14,14 +14,13 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-/** @defgroup MathAllModule math/all.h
- Includes all other math module headers.
+/**
+ * \file math/all.h
+ * \brief Includes all other math module headers
*/
#pragma once
-/* @{ */ // start of group
-
#include "const.h"
#include "func.h"
#include "point.h"
@@ -30,5 +29,3 @@
#include "geometry.h"
#include "conv.h"
-
-/* @} */ // end of group
diff --git a/src/math/const.h b/src/math/const.h
index dd7ab0f..0b6f971 100644
--- a/src/math/const.h
+++ b/src/math/const.h
@@ -14,28 +14,30 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-/** @defgroup MathConstModule math/const.h
- Contains the math constants used in math functions.
+/**
+ * \file math/const.h
+ * \brief Constants used in math functions
*/
#pragma once
+#include <cmath>
+
// Math module namespace
namespace Math
{
-/* @{ */ // start of group
//! Tolerance level -- minimum accepted float value
const float TOLERANCE = 1e-6f;
//! Very small number (used in testing/returning some values)
-const float VERY_SMALL = 1e-6f;
+const float VERY_SMALL_NUM = 1e-6f;
//! Very big number (used in testing/returning some values)
-const float VERY_BIG = 1e6f;
+const float VERY_BIG_NUM = 1e6f;
//! Huge number
-const float HUGE = 1.0e+38f;
+const float HUGE_NUM = 1.0e+38f;
//! PI
const float PI = 3.14159265358979323846f;
@@ -45,6 +47,8 @@ const float DEG_TO_RAD = 0.01745329251994329547f;
//! Radians to degrees multiplier
const float RAD_TO_DEG = 57.29577951308232286465f;
-/* @} */ // end of group
+//! Natural logarithm of 2
+const float LOG_2 = log(2.0f);
+
}; // namespace Math
diff --git a/src/math/func.h b/src/math/func.h
index 2127d1a..541b084 100644
--- a/src/math/func.h
+++ b/src/math/func.h
@@ -15,8 +15,9 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-/** @defgroup MathFuncModule math/func.h
- Contains common math functions.
+/**
+ * \file math/func.h
+ * \brief Common math functions
*/
#pragma once
@@ -31,8 +32,6 @@
namespace Math
{
-/* @{ */ // start of group
-
//! Compares \a a and \a b within \a tolerance
inline bool IsEqual(float a, float b, float tolerance = Math::TOLERANCE)
{
@@ -127,6 +126,14 @@ inline float Rand()
return static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
}
+//! Returns the next nearest power of two to \a x
+inline int NextPowerOfTwo(int x)
+{
+ double logbase2 = log(static_cast<float>(x)) / Math::LOG_2;
+ return static_cast<int>(pow(2, ceil(logbase2)) + 0.5);
+}
+
+
//! Returns a normalized angle, that is in other words between 0 and 2 * PI
inline float NormAngle(float angle)
{
@@ -180,11 +187,13 @@ inline float Direction(float a, float g)
//! Managing the dead zone of a joystick.
/**
-\verbatimin: -1 0 1
+\verbatim
+in: -1 0 1
--|-------|----o----|-------|-->
<---->
dead
-out: -1 0 0 1\endverbatim */
+out: -1 0 0 1
+\endverbatim */
inline float Neutral(float value, float dead)
{
if ( fabs(value) <= dead )
@@ -218,7 +227,8 @@ inline float Smooth(float actual, float hope, float time)
//! Bounces any movement
/**
-\verbatimout
+\verbatim
+out
|
1+------o-------o---
| o | o o | | bounce
@@ -227,7 +237,8 @@ inline float Smooth(float actual, float hope, float time)
| o | |
-o------|-------+----> progress
0| | 1
- |<---->|middle\endverbatim */
+ |<---->|middle
+\endverbatim */
inline float Bounce(float progress, float middle = 0.3f, float bounce = 0.4f)
{
if ( progress < middle )
@@ -242,6 +253,4 @@ inline float Bounce(float progress, float middle = 0.3f, float bounce = 0.4f)
}
}
-/* @} */ // end of group
-
}; // namespace Math
diff --git a/src/math/geometry.h b/src/math/geometry.h
index 61d1868..1c5f60f 100644
--- a/src/math/geometry.h
+++ b/src/math/geometry.h
@@ -15,9 +15,9 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-/** @defgroup MathGeometryModule math/geometry.h
- Contains math functions related to 3D geometry calculations,
- transformations, etc.
+/**
+ * \file math/geometry.h
+ * \brief Math functions related to 3D geometry calculations, transformations, etc.
*/
#pragma once
@@ -36,18 +36,15 @@
namespace Math
{
-/* @{ */ // start of group
-
-
//! Returns py up on the line \a a - \a b
inline float MidPoint(const Math::Point &a, const Math::Point &b, float px)
{
if (IsEqual(a.x, b.x))
{
if (a.y < b.y)
- return HUGE;
+ return Math::HUGE_NUM;
else
- return -HUGE;
+ return -Math::HUGE_NUM;
}
return (b.y-a.y) * (px-a.x) / (b.x-a.x) + a.y;
}
@@ -566,6 +563,4 @@ inline Math::Vector RotateView(Math::Vector center, float angleH, float angleV,
return eye+center;
}
-/* @} */ // end of group
-
}; // namespace Math
diff --git a/src/math/intpoint.h b/src/math/intpoint.h
index a760bc2..8e13b19 100644
--- a/src/math/intpoint.h
+++ b/src/math/intpoint.h
@@ -14,31 +14,29 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-/** @defgroup MathIntPointModule math/intpoint.h
- Contains the IntPoint struct.
+/**
+ * \file math/intpoint.h
+ * \brief IntPoint struct
*/
#pragma once
namespace Math {
-/* @{ */ // start of group
-
/**
- * \struct IntPoint 2D Point with integer coords
+ * \struct IntPoint
+ * \brief 2D Point with integer coords
*
* Analog of WinAPI's POINT struct.
*/
struct IntPoint
{
//! X coord
- long x;
+ int x;
//! Y coord
- long y;
+ int y;
- IntPoint(long aX = 0, long aY = 0) : x(aX), y(aY) {}
+ IntPoint(int aX = 0, int aY = 0) : x(aX), y(aY) {}
};
-/* @} */ // end of group
-
}; // namespace Math
diff --git a/src/math/intsize.h b/src/math/intsize.h
deleted file mode 100644
index f4b2431..0000000
--- a/src/math/intsize.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// * This file is part of the COLOBOT source code
-// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
-// *
-// * This program is free software: you can redistribute it and/or modify
-// * it under the terms of the GNU General Public License as published by
-// * the Free Software Foundation, either version 3 of the License, or
-// * (at your option) any later version.
-// *
-// * This program is distributed in the hope that it will be useful,
-// * but WITHOUT ANY WARRANTY; without even the implied warranty of
-// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// * GNU General Public License for more details.
-// *
-// * You should have received a copy of the GNU General Public License
-// * along with this program. If not, see http://www.gnu.org/licenses/.
-
-/** @defgroup MathIntSizeModule math/intsize.h
- Contains the IntSize struct.
- */
-
-#pragma once
-
-// Math module namespace
-namespace Math
-{
-
-/* @{ */ // start of group
-
-/** \struct IntSize math/size.h
- \brief 2D size with integer dimensions */
-struct IntSize
-{
- //! Width
- int w;
- //! Height
- int h;
-
- //! Constructs a zero size: (0,0)
- inline IntSize()
- {
- LoadZero();
- }
-
- //! Constructs a size from given dimensions: (w,h)
- inline explicit IntSize(int w, int h)
- {
- this->w = w;
- this->h = h;
- }
-
- //! Sets the zero size: (0,0)
- inline void LoadZero()
- {
- w = h = 0;
- }
-}; // struct Size
-
-
-/* @} */ // end of group
-
-}; // namespace Math
diff --git a/src/math/matrix.h b/src/math/matrix.h
index 45a7d75..30e629a 100644
--- a/src/math/matrix.h
+++ b/src/math/matrix.h
@@ -14,8 +14,9 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-/** @defgroup MathMatrixModule math/matrix.h
- Contains the Matrix struct and related functions.
+/**
+ * \file math/matrix.h
+ * \brief Matrix struct and related functions
*/
#pragma once
@@ -32,8 +33,6 @@
namespace Math
{
-/* @{ */ // start of group
-
/** \struct Matrix math/matrix.h
\brief 4x4 matrix
@@ -42,11 +41,12 @@ namespace Math
The internal representation is a 16-value table in column-major order, thus:
- \verbatim
+\verbatim
m[0 ] m[4 ] m[8 ] m[12]
m[1 ] m[5 ] m[9 ] m[13]
m[2 ] m[6 ] m[10] m[14]
-m[3 ] m[7 ] m[11] m[15] \endverbatim
+m[3 ] m[7 ] m[11] m[15]
+\endverbatim
This representation is native to OpenGL; DirectX requires transposing the matrix.
@@ -405,11 +405,15 @@ inline Math::Matrix MultiplyMatrices(const Math::Matrix &left, const Math::Matri
}
//! Calculates the result of multiplying m * v
-/** The multiplication is performed thus:
-\verbatim [ m.m[0 ] m.m[4 ] m.m[8 ] m.m[12] ] [ v.x ]
+/**
+ The multiplication is performed thus:
+
+\verbatim
+[ m.m[0 ] m.m[4 ] m.m[8 ] m.m[12] ] [ v.x ]
[ m.m[1 ] m.m[5 ] m.m[9 ] m.m[13] ] [ v.y ]
[ m.m[2 ] m.m[6 ] m.m[10] m.m[14] ] * [ v.z ]
-[ m.m[3 ] m.m[7 ] m.m[11] m.m[15] ] [ 1 ] \endverbatim
+[ m.m[3 ] m.m[7 ] m.m[11] m.m[15] ] [ 1 ]
+\endverbatim
The result, a 4x1 vector is then converted to 3x1 by dividing
x,y,z coords by the fourth coord (w). */
@@ -434,6 +438,4 @@ inline Math::Vector MatrixVectorMultiply(const Math::Matrix &m, const Math::Vect
return Math::Vector(x, y, z);
}
-/* @} */ // end of group
-
}; // namespace Math
diff --git a/src/math/point.h b/src/math/point.h
index ea20db9..ecf896f 100644
--- a/src/math/point.h
+++ b/src/math/point.h
@@ -14,8 +14,9 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-/** @defgroup MathPointModule math/point.h
- Contains the Point struct and related functions.
+/**
+ * \file math/point.h
+ * \brief Point struct and related functions
*/
#pragma once
@@ -31,8 +32,6 @@
namespace Math
{
-/* @{ */ // start of group
-
/** \struct Point math/point.h
\brief 2D point
@@ -188,6 +187,4 @@ inline float Distance(const Point &a, const Point &b)
return sqrtf((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
-/* @} */ // end of group
-
}; // namespace Math
diff --git a/src/math/size.h b/src/math/size.h
deleted file mode 100644
index 781b9a4..0000000
--- a/src/math/size.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// * This file is part of the COLOBOT source code
-// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
-// *
-// * This program is free software: you can redistribute it and/or modify
-// * it under the terms of the GNU General Public License as published by
-// * the Free Software Foundation, either version 3 of the License, or
-// * (at your option) any later version.
-// *
-// * This program is distributed in the hope that it will be useful,
-// * but WITHOUT ANY WARRANTY; without even the implied warranty of
-// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// * GNU General Public License for more details.
-// *
-// * You should have received a copy of the GNU General Public License
-// * along with this program. If not, see http://www.gnu.org/licenses/.
-
-/** @defgroup MathSizeModule math/size.h
- Contains the Size struct.
- */
-
-#pragma once
-
-// Math module namespace
-namespace Math
-{
-
-/* @{ */ // start of group
-
-/** \struct Size math/size.h
- \brief 2D size
-
- Represents a 2D size (w, h).
- Is separate from Math::Point to avoid confusion.
-
- */
-struct Size
-{
- //! Width
- float w;
- //! Height
- float h;
-
- //! Constructs a zero size: (0,0)
- inline Size()
- {
- LoadZero();
- }
-
- //! Constructs a size from given dimensions: (w,h)
- inline explicit Size(float w, float h)
- {
- this->w = w;
- this->h = h;
- }
-
- //! Sets the zero size: (0,0)
- inline void LoadZero()
- {
- w = h = 0.0f;
- }
-}; // struct Size
-
-
-/* @} */ // end of group
-
-}; // namespace Math
diff --git a/src/math/vector.h b/src/math/vector.h
index 147869f..4378e75 100644
--- a/src/math/vector.h
+++ b/src/math/vector.h
@@ -14,8 +14,9 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-/** @defgroup MathVectorModule math/vector.h
- Contains the Vector struct and related functions.
+/**
+ * \file math/vector.h
+ * \brief Vector struct and related functions
*/
#pragma once
@@ -31,8 +32,6 @@
namespace Math
{
-/* @{ */ // start of group
-
/** \struct Vector math/vector.h
\brief 3D (3x1) vector
@@ -263,6 +262,4 @@ inline float Distance(const Math::Vector &a, const Math::Vector &b)
(a.z-b.z)*(a.z-b.z) );
}
-/* @} */ // end of group
-
}; // namespace Math
diff --git a/src/object/README.txt b/src/object/README.txt
index f4c25d0..f3bad23 100644
--- a/src/object/README.txt
+++ b/src/object/README.txt
@@ -1,3 +1,7 @@
-src/object
-
-Contains modules of robots and buildings.
+/**
+ * \dir object
+ * \brief Game engine
+ *
+ * Contains the main class of game engine - CRobotMain and the various in-game objects:
+ * CObject and related auto, motion and task subclasses.
+ */ \ No newline at end of file
diff --git a/src/object/object.h b/src/object/object.h
index 75283d6..4a4dcb0 100644
--- a/src/object/object.h
+++ b/src/object/object.h
@@ -19,16 +19,12 @@
#pragma once
-#include "old/d3dengine.h"
-#include "old/camera.h"
-#include "old/sound.h"
+#include "graphics/engine/engine.h"
+#include "graphics/engine/camera.h"
+#include "sound/sound.h"
class CInstanceManager;
-class CLight;
-class CTerrain;
-class CWater;
-class CParticule;
class CPhysics;
class CBrain;
class CMotion;
@@ -40,6 +36,7 @@ class CScript;
+
// The father of all parts must always be the part number zero!
const int OBJECTMAXPART = 40;
@@ -306,7 +303,7 @@ enum ObjectMaterial
struct ObjectPart
{
char bUsed;
- int object; // number of the object in CD3DEngine
+ int object; // number of the object in CEngine
int parentPart; // number of father part
int masterParti; // master canal of the particle
Math::Vector position;
@@ -377,16 +374,16 @@ public:
int CreatePart();
void DeletePart(int part);
void SetObjectRank(int part, int objRank);
- int RetObjectRank(int part);
+ int GetObjectRank(int part);
void SetObjectParent(int part, int parent);
void SetType(ObjectType type);
- ObjectType RetType();
- char* RetName();
+ ObjectType GetType();
+ char* GetName();
void SetOption(int option);
- int RetOption();
+ int GetOption();
void SetID(int id);
- int RetID();
+ int GetID();
bool Write(char *line);
bool Read(char *line);
@@ -413,203 +410,205 @@ public:
bool WriteProgram(int rank, char* filename);
bool RunProgram(int rank);
- int RetShadowLight();
- int RetEffectLight();
+ int GetShadowLight();
+ int GetEffectLight();
void FlushCrashShere();
int CreateCrashSphere(Math::Vector pos, float radius, Sound sound, float hardness=0.45f);
- int RetCrashSphereTotal();
+ int GetCrashSphereTotal();
bool GetCrashSphere(int rank, Math::Vector &pos, float &radius);
- float RetCrashSphereHardness(int rank);
- Sound RetCrashSphereSound(int rank);
+ float GetCrashSphereHardness(int rank);
+ Sound GetCrashSphereSound(int rank);
void DeleteCrashSphere(int rank);
void SetGlobalSphere(Math::Vector pos, float radius);
void GetGlobalSphere(Math::Vector &pos, float &radius);
void SetJotlerSphere(Math::Vector pos, float radius);
void GetJotlerSphere(Math::Vector &pos, float &radius);
void SetShieldRadius(float radius);
- float RetShieldRadius();
+ float GetShieldRadius();
void SetFloorHeight(float height);
void FloorAdjust();
void SetLinVibration(Math::Vector dir);
- Math::Vector RetLinVibration();
+ Math::Vector GetLinVibration();
void SetCirVibration(Math::Vector dir);
- Math::Vector RetCirVibration();
+ Math::Vector GetCirVibration();
void SetInclinaison(Math::Vector dir);
- Math::Vector RetInclinaison();
+ Math::Vector GetInclinaison();
void SetPosition(int part, const Math::Vector &pos);
- Math::Vector RetPosition(int part);
+ Math::Vector GetPosition(int part);
void SetAngle(int part, const Math::Vector &angle);
- Math::Vector RetAngle(int part);
+ Math::Vector GetAngle(int part);
void SetAngleY(int part, float angle);
void SetAngleX(int part, float angle);
void SetAngleZ(int part, float angle);
- float RetAngleY(int part);
- float RetAngleX(int part);
- float RetAngleZ(int part);
+ float GetAngleY(int part);
+ float GetAngleX(int part);
+ float GetAngleZ(int part);
void SetZoom(int part, float zoom);
void SetZoom(int part, Math::Vector zoom);
- Math::Vector RetZoom(int part);
+ Math::Vector GetZoom(int part);
void SetZoomX(int part, float zoom);
- float RetZoomX(int part);
+ float GetZoomX(int part);
void SetZoomY(int part, float zoom);
- float RetZoomY(int part);
+ float GetZoomY(int part);
void SetZoomZ(int part, float zoom);
- float RetZoomZ(int part);
+ float GetZoomZ(int part);
- float RetWaterLevel();
+ float GetWaterLevel();
void SetTrainer(bool bEnable);
- bool RetTrainer();
+ bool GetTrainer();
void SetToy(bool bEnable);
- bool RetToy();
+ bool GetToy();
void SetManual(bool bManual);
- bool RetManual();
+ bool GetManual();
void SetResetCap(ResetCap cap);
- ResetCap RetResetCap();
+ ResetCap GetResetCap();
void SetResetBusy(bool bBusy);
- bool RetResetBusy();
+ bool GetResetBusy();
void SetResetPosition(const Math::Vector &pos);
- Math::Vector RetResetPosition();
+ Math::Vector GetResetPosition();
void SetResetAngle(const Math::Vector &angle);
- Math::Vector RetResetAngle();
+ Math::Vector GetResetAngle();
void SetResetRun(int run);
- int RetResetRun();
+ int GetResetRun();
void SetMasterParticule(int part, int parti);
- int RetMasterParticule(int part);
+ int GetMasterParticule(int part);
void SetPower(CObject* power);
- CObject* RetPower();
+ CObject* GetPower();
void SetFret(CObject* fret);
- CObject* RetFret();
+ CObject* GetFret();
void SetTruck(CObject* truck);
- CObject* RetTruck();
+ CObject* GetTruck();
void SetTruckPart(int part);
- int RetTruckPart();
+ int GetTruckPart();
void InfoFlush();
void DeleteInfo(int rank);
void SetInfo(int rank, Info info);
- Info RetInfo(int rank);
- int RetInfoTotal();
- void SetInfoReturn(float value);
- float RetInfoReturn();
+ Info GetInfo(int rank);
+ int GetInfoTotal();
+ void SetInfoGeturn(float value);
+ float GetInfoGeturn();
void SetInfoUpdate(bool bUpdate);
- bool RetInfoUpdate();
+ bool GetInfoUpdate();
bool SetCmdLine(int rank, float value);
- float RetCmdLine(int rank);
+ float GetCmdLine(int rank);
- Math::Matrix* RetRotateMatrix(int part);
- Math::Matrix* RetTranslateMatrix(int part);
- Math::Matrix* RetTransformMatrix(int part);
- Math::Matrix* RetWorldMatrix(int part);
+ Math::Matrix* GetRotateMatrix(int part);
+ Math::Matrix* GetTranslateMatrix(int part);
+ Math::Matrix* GetTransformMatrix(int part);
+ Math::Matrix* GetWorldMatrix(int part);
- void SetViewFromHere(Math::Vector &eye, float &dirH, float &dirV, Math::Vector &lookat, Math::Vector &upVec, CameraType type);
+ void SetViewFromHere(Math::Vector &eye, float &dirH, float &dirV,
+ Math::Vector &lookat, Math::Vector &upVec,
+ Gfx::CameraType type);
void SetCharacter(Character* character);
void GetCharacter(Character* character);
- Character* RetCharacter();
+ Character* GetCharacter();
- float RetAbsTime();
+ float GetAbsTime();
void SetEnergy(float level);
- float RetEnergy();
+ float GetEnergy();
void SetCapacity(float capacity);
- float RetCapacity();
+ float GetCapacity();
void SetShield(float level);
- float RetShield();
+ float GetShield();
void SetRange(float delay);
- float RetRange();
+ float GetRange();
void SetTransparency(float value);
- float RetTransparency();
+ float GetTransparency();
- ObjectMaterial RetMaterial();
+ ObjectMaterial GetMaterial();
void SetGadget(bool bMode);
- bool RetGadget();
+ bool GetGadget();
void SetFixed(bool bFixed);
- bool RetFixed();
+ bool GetFixed();
void SetClip(bool bClip);
- bool RetClip();
+ bool GetClip();
bool JostleObject(float force);
void StartDetectEffect(CObject *target, bool bFound);
void SetVirusMode(bool bEnable);
- bool RetVirusMode();
- float RetVirusTime();
+ bool GetVirusMode();
+ float GetVirusTime();
- void SetCameraType(CameraType type);
- CameraType RetCameraType();
+ void SetCameraType(Gfx::CameraType type);
+ Gfx::CameraType GetCameraType();
void SetCameraDist(float dist);
- float RetCameraDist();
+ float GetCameraDist();
void SetCameraLock(bool bLock);
- bool RetCameraLock();
+ bool GetCameraLock();
void SetHilite(bool bMode);
- bool RetHilite();
+ bool GetHilite();
void SetSelect(bool bMode, bool bDisplayError=true);
- bool RetSelect(bool bReal=false);
+ bool GetSelect(bool bReal=false);
void SetSelectable(bool bMode);
- bool RetSelectable();
+ bool GetSelectable();
void SetActivity(bool bMode);
- bool RetActivity();
+ bool GetActivity();
void SetVisible(bool bVisible);
- bool RetVisible();
+ bool GetVisible();
void SetEnable(bool bEnable);
- bool RetEnable();
+ bool GetEnable();
void SetCheckToken(bool bMode);
- bool RetCheckToken();
+ bool GetCheckToken();
void SetProxyActivate(bool bActivate);
- bool RetProxyActivate();
+ bool GetProxyActivate();
void SetProxyDistance(float distance);
- float RetProxyDistance();
+ float GetProxyDistance();
void SetMagnifyDamage(float factor);
- float RetMagnifyDamage();
+ float GetMagnifyDamage();
void SetParam(float value);
- float RetParam();
+ float GetParam();
void SetExplo(bool bExplo);
- bool RetExplo();
+ bool GetExplo();
void SetLock(bool bLock);
- bool RetLock();
+ bool GetLock();
void SetCargo(bool bCargo);
- bool RetCargo();
+ bool GetCargo();
void SetBurn(bool bBurn);
- bool RetBurn();
+ bool GetBurn();
void SetDead(bool bDead);
- bool RetDead();
- bool RetRuin();
- bool RetActif();
+ bool GetDead();
+ bool GetRuin();
+ bool GetActif();
void SetGunGoalV(float gunGoal);
void SetGunGoalH(float gunGoal);
- float RetGunGoalV();
- float RetGunGoalH();
+ float GetGunGoalV();
+ float GetGunGoalH();
bool StartShowLimit();
void StopShowLimit();
@@ -618,16 +617,16 @@ public:
void CreateSelectParticule();
void SetRunScript(CScript* script);
- CScript* RetRunScript();
- CBotVar* RetBotVar();
- CPhysics* RetPhysics();
- CBrain* RetBrain();
- CMotion* RetMotion();
- CAuto* RetAuto();
+ CScript* GetRunScript();
+ CBotVar* GetBotVar();
+ CPhysics* GetPhysics();
+ CBrain* GetBrain();
+ CMotion* GetMotion();
+ CAuto* GetAuto();
void SetAuto(CAuto* automat);
void SetDefRank(int rank);
- int RetDefRank();
+ int GetDefRank();
bool GetTooltipName(char* name);
@@ -635,17 +634,17 @@ public:
CObject* SubDeselList();
void DeleteDeselList(CObject* pObj);
- bool CreateShadowCircle(float radius, float intensity, D3DShadowType type=D3DSHADOWNORM);
- bool CreateShadowLight(float height, D3DCOLORVALUE color);
- bool CreateEffectLight(float height, D3DCOLORVALUE color);
+ bool CreateShadowCircle(float radius, float intensity, Gfx::EngineShadowType type = Gfx::ENG_SHADOW_NORM);
+ bool CreateShadowLight(float height, Gfx::Color color);
+ bool CreateEffectLight(float height, Gfx::Color color);
void FlatParent();
- bool RetTraceDown();
+ bool GetTraceDown();
void SetTraceDown(bool bDown);
- int RetTraceColor();
+ int GetTraceColor();
void SetTraceColor(int color);
- float RetTraceWidth();
+ float GetTraceWidth();
void SetTraceWidth(float width);
protected:
@@ -663,19 +662,19 @@ protected:
protected:
CInstanceManager* m_iMan;
- CD3DEngine* m_engine;
- CLight* m_light;
- CTerrain* m_terrain;
- CWater* m_water;
- CCamera* m_camera;
- CParticule* m_particule;
+ Gfx::CEngine* m_engine;
+ Gfx::CLightManager* m_lightMan;
+ Gfx::CTerrain* m_terrain;
+ Gfx::CWater* m_water;
+ Gfx::CCamera* m_camera;
+ Gfx::CParticle* m_particle;
CPhysics* m_physics;
CBrain* m_brain;
CMotion* m_motion;
CAuto* m_auto;
CDisplayText* m_displayText;
CRobotMain* m_main;
- CSound* m_sound;
+ CSoundInterface* m_sound;
CBotVar* m_botVar;
CScript* m_runScript;
@@ -732,7 +731,7 @@ protected:
float m_showLimitRadius;
float m_gunGoalV;
float m_gunGoalH;
- CameraType m_cameraType;
+ Gfx::CameraType m_cameraType;
float m_cameraDist;
bool m_bCameraLock;
int m_defRank;
@@ -767,7 +766,7 @@ protected:
int m_infoTotal;
Info m_info[OBJECTMAXINFO];
- float m_infoReturn;
+ float m_infoGeturn;
bool m_bInfoUpdate;
float m_cmdLine[OBJECTMAXCMDLINE];
diff --git a/src/object/robotmain.cpp b/src/object/robotmain.cpp
index fb68152..668be87 100644
--- a/src/object/robotmain.cpp
+++ b/src/object/robotmain.cpp
@@ -722,7 +722,7 @@ CRobotMain::CRobotMain(CInstanceManager* iMan)
g_unit = 4.0f;
m_gamerName[0] = 0;
- GetLocalProfileString("Gamer", "LastName", m_gamerName, 100);
+ GetProfile()->GetLocalProfileString("Gamer", "LastName", m_gamerName, 100);
SetGlobalGamerName(m_gamerName);
ReadFreeParam();
m_dialog->SetupRecall();
diff --git a/src/physics/README.txt b/src/physics/README.txt
index 0003956..4ad5989 100644
--- a/src/physics/README.txt
+++ b/src/physics/README.txt
@@ -1,3 +1,4 @@
-src/physics
-
-Contains the physics module.
+/**
+ * \dir physics
+ * \brief Physics engine
+ */ \ No newline at end of file
diff --git a/src/physics/physics.h b/src/physics/physics.h
index 6865b6a..2e1f5eb 100644
--- a/src/physics/physics.h
+++ b/src/physics/physics.h
@@ -19,23 +19,27 @@
#pragma once
-#include "old/d3dengine.h"
#include "common/misc.h"
#include "object/object.h"
+#include "math/vector.h"
class CInstanceManager;
-class CD3DEngine;
-class CLight;
-class CParticule;
-class CTerrain;
-class CWater;
class CCamera;
class CObject;
class CBrain;
class CMotion;
class CSound;
+namespace Gfx
+{
+class CEngine;
+class CLight;
+class CParticule;
+class CTerrain;
+class CWater;
+};
+
enum PhysicsType
{
@@ -98,64 +102,64 @@ public:
void SetMotion(CMotion* motion);
void SetType(PhysicsType type);
- PhysicsType RetType();
+ PhysicsType GetType();
bool Write(char *line);
bool Read(char *line);
void SetGravity(float value);
- float RetGravity();
+ float GetGravity();
- float RetFloorHeight();
+ float GetFloorHeight();
void SetLinMotion(PhysicsMode mode, Math::Vector value);
- Math::Vector RetLinMotion(PhysicsMode mode);
+ Math::Vector GetLinMotion(PhysicsMode mode);
void SetLinMotionX(PhysicsMode mode, float value);
void SetLinMotionY(PhysicsMode mode, float value);
void SetLinMotionZ(PhysicsMode mode, float value);
- float RetLinMotionX(PhysicsMode mode);
- float RetLinMotionY(PhysicsMode mode);
- float RetLinMotionZ(PhysicsMode mode);
+ float GetLinMotionX(PhysicsMode mode);
+ float GetLinMotionY(PhysicsMode mode);
+ float GetLinMotionZ(PhysicsMode mode);
void SetCirMotion(PhysicsMode mode, Math::Vector value);
- Math::Vector RetCirMotion(PhysicsMode mode);
+ Math::Vector GetCirMotion(PhysicsMode mode);
void SetCirMotionX(PhysicsMode mode, float value);
void SetCirMotionY(PhysicsMode mode, float value);
void SetCirMotionZ(PhysicsMode mode, float value);
- float RetCirMotionX(PhysicsMode mode);
- float RetCirMotionY(PhysicsMode mode);
- float RetCirMotionZ(PhysicsMode mode);
+ float GetCirMotionX(PhysicsMode mode);
+ float GetCirMotionY(PhysicsMode mode);
+ float GetCirMotionZ(PhysicsMode mode);
- float RetLinStopLength(PhysicsMode sMode=MO_ADVSPEED, PhysicsMode aMode=MO_STOACCEL);
- float RetCirStopLength();
- float RetLinMaxLength(float dir);
- float RetLinTimeLength(float dist, float dir=1.0f);
- float RetLinLength(float dist);
+ float GetLinStopLength(PhysicsMode sMode=MO_ADVSPEED, PhysicsMode aMode=MO_STOACCEL);
+ float GetCirStopLength();
+ float GetLinMaxLength(float dir);
+ float GetLinTimeLength(float dist, float dir=1.0f);
+ float GetLinLength(float dist);
void SetMotor(bool bState);
- bool RetMotor();
+ bool GetMotor();
void SetLand(bool bState);
- bool RetLand();
+ bool GetLand();
void SetSwim(bool bState);
- bool RetSwim();
+ bool GetSwim();
void SetCollision(bool bCollision);
- bool RetCollision();
+ bool GetCollision();
void SetFreeze(bool bFreeze);
- bool RetFreeze();
+ bool GetFreeze();
void SetReactorRange(float range);
- float RetReactorRange();
+ float GetReactorRange();
void SetMotorSpeed(Math::Vector speed);
void SetMotorSpeedX(float speed);
void SetMotorSpeedY(float speed);
void SetMotorSpeedZ(float speed);
- Math::Vector RetMotorSpeed();
- float RetMotorSpeedX();
- float RetMotorSpeedY();
- float RetMotorSpeedZ();
+ Math::Vector GetMotorSpeed();
+ float GetMotorSpeedX();
+ float GetMotorSpeedY();
+ float GetMotorSpeedZ();
void CreateInterface(bool bSelect);
- Error RetError();
+ Error GetError();
protected:
bool EventFrame(const Event &event);
@@ -186,12 +190,12 @@ protected:
protected:
CInstanceManager* m_iMan;
- CD3DEngine* m_engine;
- CLight* m_light;
- CParticule* m_particule;
- CTerrain* m_terrain;
- CWater* m_water;
- CCamera* m_camera;
+ Gfx::CEngine* m_engine;
+ Gfx::CLightManager* m_lightMan;
+ Gfx::CParticle* m_particle;
+ Gfx::CTerrain* m_terrain;
+ Gfx::CWater* m_water;
+ Gfx::CCamera* m_camera;
CObject* m_object;
CBrain* m_brain;
CMotion* m_motion;
diff --git a/src/plugins/plugininterface.h b/src/plugins/plugininterface.h
new file mode 100644
index 0000000..b8adddc
--- /dev/null
+++ b/src/plugins/plugininterface.h
@@ -0,0 +1,65 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// plugininterface.h
+
+/**
+ * @file plugin/plugininterface.h
+ * @brief Generic plugin interface
+ */
+
+#pragma once
+
+#include <string>
+
+#define PLUGIN_INTERFACE(class_type) \
+ static class_type* Plugin##class_type; \
+ extern "C" void InstallPluginEntry() { Plugin##class_type = new class_type(); Plugin##class_type->InstallPlugin(); } \
+ extern "C" bool UninstallPluginEntry(std::string &reason) { bool result = Plugin##class_type->UninstallPlugin(reason); \
+ if (!result) \
+ return false; \
+ delete Plugin##class_type; \
+ return true; } \
+ extern "C" CPluginInterface* GetPluginInterfaceEntry() { return static_cast<CPluginInterface*>(Plugin##class_type); }
+
+
+/**
+* @class CPluginInterface
+*
+* @brief Generic plugin interface. All plugins that will be managed by plugin manager have to derive from this class.
+*
+*/
+class CPluginInterface {
+ public:
+ /** Function to get plugin name or description
+ * @return returns plugin name
+ */
+ virtual std::string PluginName() = 0;
+
+ /** Function to get plugin version. 1 means version 0.01, 2 means 0.02 etc.
+ * @return number indicating plugin version
+ */
+ virtual int PluginVersion() = 0;
+
+ /** Function to initialize plugin
+ */
+ virtual void InstallPlugin() = 0;
+
+ /** Function called before removing plugin
+ */
+ virtual bool UninstallPlugin(std::string &) = 0;
+};
+
diff --git a/src/plugins/pluginloader.cpp b/src/plugins/pluginloader.cpp
new file mode 100644
index 0000000..fd8ce74
--- /dev/null
+++ b/src/plugins/pluginloader.cpp
@@ -0,0 +1,126 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2012 Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// pluginloader.cpp
+
+
+#include "pluginloader.h"
+
+
+CPluginLoader::CPluginLoader(std::string filename)
+{
+ mInterface = nullptr;
+ mFilename = filename;
+ mLoaded = false;
+}
+
+
+std::string CPluginLoader::GetName()
+{
+ if (mLoaded)
+ return mInterface->PluginName();
+ return "(not loaded)";
+}
+
+
+int CPluginLoader::GetVersion()
+{
+ if (mLoaded)
+ return mInterface->PluginVersion();
+ return 0;
+}
+
+
+bool CPluginLoader::IsLoaded()
+{
+ return mLoaded;
+}
+
+
+bool CPluginLoader::UnloadPlugin()
+{
+ if (!mLoaded) {
+ GetLogger()->Warn("Plugin %s is not loaded.\n");
+ return true;
+ }
+
+ bool (*uninstall)(std::string &) = (bool (*)(std::string &)) lt_dlsym(mHandle, "UninstallPluginEntry");
+ if (!uninstall) {
+ GetLogger()->Error("Error getting UninstallPluginEntry for plugin %s: %s\n", mFilename.c_str(), lt_dlerror());
+ return false;
+ }
+
+ std::string reason;
+ if (!uninstall(reason)) {
+ GetLogger()->Error("Could not unload plugin %s: %s\n", mFilename.c_str(), reason.c_str());
+ return false;
+ }
+
+ lt_dlclose(mHandle);
+ mLoaded = false;
+ return true;
+}
+
+
+bool CPluginLoader::LoadPlugin()
+{
+ if (mFilename.length() == 0) {
+ GetLogger()->Warn("No plugin filename specified.\n");
+ return false;
+ }
+
+ mHandle = lt_dlopenext(mFilename.c_str());
+ if (!mHandle) {
+ GetLogger()->Error("Error loading plugin %s: %s\n", mFilename.c_str(), lt_dlerror());
+ return false;
+ }
+
+ void (*install)() = (void (*)()) lt_dlsym(mHandle, "InstallPluginEntry");
+ if (!install) {
+ GetLogger()->Error("Error getting InstallPluginEntry for plugin %s: %s\n", mFilename.c_str(), lt_dlerror());
+ return false;
+ }
+
+ CPluginInterface* (*getInterface)() = (CPluginInterface* (*)()) lt_dlsym(mHandle, "GetPluginInterfaceEntry");
+
+ if (!getInterface) {
+ GetLogger()->Error("Error getting GetPluginInterfaceEntry for plugin %s: %s\n", mFilename.c_str(), lt_dlerror());
+ return false;
+ }
+
+ install();
+ mInterface = getInterface();
+ mLoaded = true;
+ return true;
+}
+
+
+bool CPluginLoader::SetFilename(std::string filename)
+{
+ bool ok = true;
+ if (mLoaded)
+ ok = UnloadPlugin();
+
+ if (ok)
+ mFilename = filename;
+ return ok;
+}
+
+
+std::string CPluginLoader::GetFilename()
+{
+ return mFilename;
+}
diff --git a/src/plugins/pluginloader.h b/src/plugins/pluginloader.h
new file mode 100644
index 0000000..40b19da
--- /dev/null
+++ b/src/plugins/pluginloader.h
@@ -0,0 +1,88 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// pluginloader.h
+
+/**
+ * @file plugin/pluginloader.h
+ * @brief Plugin loader interface
+ */
+
+#pragma once
+
+#include <ltdl.h>
+#include <string>
+
+#include <common/logger.h>
+
+#include "plugininterface.h"
+
+
+/**
+* @class CPluginLoader
+*
+* @brief Plugin loader interface. Plugin manager uses this class to load plugins.
+*
+*/
+class CPluginLoader {
+ public:
+ /** Class contructor
+ * @param std::string plugin filename
+ */
+ CPluginLoader(std::string);
+
+ /** Function to get plugin name or description
+ * @return returns plugin name
+ */
+ std::string GetName();
+
+ /** Function to get plugin version
+ * @return returns plugin version
+ */
+ int GetVersion();
+
+ /** Function to unload plugin
+ * @return returns true on success
+ */
+ bool UnloadPlugin();
+
+ /** Function to load plugin
+ * @return returns true on success
+ */
+ bool LoadPlugin();
+
+ /** Function to check if plugin is loaded
+ * @return returns true if plugin is loaded
+ */
+ bool IsLoaded();
+
+ /** Function to set plugin filename
+ * @return returns true on success. Action can fail if plugin was loaded and cannot be unloaded
+ */
+ bool SetFilename(std::string);
+
+ /** Function to get plugin filename
+ * @return returns plugin filename
+ */
+ std::string GetFilename();
+
+
+ private:
+ CPluginInterface* mInterface;
+ std::string mFilename;
+ lt_dlhandle mHandle;
+ bool mLoaded;
+};
diff --git a/src/plugins/pluginmanager.cpp b/src/plugins/pluginmanager.cpp
new file mode 100644
index 0000000..470ac2f
--- /dev/null
+++ b/src/plugins/pluginmanager.cpp
@@ -0,0 +1,132 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2012 Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// pluginmanager.cpp
+
+
+#include "pluginmanager.h"
+
+
+template<> CPluginManager* CSingleton<CPluginManager>::mInstance = nullptr;
+
+
+CPluginManager::CPluginManager()
+{
+ lt_dlinit();
+}
+
+
+CPluginManager::~CPluginManager()
+{
+ UnloadAllPlugins();
+ lt_dlexit();
+}
+
+
+
+void CPluginManager::LoadFromProfile()
+{
+ std::vector< std::string > dirs = GetProfile()->GetLocalProfileSection("Plugins", "Path");
+ std::vector< std::string > plugins = GetProfile()->GetLocalProfileSection("Plugins", "File");
+
+ for (std::string dir : dirs)
+ m_folders.insert(dir);
+
+ for (std::string plugin : plugins) {
+ GetLogger()->Info("Trying to load plugin %s...\n", plugin.c_str());
+ LoadPlugin(plugin);
+ }
+}
+
+
+bool CPluginManager::LoadPlugin(std::string filename)
+{
+ bool result = false;
+ CPluginLoader *loader = new CPluginLoader("");
+ for (std::string dir : m_folders) {
+ loader->SetFilename(dir + "/" + filename);
+ result = loader->LoadPlugin();
+ if (result) {
+ GetLogger()->Info("Plugin %s (%s) version %0.2f loaded!\n", filename.c_str(), loader->GetName().c_str(), loader->GetVersion() / 100.0f);
+ m_plugins.push_back(loader);
+ break;
+ }
+ }
+ return result;
+}
+
+
+bool CPluginManager::UnloadPlugin(std::string filename)
+{
+ std::vector<CPluginLoader *>::iterator it;
+ GetLogger()->Info("Trying to unload plugin %s...\n", filename.c_str());
+ for (it = m_plugins.begin(); it != m_plugins.end(); it++) {
+ CPluginLoader *plugin = *it;
+ if (NameEndsWith(plugin->GetFilename(), filename)) {
+ m_plugins.erase(it);
+ plugin->UnloadPlugin();
+ delete plugin;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool CPluginManager::AddSearchDirectory(std::string dir)
+{
+ m_folders.insert(dir);
+ return true;
+}
+
+
+bool CPluginManager::RemoveSearchDirectory(std::string dir)
+{
+ m_folders.erase(dir);
+ return false;
+}
+
+
+bool CPluginManager::UnloadAllPlugins()
+{
+ bool allOk = true;
+ std::vector<CPluginLoader *>::iterator it;
+ for (it = m_plugins.begin(); it != m_plugins.end(); it++) {
+ CPluginLoader *plugin = *it;
+ bool result;
+
+ GetLogger()->Info("Trying to unload plugin %s (%s)...\n", plugin->GetFilename().c_str(), plugin->GetName().c_str());
+ result = plugin->UnloadPlugin();
+ if (!result) {
+ allOk = false;
+ continue;
+ }
+ delete plugin;
+ m_plugins.erase(it);
+ }
+
+ return allOk;
+}
+
+
+bool CPluginManager::NameEndsWith(std::string filename, std::string ending)
+{
+ if (filename.length() > ending.length()) {
+ std::string fileEnd = filename.substr(filename.length() - ending.length());
+ return (fileEnd == ending);
+ }
+ return false;
+}
diff --git a/src/plugins/pluginmanager.h b/src/plugins/pluginmanager.h
new file mode 100644
index 0000000..e425c62
--- /dev/null
+++ b/src/plugins/pluginmanager.h
@@ -0,0 +1,88 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// pluginmanager.h
+
+/**
+ * @file plugin/pluginmanager.h
+ * @brief Plugin manager class.
+ */
+
+#pragma once
+
+#include <string>
+#include <set>
+#include <vector>
+
+#include <common/logger.h>
+#include <common/profile.h>
+
+#include <common/singleton.h>
+
+#include "pluginloader.h"
+
+
+/**
+* @class CPluginManager
+*
+* @brief Plugin manager class. Plugin manager can load plugins from colobot.ini or manually specified files.
+*
+*/
+class CPluginManager : public CSingleton<CPluginManager> {
+ public:
+ CPluginManager();
+ ~CPluginManager();
+
+ /** Function loads plugin list and path list from profile file
+ */
+ void LoadFromProfile();
+
+ /** Function loads specified plugin
+ * @param std::string plugin filename
+ * @return returns true on success
+ */
+ bool LoadPlugin(std::string);
+
+ /** Function unloads specified plugin
+ * @param std::string plugin filename
+ * @return returns true on success
+ */
+ bool UnloadPlugin(std::string);
+
+ /** Function adds path to be checked when searching for plugin file. If path was already added it will be ignored
+ * @param std::string plugin search path
+ * @return returns true on success
+ */
+ bool AddSearchDirectory(std::string);
+
+ /** Function removes path from list
+ * @param std::string plugin search path
+ * @return returns true on success
+ */
+ bool RemoveSearchDirectory(std::string);
+
+ /** Function tries to unload all plugins
+ * @return returns true on success
+ */
+ bool UnloadAllPlugins();
+
+ private:
+ bool NameEndsWith(std::string, std::string);
+
+ std::set< std::string > m_folders;
+ std::vector<CPluginLoader *> m_plugins;
+};
+
diff --git a/src/plugins/test/CMakeLists.txt b/src/plugins/test/CMakeLists.txt
new file mode 100644
index 0000000..5f86b6f
--- /dev/null
+++ b/src/plugins/test/CMakeLists.txt
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 2.8)
+
+set(CMAKE_BUILD_TYPE debug)
+set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g -O0 -std=c++11 -rdynamic")
+
+add_executable(manager_test manager_test.cpp ../../common/logger.cpp ../../common/profile.cpp ../../common/iman.cpp ../pluginmanager.cpp ../pluginloader.cpp)
+
+include_directories(".")
+include_directories("../../")
+include_directories("../../../")
+
+target_link_libraries(manager_test ltdl)
diff --git a/src/plugins/test/colobot.ini b/src/plugins/test/colobot.ini
new file mode 100644
index 0000000..08956be
--- /dev/null
+++ b/src/plugins/test/colobot.ini
@@ -0,0 +1,3 @@
+[Plugins]
+Path=.
+File=libopenalsound.so
diff --git a/src/plugins/test/manager_test.cpp b/src/plugins/test/manager_test.cpp
new file mode 100644
index 0000000..d921c1d
--- /dev/null
+++ b/src/plugins/test/manager_test.cpp
@@ -0,0 +1,31 @@
+#include <common/logger.h>
+#include <common/profile.h>
+#include <common/iman.h>
+#include <plugins/pluginmanager.h>
+#include <sound/sound.h>
+
+
+int main() {
+ new CLogger();
+ new CProfile();
+ new CInstanceManager();
+ CPluginManager *mgr = new CPluginManager();
+
+ if (!GetProfile()->InitCurrentDirectory()) {
+ GetLogger()->Error("Config not found!\n");
+ return 1;
+ }
+
+ mgr->LoadFromProfile();
+ CSoundInterface *sound = static_cast<CSoundInterface*>(CInstanceManager::GetInstancePointer()->SearchInstance(CLASS_SOUND));
+
+ if (!sound) {
+ GetLogger()->Error("Sound not loaded!\n");
+ return 2;
+ }
+
+ sound->Create(true);
+ mgr->UnloadAllPlugins();
+
+ return 0;
+}
diff --git a/src/sound/README.txt b/src/sound/README.txt
index d6ac0bd..fa7bbad 100644
--- a/src/sound/README.txt
+++ b/src/sound/README.txt
@@ -1,3 +1,4 @@
-src/sound
-
-Contains the sound module - for playing sounds and music.
+/**
+ * \dir sound
+ * \brief Sound module - playing sounds and music
+ */ \ No newline at end of file
diff --git a/src/sound/plugins/oalsound/CMakeLists.txt b/src/sound/plugins/oalsound/CMakeLists.txt
new file mode 100644
index 0000000..e36f3ac
--- /dev/null
+++ b/src/sound/plugins/oalsound/CMakeLists.txt
@@ -0,0 +1,24 @@
+cmake_minimum_required(VERSION 2.8)
+
+set(SOURCES
+ alsound.cpp
+ buffer.cpp
+ channel.cpp
+)
+
+SET (CMAKE_CXX_FLAGS "-Wall -g -std=c++0x -fPIC")
+
+include(FindPkgConfig)
+include(FindOpenAL)
+pkg_check_modules(OPENAL_LIB REQUIRED openal)
+
+set(OPENAL_LIBRARIES
+ openal
+ alut
+)
+
+
+include_directories(../../..)
+include_directories(.)
+add_library(openalsound SHARED ${SOURCES})
+target_link_libraries(openalsound ${OPENAL_LIBRARIES})
diff --git a/src/sound/plugins/oalsound/alsound.cpp b/src/sound/plugins/oalsound/alsound.cpp
new file mode 100644
index 0000000..8cd8221
--- /dev/null
+++ b/src/sound/plugins/oalsound/alsound.cpp
@@ -0,0 +1,553 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2001-2008, Daniel ROUX & EPSITEC SA, www.epsitec.ch
+// * Copyright (C) 2012 Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// alsound.cpp
+
+
+#include "alsound.h"
+
+
+#define MIN(a, b) (a > b ? b : a)
+
+
+PLUGIN_INTERFACE(ALSound)
+
+
+std::string ALSound::PluginName()
+{
+ return "Sound plugin using OpenAL library to play sounds.";
+}
+
+
+int ALSound::PluginVersion()
+{
+ return 2;
+}
+
+
+void ALSound::InstallPlugin()
+{
+ auto pointer = CInstanceManager::GetInstancePointer();
+ if (pointer != nullptr)
+ CInstanceManager::GetInstancePointer()->AddInstance(CLASS_SOUND, this);
+}
+
+
+bool ALSound::UninstallPlugin(std::string &reason)
+{
+ auto pointer = CInstanceManager::GetInstancePointer();
+ if (pointer != nullptr)
+ CInstanceManager::GetInstancePointer()->DeleteInstance(CLASS_SOUND, this);
+ CleanUp();
+ return true;
+}
+
+
+ALSound::ALSound()
+{
+ mEnabled = false;
+ m3D = false;
+ mAudioVolume = MAXVOLUME;
+ mMute = false;
+}
+
+
+ALSound::~ALSound()
+{
+ CleanUp();
+}
+
+
+void ALSound::CleanUp()
+{
+ if (mEnabled) {
+ GetLogger()->Info("Unloading files and closing device...\n");
+ StopAll();
+
+ for (auto item : mSounds)
+ delete item.second;
+
+ mEnabled = false;
+ alutExit();
+ }
+}
+
+
+bool ALSound::Create(bool b3D)
+{
+ CleanUp();
+
+ if (mEnabled)
+ return true;
+
+ GetLogger()->Info("Opening audio device...\n");
+ if (!alutInit(NULL, NULL)) {
+ ALenum error = alutGetError();
+ GetLogger()->Error("Could not open audio device! Reason: %s\n", alutGetErrorString(error));
+ return false;
+ }
+ GetLogger()->Info("Done.\n");
+
+ mEnabled = true;
+ return true;
+}
+
+
+void ALSound::SetSound3D(bool bMode)
+{
+ // TODO stub! need to be implemented
+ m3D = bMode;
+}
+
+
+bool ALSound::RetSound3D()
+{
+ // TODO stub! need to be implemented
+ return true;
+}
+
+
+bool ALSound::RetSound3DCap()
+{
+ // TODO stub! need to be implemented
+ return true;
+}
+
+
+bool ALSound::RetEnable()
+{
+ return mEnabled;
+}
+
+
+void ALSound::SetAudioVolume(int volume)
+{
+ alListenerf(AL_GAIN, MIN(volume, MAXVOLUME) * 0.01f);
+ mAudioVolume = MIN(volume, MAXVOLUME);
+}
+
+
+int ALSound::RetAudioVolume()
+{
+ float volume;
+ if ( !mEnabled )
+ return 0;
+
+ alGetListenerf(AL_GAIN, &volume);
+ return volume * MAXVOLUME;
+}
+
+
+void ALSound::SetMusicVolume(int volume)
+{
+ // TODO stub! Add music support
+}
+
+
+int ALSound::RetMusicVolume()
+{
+ // TODO stub! Add music support
+ if ( !mEnabled )
+ return 0;
+
+ return 0;
+}
+
+
+bool ALSound::Cache(Sound sound, std::string filename)
+{
+ Buffer *buffer = new Buffer();
+ if (buffer->LoadFromFile(filename, sound)) {
+ mSounds[sound] = buffer;
+ return true;
+ }
+ return false;
+}
+
+
+void ALSound::CacheAll()
+{
+ char filename[100];
+ for ( int i = 1; i < 69; i++ )
+ {
+ sprintf(filename, "high/sound%.3d.wav", i);
+ if ( !Cache((Sound) i, std::string(filename)) )
+ {
+ fprintf(stderr, "Unable to load audio: %s\n", filename);
+ }
+ }
+}
+
+
+int ALSound::RetPriority(Sound sound)
+{
+ if ( sound == SOUND_FLYh ||
+ sound == SOUND_FLY ||
+ sound == SOUND_MOTORw ||
+ sound == SOUND_MOTORt ||
+ sound == SOUND_MOTORr ||
+ sound == SOUND_MOTORs ||
+ sound == SOUND_SLIDE ||
+ sound == SOUND_ERROR )
+ {
+ return 30;
+ }
+
+ if ( sound == SOUND_CONVERT ||
+ sound == SOUND_ENERGY ||
+ sound == SOUND_DERRICK ||
+ sound == SOUND_STATION ||
+ sound == SOUND_REPAIR ||
+ sound == SOUND_RESEARCH ||
+ sound == SOUND_BURN ||
+ sound == SOUND_BUILD ||
+ sound == SOUND_TREMBLE ||
+ sound == SOUND_NUCLEAR ||
+ sound == SOUND_EXPLO ||
+ sound == SOUND_EXPLOl ||
+ sound == SOUND_EXPLOlp ||
+ sound == SOUND_EXPLOp ||
+ sound == SOUND_EXPLOi )
+ {
+ return 20;
+ }
+
+ if ( sound == SOUND_BLUP ||
+ sound == SOUND_INSECTs ||
+ sound == SOUND_INSECTa ||
+ sound == SOUND_INSECTb ||
+ sound == SOUND_INSECTw ||
+ sound == SOUND_INSECTm ||
+ sound == SOUND_PSHHH ||
+ sound == SOUND_EGG )
+ {
+ return 0;
+ }
+
+ return 10;
+}
+
+
+bool ALSound::SearchFreeBuffer(Sound sound, int &channel, bool &bAlreadyLoaded)
+{
+ int priority = RetPriority(sound);
+
+ // Seeks a channel used which sound is stopped.
+ for (auto it : mChannels) {
+ if (it.second->IsPlaying())
+ continue;
+ if (it.second->GetSoundType() != sound)
+ continue;
+
+ it.second->SetPriority(priority);
+ channel = it.first;
+ bAlreadyLoaded = true;
+ return true;
+ }
+
+ // just add a new channel if we dont have any
+ if (mChannels.size() == 0) {
+ Channel *chn = new Channel();
+ // check if we channel ready to play music, if not report error
+ if (chn->IsReady()) {
+ chn->SetPriority(priority);
+ mChannels[1] = chn;
+ channel = 1;
+ bAlreadyLoaded = false;
+ return true;
+ }
+ delete chn;
+ GetLogger()->Error("Could not open channel to play sound!");
+ return false;
+ }
+
+ // Seeks a channel completely free.
+ auto it = mChannels.end();
+ it--;
+ int i = (*it).first;
+ while (++i)
+ {
+ if (mChannels.find(i) == mChannels.end()) {
+ Channel *chn = new Channel();
+ // check if we channel ready to play music, if not destroy it and seek free one
+ if (chn->IsReady()) {
+ chn->SetPriority(priority);
+ mChannels[1] = chn;
+ channel = 1;
+ bAlreadyLoaded = false;
+ return true;
+ }
+ delete chn;
+ GetLogger()->Warn("Could not open additional channel to play sound!");
+ }
+ }
+
+ int lowerOrEqual = -1;
+ for (auto it : mChannels) {
+ if (it.second->GetPriority() < priority) {
+ GetLogger()->Info("Sound channel with lower priority will be reused.");
+ channel = it.first;
+ return true;
+ }
+ if (it.second->GetPriority() <= priority)
+ lowerOrEqual = it.first;
+ }
+
+ if (lowerOrEqual != -1) {
+ channel = lowerOrEqual;
+ GetLogger()->Info("Sound channel with lower or equal priority will be reused.");
+ return true;
+ }
+
+ GetLogger()->Warn("Could not find free buffer to use.\n");
+ return false;
+}
+
+
+int ALSound::Play(Sound sound, float amplitude, float frequency, bool bLoop)
+{
+ return Play(sound, Math::Vector(), amplitude, frequency, bLoop);
+}
+
+
+int ALSound::Play(Sound sound, Math::Vector pos, float amplitude, float frequency, bool bLoop)
+{
+ if (!mEnabled)
+ return -1;
+
+ if (mAudioVolume <= 0.0f)
+ return -1;
+
+ if (mSounds.find(sound) == mSounds.end()) {
+ GetLogger()->Warn("Sound %d was not loaded!\n", sound);
+ return -1;
+ }
+
+ int channel;
+ bool bAlreadyLoaded;
+ if (!SearchFreeBuffer(sound, channel, bAlreadyLoaded))
+ return -1;
+ if ( !bAlreadyLoaded ) {
+ mChannels[channel]->SetBuffer(mSounds[sound]);
+ }
+
+ Position(channel, pos);
+
+ // setting initial values
+ mChannels[channel]->SetStartAmplitude(amplitude);
+ mChannels[channel]->SetStartFrequency(frequency);
+ mChannels[channel]->SetChangeFrequency(1.0f);
+ mChannels[channel]->ResetOper();
+ mChannels[channel]->AdjustFrequency(frequency);
+ mChannels[channel]->AdjustVolume(mAudioVolume);
+ mChannels[channel]->Play();
+ return channel;
+}
+
+
+bool ALSound::FlushEnvelope(int channel)
+{
+ if (mChannels.find(channel) == mChannels.end()) {
+ return false;
+ }
+
+ mChannels[channel]->ResetOper();
+ return true;
+}
+
+
+bool ALSound::AddEnvelope(int channel, float amplitude, float frequency, float time, SoundNext oper)
+{
+ if (!mEnabled)
+ return false;
+
+ if (mChannels.find(channel) == mChannels.end()) {
+ return false;
+ }
+
+ SoundOper op;
+ op.finalAmplitude = amplitude;
+ op.finalFrequency = frequency;
+ op.totalTime = time;
+ op.nextOper = oper;
+ mChannels[channel]->AddOper(op);
+
+ return false;
+}
+
+
+bool ALSound::Position(int channel, Math::Vector pos)
+{
+ if (!mEnabled)
+ return false;
+
+ if (mChannels.find(channel) == mChannels.end()) {
+ return false;
+ }
+
+ mChannels[channel]->SetPosition(pos);
+ return true;
+}
+
+
+bool ALSound::Frequency(int channel, float frequency)
+{
+ if (!mEnabled)
+ return false;
+
+ if (mChannels.find(channel) == mChannels.end()) {
+ return false;
+ }
+
+ mChannels[channel]->SetFrequency(frequency);
+ return true;
+}
+
+bool ALSound::Stop(int channel)
+{
+ if (!mEnabled)
+ return false;
+
+ if (mChannels.find(channel) == mChannels.end()) {
+ return false;
+ }
+
+ mChannels[channel]->Stop();
+ mChannels[channel]->ResetOper();
+
+ return true;
+}
+
+
+bool ALSound::StopAll()
+{
+ if (!mEnabled)
+ return false;
+
+ for (auto channel : mChannels) {
+ channel.second->Stop();
+ channel.second->ResetOper();
+ }
+
+ return true;
+}
+
+
+bool ALSound::MuteAll(bool bMute)
+{
+ if (!mEnabled)
+ return false;
+
+ float volume;
+ mMute = bMute;
+ if (mMute)
+ volume = 0;
+ else
+ volume = mAudioVolume;
+
+ for (auto channel : mChannels) {
+ channel.second->SetVolume(volume);
+ }
+
+ return true;
+}
+
+
+void ALSound::FrameMove(float delta)
+{
+ if (!mEnabled)
+ return;
+
+ float progress;
+ float volume, frequency;
+ for (auto it : mChannels) {
+ if (!it.second->IsPlaying())
+ continue;
+
+ if (!it.second->HasEnvelope())
+ continue;
+
+ //it.second->GetEnvelope().currentTime += delta;
+ SoundOper oper = it.second->GetEnvelope();
+ progress = it.second->GetCurrentTime() / oper.totalTime;
+ progress = MIN(progress, 1.0f);
+
+ // setting volume
+ volume = progress * abs(oper.finalAmplitude - it.second->GetStartAmplitude());
+ it.second->AdjustVolume(volume * mAudioVolume);
+
+ // setting frequency
+ frequency = progress * (oper.finalFrequency - it.second->GetStartFrequency()) * it.second->GetStartFrequency() * it.second->GetChangeFrequency();
+ it.second->AdjustFrequency(frequency);
+
+ if (it.second->GetEnvelope().totalTime <= it.second->GetCurrentTime()) {
+
+ if (oper.nextOper == SOPER_LOOP) {
+ GetLogger()->Info("Replay.\n");
+ it.second->SetCurrentTime(0.0f);
+ it.second->Play();
+ } else {
+ GetLogger()->Info("Next.\n");
+ it.second->SetStartAmplitude(oper.finalAmplitude);
+ it.second->SetStartFrequency(oper.finalFrequency);
+ it.second->PopEnvelope();
+ }
+ }
+ }
+}
+
+
+void ALSound::SetListener(Math::Vector eye, Math::Vector lookat)
+{
+ GetLogger()->Info("Setting listener position.\n");
+ float orientation[] = {lookat.x, lookat.y, lookat.z, 0.f, 1.f, 0.f};
+ alListener3f(AL_POSITION, eye.x, eye.y, eye.z);
+ alListenerfv(AL_ORIENTATION, orientation);
+}
+
+
+bool ALSound::PlayMusic(int rank, bool bRepeat)
+{
+ // TODO stub! Add music support
+ return true;
+}
+
+
+bool ALSound::RestartMusic()
+{
+ // TODO stub! Add music support
+ return true;
+}
+
+void ALSound::StopMusic()
+{
+ // TODO stub! Add music support
+ SuspendMusic();
+}
+
+
+bool ALSound::IsPlayingMusic()
+{
+ // TODO stub! Add music support
+ return true;
+}
+
+
+void ALSound::SuspendMusic()
+{
+ // TODO stub! Add music support
+}
diff --git a/src/sound/plugins/oalsound/alsound.h b/src/sound/plugins/oalsound/alsound.h
new file mode 100644
index 0000000..c1cdb81
--- /dev/null
+++ b/src/sound/plugins/oalsound/alsound.h
@@ -0,0 +1,95 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2001-2008, Daniel ROUX & EPSITEC SA, www.epsitec.ch
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// alsound.h
+
+#pragma once
+
+#include <map>
+#include <string>
+
+#include <AL/alut.h>
+
+#include <common/iman.h>
+#include <common/logger.h>
+#include <sound/sound.h>
+
+#include "buffer.h"
+#include "channel.h"
+#include "check.h"
+
+
+class ALSound : public CSoundInterface
+{
+ public:
+ ALSound();
+ ~ALSound();
+
+ bool Create(bool b3D);
+ void CacheAll();
+ bool Cache(Sound, std::string);
+
+ bool RetEnable();
+
+ void SetSound3D(bool bMode);
+ bool RetSound3D();
+ bool RetSound3DCap();
+
+ void SetAudioVolume(int volume);
+ int RetAudioVolume();
+ void SetMusicVolume(int volume);
+ int RetMusicVolume();
+
+ void SetListener(Math::Vector eye, Math::Vector lookat);
+ void FrameMove(float rTime);
+
+ int Play(Sound sound, float amplitude=1.0f, float frequency=1.0f, bool bLoop = false);
+ int Play(Sound sound, Math::Vector pos, float amplitude=1.0f, float frequency=1.0f, bool bLoop = false);
+ bool FlushEnvelope(int channel);
+ bool AddEnvelope(int channel, float amplitude, float frequency, float time, SoundNext oper);
+ bool Position(int channel, Math::Vector pos);
+ bool Frequency(int channel, float frequency);
+ bool Stop(int channel);
+ bool StopAll();
+ bool MuteAll(bool bMute);
+
+ bool PlayMusic(int rank, bool bRepeat);
+ bool RestartMusic();
+ void SuspendMusic();
+ void StopMusic();
+ bool IsPlayingMusic();
+
+ // plugin interface
+ std::string PluginName();
+ int PluginVersion();
+ void InstallPlugin();
+ bool UninstallPlugin(std::string &);
+
+ private:
+ void CleanUp();
+ int RetPriority(Sound);
+ bool SearchFreeBuffer(Sound sound, int &channel, bool &bAlreadyLoaded);
+
+ bool mEnabled;
+ bool m3D;
+ bool mMute;
+ int mAudioVolume;
+ ALCdevice* audioDevice;
+ ALCcontext* audioContext;
+ std::map<Sound, Buffer*> mSounds;
+ std::map<int, Channel*> mChannels;
+};
diff --git a/src/sound/plugins/oalsound/buffer.cpp b/src/sound/plugins/oalsound/buffer.cpp
new file mode 100644
index 0000000..37211e9
--- /dev/null
+++ b/src/sound/plugins/oalsound/buffer.cpp
@@ -0,0 +1,80 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// buffer.cpp
+
+#include "buffer.h"
+
+Buffer::Buffer() {
+ mLoaded = false;
+ mDuration = 0;
+}
+
+
+Buffer::~Buffer() {
+ if (mLoaded) {
+ alDeleteBuffers(1, &mBuffer);
+ if (alCheck())
+ GetLogger()->Warn("Failed to unload buffer. Code %d\n", alGetCode());
+ }
+}
+
+
+bool Buffer::LoadFromFile(std::string filename, Sound sound) {
+ mSound = sound;
+
+ GetLogger()->Info("Loading audio file: %s\n", filename.c_str());
+ mBuffer = alutCreateBufferFromFile(filename.c_str());
+
+ ALenum error = alutGetError();
+ if (error) {
+ GetLogger()->Warn("Failed to load file. Reason: %s\n", alutGetErrorString(error));
+ mLoaded = false;
+ return false;
+ }
+
+ ALint size, bits, channels, freq;
+
+ alGetBufferi(mBuffer, AL_SIZE, &size);
+ alGetBufferi(mBuffer, AL_BITS, &bits);
+ alGetBufferi(mBuffer, AL_CHANNELS, &channels);
+ alGetBufferi(mBuffer, AL_FREQUENCY, &freq);
+
+ mDuration = (ALfloat)size / channels / bits / 8 / (ALfloat)freq;
+
+ mLoaded = true;
+ return true;
+}
+
+
+Sound Buffer::GetSoundType() {
+ return mSound;
+}
+
+
+ALuint Buffer::GetBuffer() {
+ return mBuffer;
+}
+
+
+bool Buffer::IsLoaded() {
+ return mLoaded;
+}
+
+
+float Buffer::GetDuration() {
+ return mDuration;
+}
diff --git a/src/sound/plugins/oalsound/buffer.h b/src/sound/plugins/oalsound/buffer.h
new file mode 100644
index 0000000..6eefe72
--- /dev/null
+++ b/src/sound/plugins/oalsound/buffer.h
@@ -0,0 +1,48 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// buffer.h
+
+#pragma once
+
+#include <string>
+
+#include <AL/alut.h>
+
+#include <sound/sound.h>
+#include <common/logger.h>
+
+#include "check.h"
+
+class Buffer
+{
+ public:
+ Buffer();
+ ~Buffer();
+
+ bool LoadFromFile(std::string, Sound);
+ bool IsLoaded();
+
+ Sound GetSoundType();
+ ALuint GetBuffer();
+ float GetDuration();
+
+ private:
+ ALuint mBuffer;
+ Sound mSound;
+ bool mLoaded;
+ float mDuration;
+};
diff --git a/src/sound/plugins/oalsound/channel.cpp b/src/sound/plugins/oalsound/channel.cpp
new file mode 100644
index 0000000..4476dee
--- /dev/null
+++ b/src/sound/plugins/oalsound/channel.cpp
@@ -0,0 +1,304 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// channel.cpp
+
+#include "channel.h"
+
+
+Channel::Channel() {
+ alGenSources(1, &mSource);
+
+ if (alCheck()) {
+ GetLogger()->Warn("Failed to create sound source. Code: %d\n", alGetCode());
+ mReady = false;
+ } else {
+ mReady = true;
+ }
+
+ mPriority = 0;
+ mBuffer = nullptr;
+}
+
+
+Channel::~Channel() {
+ if (mReady) {
+ alSourcei(mSource, AL_BUFFER, 0);
+ alDeleteSources(1, &mSource);
+ if (alCheck())
+ GetLogger()->Warn("Failed to delete sound source. Code: %s\n", alGetCode());
+ }
+}
+
+
+bool Channel::Play() {
+ if (!mReady)
+ return false;
+
+ alSourcePlay(mSource);
+ if (alCheck())
+ GetLogger()->Warn("Could not play audio sound source. Code: %s\n", alGetCode());
+ return true;
+}
+
+
+bool Channel::SetPosition(Math::Vector pos) {
+ if (!mReady)
+ return false;
+
+ alSource3f(mSource, AL_POSITION, pos.x, pos.y, pos.z);
+ if (alCheck()) {
+ GetLogger()->Warn("Could not set sound position. Code: %s\n", alGetCode());
+ return false;
+ }
+ return true;
+}
+
+
+bool Channel::SetFrequency(float freq)
+{
+ if (!mReady)
+ return false;
+
+ alSourcef(mSource, AL_PITCH, freq);
+ if (alCheck()) {
+ GetLogger()->Warn("Could not set sound pitch. Code: %s\n", alGetCode());
+ return false;
+ }
+ return true;
+}
+
+
+float Channel::GetFrequency()
+{
+ ALfloat freq;
+ if (!mReady)
+ return 0;
+
+ alGetSourcef(mSource, AL_PITCH, &freq);
+ if (alCheck()) {
+ GetLogger()->Warn("Could not get sound pitch. Code: %s\n", alGetCode());
+ return 0;
+ }
+
+ return freq;
+}
+
+
+bool Channel::SetVolume(float vol)
+{
+ if (!mReady || vol < 0)
+ return false;
+
+ alSourcef(mSource, AL_GAIN, vol / MAXVOLUME);
+ if (alCheck()) {
+ GetLogger()->Warn("Could not set sound volume. Code: %s\n", alGetCode());
+ return false;
+ }
+ return true;
+}
+
+
+float Channel::GetVolume()
+{
+ ALfloat vol;
+ if (!mReady)
+ return 0;
+
+ alGetSourcef(mSource, AL_GAIN, &vol);
+ if (alCheck()) {
+ GetLogger()->Warn("Could not get sound volume. Code: %s\n", alGetCode());
+ return 0;
+ }
+
+ return vol * MAXVOLUME;
+}
+
+
+int Channel::GetPriority()
+{
+ return mPriority;
+}
+
+
+void Channel::SetPriority(int pri)
+{
+ mPriority = pri;
+}
+
+
+void Channel::SetStartAmplitude(float gain)
+{
+ mStartAmplitude = gain;
+}
+
+
+void Channel::SetStartFrequency(float freq)
+{
+ mStartFrequency = freq;
+}
+
+
+void Channel::SetChangeFrequency(float freq)
+{
+ mChangeFrequency = freq;
+}
+
+
+void Channel::SetInitFrequency(float freq)
+{
+ mInitFrequency = freq;
+}
+
+
+float Channel::GetStartAmplitude()
+{
+ return mStartAmplitude;
+}
+
+
+float Channel::GetStartFrequency()
+{
+ return mStartFrequency;
+}
+
+
+float Channel::GetChangeFrequency()
+{
+ return mChangeFrequency;
+}
+
+
+float Channel::GetInitFrequency()
+{
+ return mInitFrequency;
+}
+
+
+void Channel::AddOper(SoundOper oper)
+{
+ mOper.push_back(oper);
+}
+
+
+void Channel::ResetOper()
+{
+ mOper.clear();
+}
+
+
+Sound Channel::GetSoundType() {
+ return mBuffer->GetSoundType();
+}
+
+
+bool Channel::SetBuffer(Buffer *buffer) {
+ if (!mReady)
+ return false;
+
+ assert(buffer);
+ mBuffer = buffer;
+ alSourcei(mSource, AL_BUFFER, buffer->GetBuffer());
+ if (alCheck()) {
+ GetLogger()->Warn("Could not set sound buffer. Code: %s\n", alGetCode());
+ return false;
+ }
+ mInitFrequency = GetFrequency();
+ return true;
+}
+
+
+void Channel::AdjustFrequency(float freq) {
+ SetFrequency(freq * mInitFrequency);
+}
+
+
+void Channel::AdjustVolume(float volume) {
+ SetVolume(mStartAmplitude * (float) volume);
+}
+
+
+bool Channel::IsPlaying() {
+ ALint status;
+ if (!mReady) return false;
+
+ alGetSourcei(mSource, AL_SOURCE_STATE, &status);
+ if (alCheck()) {
+ GetLogger()->Warn("Could not get sound status. Code: %s\n", alGetCode());
+ return false;
+ }
+
+ return status == AL_PLAYING;
+}
+
+
+bool Channel::IsReady() {
+ return mReady;
+}
+
+
+bool Channel::Stop() {
+ alSourceStop(mSource);
+ if (alCheck()) {
+ GetLogger()->Warn("Could not stop sound. Code: %s\n", alGetCode());
+ return false;
+ }
+ return true;
+}
+
+
+float Channel::GetCurrentTime()
+{
+ ALfloat current;
+ alGetSourcef(mSource, AL_SEC_OFFSET, &current);
+ if (alCheck()) {
+ GetLogger()->Warn("Could not get source current play time. Code: %s\n", alGetCode());
+ return 0.0f;
+ }
+ return current;
+}
+
+
+void Channel::SetCurrentTime(float current)
+{
+ alSourcef(mSource, AL_SEC_OFFSET, current);
+ if (alCheck())
+ GetLogger()->Warn("Could not get source current play time. Code: %s\n", alGetCode());
+}
+
+
+float Channel::GetDuration()
+{
+ return mBuffer->GetDuration();
+}
+
+
+bool Channel::HasEnvelope()
+{
+ return mOper.size() > 0;
+}
+
+
+SoundOper& Channel::GetEnvelope()
+{
+ return mOper.front();
+}
+
+
+void Channel::PopEnvelope()
+{
+ mOper.pop_front();
+}
diff --git a/src/sound/plugins/oalsound/channel.h b/src/sound/plugins/oalsound/channel.h
new file mode 100644
index 0000000..3099931
--- /dev/null
+++ b/src/sound/plugins/oalsound/channel.h
@@ -0,0 +1,99 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// channel.h
+
+#pragma once
+
+#include <string>
+#include <deque>
+#include <cassert>
+
+#include <AL/al.h>
+#include <AL/alc.h>
+
+#include <sound/sound.h>
+
+#include "buffer.h"
+#include "check.h"
+
+struct SoundOper
+{
+ float finalAmplitude;
+ float finalFrequency;
+ float totalTime;
+ SoundNext nextOper;
+};
+
+
+class Channel
+{
+ public:
+ Channel();
+ ~Channel();
+
+ bool Play();
+ bool Stop();
+ bool SetPosition(Math::Vector);
+
+ bool SetFrequency(float);
+ float GetFrequency();
+
+ float GetCurrentTime();
+ void SetCurrentTime(float);
+ float GetDuration();
+
+ bool SetVolume(float);
+ float GetVolume();
+ bool IsPlaying();
+ bool IsReady();
+
+ bool SetBuffer(Buffer *);
+ bool HasEnvelope();
+ SoundOper& GetEnvelope();
+ void PopEnvelope();
+
+ int GetPriority();
+ void SetPriority(int);
+
+ void SetStartAmplitude(float);
+ void SetStartFrequency(float);
+ void SetChangeFrequency(float);
+ void SetInitFrequency(float);
+
+ float GetStartAmplitude();
+ float GetStartFrequency();
+ float GetChangeFrequency();
+ float GetInitFrequency();
+
+ void AddOper(SoundOper);
+ void ResetOper();
+ Sound GetSoundType();
+ void AdjustFrequency(float);
+ void AdjustVolume(float);
+
+ private:
+ Buffer *mBuffer;
+ ALuint mSource;
+
+ int mPriority;
+ float mStartAmplitude;
+ float mStartFrequency;
+ float mChangeFrequency;
+ float mInitFrequency;
+ std::deque<SoundOper> mOper;
+ bool mReady;
+};
diff --git a/src/plugins/plugin.h b/src/sound/plugins/oalsound/check.h
index f238122..cb6b4a1 100644
--- a/src/plugins/plugin.h
+++ b/src/sound/plugins/oalsound/check.h
@@ -14,20 +14,26 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// plugin.h
-
+// check.h
#pragma once
+#include <AL/al.h>
+#include <AL/alc.h>
-#define PLUGIN_INTERFACE(class_type, interface_type) \
- extern "C" interface_type* installPlugin() { return (interface_type *)new class_type(); } \
- extern "C" void uninstallPlugin(class_type *_class) { delete _class; }
+#include <common/logger.h>
+static ALenum CODE = AL_NO_ERROR;
-class CPlugin {
- public:
- virtual char* PluginName() = 0;
- virtual int PluginVersion() = 0;
-};
+inline bool alCheck()
+{
+ CODE = alGetError();
+ return CODE != AL_NO_ERROR;
+}
+inline ALenum alGetCode()
+{
+ ALenum ret = CODE;
+ CODE = AL_NO_ERROR;
+ return ret;
+}
diff --git a/src/sound/plugins/oalsound/test/CMakeLists.txt b/src/sound/plugins/oalsound/test/CMakeLists.txt
new file mode 100644
index 0000000..d10169b
--- /dev/null
+++ b/src/sound/plugins/oalsound/test/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 2.8)
+
+set(CMAKE_BUILD_TYPE debug)
+set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g -O0 -std=c++11 -rdynamic")
+
+add_executable(plugin_test plugin_test.cpp ../../../../common/iman.cpp ../../../../common/logger.cpp ../../../../plugins/pluginloader.cpp)
+
+include_directories(".")
+include_directories("../../../../")
+
+target_link_libraries(plugin_test ltdl)
diff --git a/src/sound/plugins/oalsound/test/plugin_test.cpp b/src/sound/plugins/oalsound/test/plugin_test.cpp
new file mode 100644
index 0000000..40c1cd2
--- /dev/null
+++ b/src/sound/plugins/oalsound/test/plugin_test.cpp
@@ -0,0 +1,40 @@
+#include <string>
+#include <cstdio>
+#include <unistd.h>
+
+#include <common/logger.h>
+#include <common/iman.h>
+#include <sound/sound.h>
+#include <plugins/pluginloader.h>
+
+
+int main() {
+ new CLogger();
+ new CInstanceManager();
+
+ lt_dlinit();
+
+ CPluginLoader *plugin = new CPluginLoader("libopenalsound");
+ if (plugin->LoadPlugin()) {
+ CSoundInterface *sound = static_cast<CSoundInterface*>(CInstanceManager::GetInstancePointer()->SearchInstance(CLASS_SOUND));
+
+ sound->Create(true);
+ sound->CacheAll();
+ sound->Play((Sound)8);
+ sound->Play((Sound)18);
+
+ sleep(10);
+ /*
+ while (1)
+ {
+ // just a test, very slow
+ plugin->FrameMove(0);
+ //if ('n' == getchar())
+ // break;
+ }*/
+ plugin->UnloadPlugin();
+ }
+
+ lt_dlexit();
+ return 0;
+}
diff --git a/src/sound/sound.h b/src/sound/sound.h
index a1b8fe3..45ec7e1 100644
--- a/src/sound/sound.h
+++ b/src/sound/sound.h
@@ -15,10 +15,10 @@
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
-// soundinterface.h
+// sound.h
/**
- * @file sound/soundinterface.h
+ * @file sound/sound.h
* @brief Sound plugin interface
*/
@@ -26,7 +26,7 @@
#include <math/vector.h>
-#include <plugins/plugin.h>
+#include <plugins/plugininterface.h>
#include <string>
@@ -39,7 +39,7 @@
/**
* \public
- * \enum Sound sound/soundinterface.h
+ * \enum Sound sound/sound.h
* \brief Sound enum representing sound file
**/
enum Sound
@@ -131,7 +131,7 @@ enum Sound
/**
* \public
- * \enum SoundNext sound/soundinterface.h
+ * \enum SoundNext sound/sound.h
* \brief Enum representing operation that will be performend on a sound at given time
**/
enum SoundNext
@@ -148,7 +148,7 @@ enum SoundNext
* @brief Sound plugin interface
*
*/
-class CSoundInterface : public CPlugin
+class CSoundInterface : public CPluginInterface
{
public:
CSoundInterface() {
@@ -319,3 +319,4 @@ class CSoundInterface : public CPlugin
*/
virtual bool IsPlayingMusic() = 0;
};
+
diff --git a/src/ui/README.txt b/src/ui/README.txt
index 4ffd8ec..9814ef0 100644
--- a/src/ui/README.txt
+++ b/src/ui/README.txt
@@ -1,3 +1,4 @@
-src/ui
-
-Contains modules responsible for displaying the user interface controls (from game menus and HUD).
+/**
+ * \dir ui
+ * \brief 2D user interface controls
+ */ \ No newline at end of file