summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-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.cpp40
-rw-r--r--src/common/logger.h38
-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.h15
-rw-r--r--src/graphics/core/texture.h36
-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.cpp3722
-rw-r--r--src/graphics/engine/engine.h917
-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.h79
-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.cpp638
-rw-r--r--src/graphics/engine/water.h152
-rw-r--r--src/graphics/opengl/README.txt13
-rw-r--r--src/graphics/opengl/gldevice.cpp65
-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, 19370 insertions, 2463 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 98e9732..04b8912 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
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..ed8df8c 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()
@@ -57,8 +43,10 @@ void CLogger::Log(LogType type, const char *str, va_list args)
return;
switch (type) {
- case LOG_WARN: fprintf(IsOpened() ? mFile : stderr, "[WARN]: "); break;
- case LOG_INFO: fprintf(IsOpened() ? mFile : stderr, "[INFO]: "); break;
+ case LOG_TRACE: fprintf(IsOpened() ? mFile : stderr, "[TRACE]: "); break;
+ case LOG_DEBUG: fprintf(IsOpened() ? mFile : stderr, "[DEBUG]: "); break;
+ case LOG_WARN: fprintf(IsOpened() ? mFile : stderr, "[WARN]: "); break;
+ case LOG_INFO: fprintf(IsOpened() ? mFile : stderr, "[INFO]: "); break;
case LOG_ERROR: fprintf(IsOpened() ? mFile : stderr, "[ERROR]: "); break;
default: break;
}
@@ -67,6 +55,24 @@ void CLogger::Log(LogType type, const char *str, va_list args)
}
+void CLogger::Trace(const char *str, ...)
+{
+ va_list args;
+ va_start(args, str);
+ Log(LOG_TRACE, str, args);
+ va_end(args);
+}
+
+
+void CLogger::Debug(const char *str, ...)
+{
+ va_list args;
+ va_start(args, str);
+ Log(LOG_DEBUG, str, args);
+ va_end(args);
+}
+
+
void CLogger::Info(const char *str, ...)
{
va_list args;
diff --git a/src/common/logger.h b/src/common/logger.h
index a67aefe..fc43735 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>
@@ -37,10 +38,12 @@
**/
enum LogType
{
- LOG_INFO = 1, /*!< lowest level, information */
- LOG_WARN = 2, /*!< warning */
- LOG_ERROR = 3, /*!< error */
- LOG_NONE = 4 /*!< none level, used for custom messages */
+ LOG_TRACE = 1, /*!< lowest level, execution tracing */
+ LOG_DEBUG = 2, /*!< debugging messages */
+ LOG_INFO = 3, /*!< information */
+ LOG_WARN = 4, /*!< warning */
+ LOG_ERROR = 5, /*!< error */
+ LOG_NONE = 6 /*!< none level, used for custom messages */
};
@@ -57,42 +60,51 @@ 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_TRACE level
+ * @param str - message to write
+ * @param ... - additional arguments
+ */
+ void Trace(const char *str, ...);
+
+ /** Write message to console or file with LOG_DEBUG level
+ * @param str - message to write
+ * @param ... - additional arguments
+ */
+ void Debug(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..156ff36 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
@@ -42,6 +45,16 @@ struct Material
Gfx::Color ambient;
//! Specular color
Gfx::Color specular;
+
+ bool operator==(const Gfx::Material &mat) const
+ {
+ return diffuse == mat.diffuse && ambient == mat.ambient && specular == mat.specular;
+ }
+
+ bool operator!=(const Gfx::Material &mat) const
+ {
+ return ! operator==(mat);
+ }
};
}; // namespace Gfx
diff --git a/src/graphics/core/texture.h b/src/graphics/core/texture.h
index 8d6b082..8abe86a 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 {
@@ -189,34 +192,43 @@ struct TextureStageParams
Also contains some additional data. */
struct Texture
{
- //! Whether the texture (ID) is valid
- bool valid;
- //! ID of the texture in graphics engine
+ //! ID of the texture in graphics engine; 0 = invalid texture
unsigned int id;
//! Size of texture
- Math::IntSize size;
+ Math::IntPoint size;
//! Whether the texture has alpha channel
bool alpha;
Texture()
{
- valid = false;
id = 0;
alpha = false;
}
+ //! Returns whether the texture is valid (ID != 0)
+ bool Valid() const
+ {
+ return id != 0;
+ }
+
+ //! Sets the ID to invalid value (0)
+ void SetInvalid()
+ {
+ id = 0;
+ }
+
//! Comparator for use in texture maps and sets
inline bool operator<(const Gfx::Texture &other) const
{
// Invalid textures are always "less than" every other texture
- if ( (!valid) && (!other.valid) )
+ if ( (! Valid()) && (! other.Valid()) )
return false;
- if (!valid)
+ if (! Valid())
return true;
- if (!other.valid)
+ if (! other.Valid())
return false;
return id < other.id;
@@ -225,9 +237,9 @@ struct Texture
//! Comparator
inline bool operator==(const Gfx::Texture &other) const
{
- if (valid != other.valid)
+ if (Valid() != other.Valid())
return false;
- if ( (!valid) && (!other.valid) )
+ if ( (! Valid()) && (! other.Valid()) )
return true;
return id == other.id;
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..d7bac0f 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_lines.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_linesUsed == 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_linesUsed ; i++ )
+ {
+ pos.y = m_level;
+ pos.z = m_lines[i].pz;
+ pos.x = m_lines[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_lines[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_lines.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_lines.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..079f77b 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_lines;
- bool m_bEnable;
+ bool m_enable;
};
diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp
index e544ee3..ddc6b23 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;
@@ -35,37 +47,77 @@ const int GROUNDSPOT_PREALLOCATE_COUNT = 100;
const int LEVEL1_PREALLOCATE_COUNT = 50;
const int LEVEL2_PREALLOCATE_COUNT = 100;
const int LEVEL3_PREALLOCATE_COUNT = 5;
-const int LEVEL4_PREALLOCATE_COUNT = 10;
-const int LEVEL5_PREALLOCATE_COUNT = 100;
-const int LEVEL5_VERTEX_PREALLOCATE_COUNT = 200;
+const int LEVEL4_PREALLOCATE_COUNT = 100;
+const int LEVEL4_VERTEX_PREALLOCATE_COUNT = 200;
+
+
+Gfx::EngineObjLevel1::EngineObjLevel1(bool used, const std::string& tex1Name, const std::string& tex2Name)
+{
+ this->used = used;
+ this->tex1Name = tex1Name;
+ this->tex2Name = tex2Name;
+
+ next.reserve(LEVEL2_PREALLOCATE_COUNT);
+}
+
+Gfx::EngineObjLevel2::EngineObjLevel2(bool used, int objRank)
+{
+ this->used = used;
+ this->objRank = objRank;
+
+ next.reserve(LEVEL3_PREALLOCATE_COUNT);
+}
+
+Gfx::EngineObjLevel3::EngineObjLevel3(bool used, float min, float max)
+{
+ this->used = used;
+ this->min = min;
+ this->max = max;
+
+ next.reserve(LEVEL4_PREALLOCATE_COUNT);
+}
+
+Gfx::EngineObjLevel4::EngineObjLevel4(bool used, Gfx::EngineTriangleType type, const Gfx::Material& material, int state)
+{
+ this->used = used;
+ this->type = type;
+ this->material = material;
+ this->state = state;
+
+ vertices.reserve(LEVEL4_VERTEX_PREALLOCATE_COUNT);
+}
+
+// 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 +151,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 +171,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 +183,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 +208,1634 @@ 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;
+}
+
+void Gfx::CEngine::SetDevice(Gfx::CDevice *device)
+{
+ m_device = device;
+}
- m_sound = NULL;
- m_terrain = NULL;
+Gfx::CDevice* Gfx::CEngine::GetDevice()
+{
+ return m_device;
}
-bool Gfx::CEngine::GetWasInit()
+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 = LoadTexture("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))
+ {
+ bool bq = !m_backgroundQuarter;
+ SetBackground(bq ? "geneda.png" : "", Gfx::Color(), Gfx::Color(), Gfx::Color(), Gfx::Color(), true, bq);
+ }
+ else if (event.key.key == KEY(F4))
+ {
+ m_skyMode = !m_skyMode;
+ if (! m_skyMode)
+ {
+ 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 write screenshot: not very important for now
+ GetLogger()->Trace("CEngine::WriteSceenShot(): stub!\n");
+ 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: when INI reading is completed
+ return true;
+}
+bool Gfx::CEngine::WriteSettings()
+{
+ // TODO: when INI writing is completed
return true;
}
-void Gfx::CEngine::ResetAfterDeviceChanged()
+void Gfx::CEngine::SetPause(bool pause)
+{
+ m_pause = pause;
+}
+
+bool Gfx::CEngine::GetPause()
{
- // TODO
+ return m_pause;
}
+void Gfx::CEngine::SetMovieLock(bool lock)
+{
+ m_movieLock = lock;
+}
-Gfx::Texture Gfx::CEngine::CreateTexture(const std::string &texName, const Gfx::TextureCreateParams &params)
+bool Gfx::CEngine::GetMovieLock()
{
- CImage img;
- if (! img.Load(m_app->GetDataFilePath(m_texPath, texName)))
+ 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()
+{
+ int i = 0;
+ for ( ; i < static_cast<int>( m_objects.size() ); i++)
{
- std::stringstream str;
- str << "Couldn't load texture '" << texName << "': " << img.GetError();
- m_error = str.str();
- return Gfx::Texture(); // invalid texture
+ if (! m_objects[i].used)
+ {
+ m_objects[i].LoadDefault();
+ break;
+ }
}
- Gfx::Texture result = m_device->CreateTexture(&img, params);
+ if (i == static_cast<int>( m_objects.size() ))
+ m_objects.push_back(Gfx::EngineObject());
+
+
+ m_objects[i].used = true;
- if (! result.valid)
+ Math::Matrix mat;
+ mat.LoadIdentity();
+ SetObjectTransform(i, mat);
+
+ m_objects[i].drawWorld = true;
+ m_objects[i].distance = 0.0f;
+ m_objects[i].bboxMin = Math::Vector(0.0f, 0.0f, 0.0f);
+ m_objects[i].bboxMax = Math::Vector(0.0f, 0.0f, 0.0f);
+ m_objects[i].shadowRank = -1;
+
+ return i;
+}
+
+void Gfx::CEngine::FlushObject()
+{
+ m_objectTree.clear();
+ m_objects.clear();
+
+ m_shadows.clear();
+
+ FlushGroundSpot();
+}
+
+bool Gfx::CEngine::DeleteObject(int objRank)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ // Delete object's triangles
+ for (int l1 = 0; l1 < static_cast<int>( m_objectTree.size() ); l1++)
{
- std::stringstream str;
- str << "Couldn't load texture '" << texName << "': " << m_device->GetError();
- m_error = str.str();
- return result;
+ Gfx::EngineObjLevel1& p1 = m_objectTree[l1];
+ if (! p1.used) continue;
+
+ for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
+ {
+ Gfx::EngineObjLevel2& p2 = p1.next[l2];
+ if (! p2.used) continue;
+
+ if (p2.objRank == objRank)
+ {
+ p2.used = false;
+ p2.next.clear();
+ }
+ }
}
- m_texNameMap[texName] = result;
- m_revTexNameMap[result] = texName;
+ // Mark object as deleted
+ m_objects[objRank].used = false;
- return result;
+ // Delete associated shadows
+ DeleteShadow(objRank);
+
+ return true;
}
-Gfx::Texture Gfx::CEngine::CreateTexture(const std::string &texName)
+bool Gfx::CEngine::SetObjectType(int objRank, Gfx::EngineObjectType type)
{
- return CreateTexture(texName, m_defaultTexParams);
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ m_objects[objRank].type = type;
+ return true;
}
-void Gfx::CEngine::DestroyTexture(const std::string &texName)
+Gfx::EngineObjectType Gfx::CEngine::GetObjectType(int objRank)
{
- std::map<std::string, Gfx::Texture>::iterator it = m_texNameMap.find(texName);
- if (it == m_texNameMap.end())
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return Gfx::ENG_OBJTYPE_NULL;
+
+ return m_objects[objRank].type;
+}
+
+
+bool Gfx::CEngine::SetObjectTransform(int objRank, const Math::Matrix& transform)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ m_objects[objRank].transform = transform;
+ return true;
+}
+
+bool Gfx::CEngine::GetObjectTransform(int objRank, Math::Matrix& transform)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ transform = m_objects[objRank].transform;
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectDrawWorld(int objRank, bool draw)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ m_objects[objRank].drawWorld = draw;
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectDrawFront(int objRank, bool draw)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ m_objects[objRank].drawFront = draw;
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectTransparency(int objRank, float value)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ m_objects[objRank].transparency = value;
+ return true;
+}
+
+bool Gfx::CEngine::GetObjectBBox(int objRank, Math::Vector& min, Math::Vector& max)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return 0;
+
+ min = m_objects[objRank].bboxMin;
+ max = m_objects[objRank].bboxMax;
+ return true;
+}
+
+
+int Gfx::CEngine::GetObjectTotalTriangles(int objRank)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return 0;
+
+ return m_objects[objRank].totalTriangles;
+}
+
+
+Gfx::EngineObjLevel1& Gfx::CEngine::AddLevel1(const std::string& tex1Name, const std::string& tex2Name)
+{
+ bool unusedPresent = false;
+ for (int i = 0; i < static_cast<int>( m_objectTree.size() ); i++)
+ {
+ if (! m_objectTree[i].used)
+ {
+ unusedPresent = true;
+ continue;
+ }
+
+ if (m_objectTree[i].tex1Name == tex1Name && m_objectTree[i].tex2Name == tex2Name)
+ return m_objectTree[i];
+ }
+
+ if (unusedPresent)
+ {
+ for (int i = 0; i < static_cast<int>( m_objectTree.size() ); i++)
+ {
+ if (! m_objectTree[i].used)
+ {
+ m_objectTree[i].used = true;
+ m_objectTree[i].tex1Name = tex1Name;
+ m_objectTree[i].tex2Name = tex2Name;
+ return m_objectTree[i];
+ }
+ }
+ }
+
+ m_objectTree.push_back(Gfx::EngineObjLevel1(true, tex1Name, tex2Name));
+ return m_objectTree.back();
+}
+
+Gfx::EngineObjLevel2& Gfx::CEngine::AddLevel2(Gfx::EngineObjLevel1& p1, int objRank)
+{
+ bool unusedPresent = false;
+ for (int i = 0; i < static_cast<int>( p1.next.size() ); i++)
+ {
+ if (! p1.next[i].used)
+ {
+ unusedPresent = true;
+ continue;
+ }
+
+ if (p1.next[i].objRank == objRank)
+ return p1.next[i];
+ }
+
+ if (unusedPresent)
+ {
+ for (int i = 0; i < static_cast<int>( p1.next.size() ); i++)
+ {
+ if (! p1.next[i].used)
+ {
+ p1.next[i].used = true;
+ p1.next[i].objRank = objRank;
+ return p1.next[i];
+ }
+ }
+ }
+
+ p1.next.push_back(Gfx::EngineObjLevel2(true, objRank));
+ return p1.next.back();
+}
+
+Gfx::EngineObjLevel3& Gfx::CEngine::AddLevel3(Gfx::EngineObjLevel2& p2, float min, float max)
+{
+ bool unusedPresent = false;
+ for (int i = 0; i < static_cast<int>( p2.next.size() ); i++)
+ {
+ if (! p2.next[i].used)
+ {
+ unusedPresent = true;
+ continue;
+ }
+
+ if ( (p2.next[i].min == min) && (p2.next[i].max == max) )
+ return p2.next[i];
+ }
+
+ if (unusedPresent)
+ {
+ for (int i = 0; i < static_cast<int>( p2.next.size() ); i++)
+ {
+ if (! p2.next[i].used)
+ {
+ p2.next[i].used = true;
+ p2.next[i].min = min;
+ p2.next[i].max = max;
+ return p2.next[i];
+ }
+ }
+ }
+
+ p2.next.push_back(Gfx::EngineObjLevel3(true, min, max));
+ return p2.next.back();
+}
+
+Gfx::EngineObjLevel4& Gfx::CEngine::AddLevel4(Gfx::EngineObjLevel3& p3, Gfx::EngineTriangleType type,
+ const Gfx::Material& material, int state)
+{
+ bool unusedPresent = false;
+ for (int i = 0; i < static_cast<int>( p3.next.size() ); i++)
+ {
+ if (! p3.next[i].used)
+ {
+ unusedPresent = true;
+ continue;
+ }
+
+ if ( (p3.next[i].type == type) && (p3.next[i].material == material) && (p3.next[i].state == state) )
+ return p3.next[i];
+ }
+
+ if (unusedPresent)
+ {
+ for (int i = 0; i < static_cast<int>( p3.next.size() ); i++)
+ {
+ if (! p3.next[i].used)
+ {
+ p3.next[i].used = true;
+ p3.next[i].type = type;
+ p3.next[i].material = material;
+ p3.next[i].state = state;
+ return p3.next[i];
+ }
+ }
+ }
+
+ p3.next.push_back(Gfx::EngineObjLevel4(true, type, material, state));
+ return p3.next.back();
+}
+
+bool Gfx::CEngine::AddTriangles(int objRank, const std::vector<Gfx::VertexTex2>& vertices,
+ const Gfx::Material& material, int state,
+ std::string tex1Name, std::string tex2Name,
+ float min, float max, bool globalUpdate)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ {
+ GetLogger()->Error("AddTriangle(): invalid object rank %d\n", objRank);
+ return false;
+ }
+
+ m_lastSize = m_size;
+ m_lastObjectDetail = m_objectDetail;
+ m_lastClippingDistance = m_clippingDistance;
+
+ Gfx::EngineObjLevel1& p1 = AddLevel1(tex1Name, tex2Name);
+ Gfx::EngineObjLevel2& p2 = AddLevel2(p1, objRank);
+ Gfx::EngineObjLevel3& p3 = AddLevel3(p2, min, max);
+ Gfx::EngineObjLevel4& p4 = AddLevel4(p3, Gfx::ENG_TRIANGLE_TYPE_TRIANGLES, material, state);
+
+ p4.vertices.insert(p4.vertices.end(), vertices.begin(), vertices.end());
+
+ if (globalUpdate)
+ {
+ m_updateGeometry = true;
+ }
+ else
+ {
+ for (int i = 0; i < static_cast<int>( vertices.size() ); i++)
+ {
+ m_objects[objRank].bboxMin.x = Math::Min(vertices[i].coord.x, m_objects[objRank].bboxMin.x);
+ m_objects[objRank].bboxMin.y = Math::Min(vertices[i].coord.y, m_objects[objRank].bboxMin.y);
+ m_objects[objRank].bboxMin.z = Math::Min(vertices[i].coord.z, m_objects[objRank].bboxMin.z);
+ m_objects[objRank].bboxMax.x = Math::Max(vertices[i].coord.x, m_objects[objRank].bboxMax.x);
+ m_objects[objRank].bboxMax.y = Math::Max(vertices[i].coord.y, m_objects[objRank].bboxMax.y);
+ m_objects[objRank].bboxMax.z = Math::Max(vertices[i].coord.z, m_objects[objRank].bboxMax.z);
+ }
+
+ m_objects[objRank].radius = Math::Max(m_objects[objRank].bboxMin.Length(),
+ m_objects[objRank].bboxMax.Length());
+ }
+
+ m_objects[objRank].totalTriangles += vertices.size() / 3;
+
+ return true;
+}
+
+bool Gfx::CEngine::AddSurface(int objRank, const std::vector<Gfx::VertexTex2>& vertices,
+ const Gfx::Material& material, int state,
+ std::string tex1Name, std::string tex2Name,
+ float min, float max, bool globalUpdate)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ {
+ GetLogger()->Error("AddSurface(): invalid object rank %d\n", objRank);
+ return false;
+ }
+
+ m_lastSize = m_size;
+ m_lastObjectDetail = m_objectDetail;
+ m_lastClippingDistance = m_clippingDistance;
+
+ Gfx::EngineObjLevel1& p1 = AddLevel1(tex1Name, tex2Name);
+ Gfx::EngineObjLevel2& p2 = AddLevel2(p1, objRank);
+ Gfx::EngineObjLevel3& p3 = AddLevel3(p2, min, max);
+ Gfx::EngineObjLevel4& p4 = AddLevel4(p3, Gfx::ENG_TRIANGLE_TYPE_SURFACE, material, state);
+
+ p4.vertices.insert(p4.vertices.end(), vertices.begin(), vertices.end());
+
+ if (globalUpdate)
+ {
+ m_updateGeometry = true;
+ }
+ else
+ {
+ for (int i = 0; i < static_cast<int>( vertices.size() ); i++)
+ {
+ m_objects[objRank].bboxMin.x = Math::Min(vertices[i].coord.x, m_objects[objRank].bboxMin.x);
+ m_objects[objRank].bboxMin.y = Math::Min(vertices[i].coord.y, m_objects[objRank].bboxMin.y);
+ m_objects[objRank].bboxMin.z = Math::Min(vertices[i].coord.z, m_objects[objRank].bboxMin.z);
+ m_objects[objRank].bboxMax.x = Math::Max(vertices[i].coord.x, m_objects[objRank].bboxMax.x);
+ m_objects[objRank].bboxMax.y = Math::Max(vertices[i].coord.y, m_objects[objRank].bboxMax.y);
+ m_objects[objRank].bboxMax.z = Math::Max(vertices[i].coord.z, m_objects[objRank].bboxMax.z);
+ }
+
+ m_objects[objRank].radius = Math::Max(m_objects[objRank].bboxMin.Length(),
+ m_objects[objRank].bboxMax.Length());
+ }
+
+ m_objects[objRank].totalTriangles += vertices.size() - 2;
+
+ return true;
+}
+
+bool Gfx::CEngine::AddQuick(int objRank, const Gfx::EngineObjLevel4& buffer,
+ std::string tex1Name, std::string tex2Name,
+ float min, float max, bool globalUpdate)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ {
+ GetLogger()->Error("AddQuick(): invalid object rank %d\n", objRank);
+ return false;
+ }
+
+ Gfx::EngineObjLevel1& p1 = AddLevel1(tex1Name, tex2Name);
+ Gfx::EngineObjLevel2& p2 = AddLevel2(p1, objRank);
+ Gfx::EngineObjLevel3& p3 = AddLevel3(p2, min, max);
+
+ p3.next.push_back(buffer);
+ p3.next.back().used = true; // ensure that it is used
+
+ if (globalUpdate)
+ {
+ m_updateGeometry = true;
+ }
+ else
+ {
+ for (int i = 0; i < static_cast<int>( buffer.vertices.size() ); i++)
+ {
+ m_objects[objRank].bboxMin.x = Math::Min(buffer.vertices[i].coord.x, m_objects[objRank].bboxMin.x);
+ m_objects[objRank].bboxMin.y = Math::Min(buffer.vertices[i].coord.y, m_objects[objRank].bboxMin.y);
+ m_objects[objRank].bboxMin.z = Math::Min(buffer.vertices[i].coord.z, m_objects[objRank].bboxMin.z);
+ m_objects[objRank].bboxMax.x = Math::Max(buffer.vertices[i].coord.x, m_objects[objRank].bboxMax.x);
+ m_objects[objRank].bboxMax.y = Math::Max(buffer.vertices[i].coord.y, m_objects[objRank].bboxMax.y);
+ m_objects[objRank].bboxMax.z = Math::Max(buffer.vertices[i].coord.z, m_objects[objRank].bboxMax.z);
+ }
+
+ m_objects[objRank].radius = Math::Max(m_objects[objRank].bboxMin.Length(),
+ m_objects[objRank].bboxMax.Length());
+ }
+
+ if (buffer.type == Gfx::ENG_TRIANGLE_TYPE_TRIANGLES)
+ m_objects[objRank].totalTriangles += buffer.vertices.size() / 3;
+ else // surfaces
+ m_objects[objRank].totalTriangles += buffer.vertices.size() - 2;
+
+ return true;
+}
+
+Gfx::EngineObjLevel4* Gfx::CEngine::FindTriangles(int objRank, const Gfx::Material& material,
+ int state, std::string tex1Name,
+ std::string tex2Name, float min, float max)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ {
+ GetLogger()->Error("FindTriangles(): invalid object rank %d\n", objRank);
+ return nullptr;
+ }
+
+ for (int l1 = 0; l1 < static_cast<int>( m_objectTree.size() ); l1++)
+ {
+ Gfx::EngineObjLevel1& p1 = m_objectTree[l1];
+ if (! p1.used) continue;
+
+ if (p1.tex1Name != tex1Name) continue;
+ // TODO: tex2Name compare?
+
+ for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
+ {
+ Gfx::EngineObjLevel2& p2 = p1.next[l2];
+ if (! p2.used) continue;
+
+ if (p2.objRank != objRank) continue;
+
+ for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
+ {
+ Gfx::EngineObjLevel3& p3 = p2.next[l1];
+ if (! p3.used) continue;
+
+ if (p3.min != min || p3.max != max) continue;
+
+ for (int l4 = 0; l4 < static_cast<int>( p3.next.size() ); l4++)
+ {
+ Gfx::EngineObjLevel4& p4 = p3.next[l4];
+ if (! p4.used) continue;
+
+ if ( (p4.state & (~(Gfx::ENG_RSTATE_DUAL_BLACK|Gfx::ENG_RSTATE_DUAL_WHITE))) != state ||
+ p4.material != material )
+ continue;
+
+ return &p4;
+ }
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+int Gfx::CEngine::GetPartialTriangles(int objRank, float min, float max, float percent, int maxCount,
+ std::vector<Gfx::EngineTriangle>& triangles)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ {
+ GetLogger()->Error("GetPartialTriangles(): invalid object rank %d\n", objRank);
+ return 0;
+ }
+
+ int total = m_objects[objRank].totalTriangles;
+ int expectedCount = static_cast<int>(percent * total);
+ triangles.reserve(Math::Min(maxCount, expectedCount));
+
+ int actualCount = 0;
+
+ for (int l1 = 0; l1 < static_cast<int>( m_objectTree.size() ); l1++)
+ {
+ Gfx::EngineObjLevel1& p1 = m_objectTree[l1];
+ if (! p1.used) continue;
+
+ for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
+ {
+ Gfx::EngineObjLevel2& p2 = p1.next[l2];
+ if (! p2.used) continue;
+
+ if (p2.objRank != objRank) continue;
+
+ for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
+ {
+ Gfx::EngineObjLevel3& p3 = p2.next[l1];
+ if (! p3.used) continue;
+
+ if (p3.min != min || p3.max != max) continue;
+
+ for (int l4 = 0; l4 < static_cast<int>( p3.next.size() ); l4++)
+ {
+ Gfx::EngineObjLevel4& p4 = p3.next[l4];
+ if (! p4.used) continue;
+
+ if (p4.type == Gfx::ENG_TRIANGLE_TYPE_TRIANGLES)
+ {
+ for (int i = 0; i < static_cast<int>( p4.vertices.size() ); i += 3)
+ {
+ if (static_cast<float>(actualCount) / total >= percent)
+ break;
+
+ if (actualCount >= maxCount)
+ break;
+
+ Gfx::EngineTriangle t;
+ t.triangle[0] = p4.vertices[i];
+ t.triangle[1] = p4.vertices[i+1];
+ t.triangle[2] = p4.vertices[i+2];
+ t.material = p4.material;
+ t.state = p4.state;
+ t.tex1Name = p1.tex1Name;
+ t.tex2Name = p1.tex2Name;
+
+ triangles.push_back(t);
+
+ ++actualCount;
+ }
+ }
+ else if (p4.type == Gfx::ENG_TRIANGLE_TYPE_SURFACE)
+ {
+ for (int i = 0; i < static_cast<int>( p4.vertices.size() ); i += 1)
+ {
+ if (static_cast<float>(actualCount) / total >= percent)
+ break;
+
+ if (actualCount >= maxCount)
+ break;
+
+ Gfx::EngineTriangle t;
+ t.triangle[0] = p4.vertices[i];
+ t.triangle[1] = p4.vertices[i+1];
+ t.triangle[2] = p4.vertices[i+2];
+ t.material = p4.material;
+ t.state = p4.state;
+ t.tex1Name = p1.tex1Name;
+ t.tex2Name = p1.tex2Name;
+
+ triangles.push_back(t);
+
+ ++actualCount;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return actualCount;
+}
+
+void Gfx::CEngine::ChangeLOD()
+{
+ float oldLimit[2] =
+ {
+ GetLimitLOD(0, true),
+ GetLimitLOD(1, true)
+ };
+
+ float newLimit[2] =
+ {
+ GetLimitLOD(0, false),
+ GetLimitLOD(1, false)
+ };
+
+ float oldTerrain = m_terrainVision * m_lastClippingDistance;
+ float newTerrain = m_terrainVision * m_clippingDistance;
+
+ for (int l1 = 0; l1 < static_cast<int>( m_objectTree.size() ); l1++)
+ {
+ Gfx::EngineObjLevel1& p1 = m_objectTree[l1];
+ if (! p1.used) continue;
+
+ for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
+ {
+ Gfx::EngineObjLevel2& p2 = p1.next[l2];
+ if (! p2.used) continue;
+
+ for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
+ {
+ Gfx::EngineObjLevel3& p3 = p2.next[l1];
+ if (! p3.used) continue;
+
+ if ( Math::IsEqual(p3.min, 0.0f ) &&
+ Math::IsEqual(p3.max, oldLimit[0]) )
+ {
+ p3.max = newLimit[0];
+ }
+ else if ( Math::IsEqual(p3.min, oldLimit[0]) &&
+ Math::IsEqual(p3.max, oldLimit[1]) )
+ {
+ p3.min = newLimit[0];
+ p3.max = newLimit[1];
+ }
+ else if ( Math::IsEqual(p3.min, oldLimit[1]) &&
+ Math::IsEqual(p3.max, 1000000.0f ) )
+ {
+ p3.min = newLimit[1];
+ }
+ else if ( Math::IsEqual(p3.min, 0.0f ) &&
+ Math::IsEqual(p3.max, oldTerrain) )
+ {
+ p3.max = newTerrain;
+ }
+ }
+ }
+ }
+
+ m_lastSize = m_size;
+ m_lastObjectDetail = m_objectDetail;
+ m_lastClippingDistance = m_clippingDistance;
+}
+
+bool Gfx::CEngine::ChangeSecondTexture(int objRank, const std::string& tex2Name)
+{
+ for (int l1 = 0; l1 < static_cast<int>( m_objectTree.size() ); l1++)
+ {
+ Gfx::EngineObjLevel1& p1 = m_objectTree[l1];
+ if (! p1.used) continue;
+
+ if (p1.tex2Name == tex2Name) continue; // already new
+
+ for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
+ {
+ Gfx::EngineObjLevel2& p2 = p1.next[l2];
+ if (! p2.used) continue;
+
+ if (p2.objRank != objRank) continue;
+
+ Gfx::EngineObjLevel1& newP1 = AddLevel1(p1.tex1Name, tex2Name);
+
+ newP1.next.push_back(Gfx::EngineObjLevel2(true, objRank));
+
+ Gfx::EngineObjLevel2& newP2 = newP1.next.back();
+ newP2.next.swap(p2.next);
+
+ p2.used = false;
+ }
+ }
+ return true;
+}
+
+bool Gfx::CEngine::ChangeTextureMapping(int objRank, const Gfx::Material& mat, int state,
+ const std::string& tex1Name, const std::string& tex2Name,
+ float min, float max, Gfx::EngineTextureMapping mode,
+ float au, float bu, float av, float bv)
+{
+ Gfx::EngineObjLevel4* p4 = FindTriangles(objRank, mat, state, tex1Name, tex2Name, min, max);
+ if (p4 == nullptr)
+ return false;
+
+ int nb = p4->vertices.size();
+
+ if (mode == Gfx::ENG_TEX_MAPPING_X)
+ {
+ for (int i = 0; i < nb; i++)
+ {
+ p4->vertices[i].texCoord.x = p4->vertices[i].coord.z * au + bu;
+ p4->vertices[i].texCoord.y = p4->vertices[i].coord.y * av + bv;
+ }
+ }
+ else if (mode == Gfx::ENG_TEX_MAPPING_Y)
+ {
+ for (int i = 0; i < nb; i++)
+ {
+ p4->vertices[i].texCoord.x = p4->vertices[i].coord.x * au + bu;
+ p4->vertices[i].texCoord.y = p4->vertices[i].coord.z * av + bv;
+ }
+ }
+ else if (mode == Gfx::ENG_TEX_MAPPING_Z)
+ {
+ for (int i = 0; i < nb; i++)
+ {
+ p4->vertices[i].texCoord.x = p4->vertices[i].coord.x * au + bu;
+ p4->vertices[i].texCoord.y = p4->vertices[i].coord.y * av + bv;
+ }
+ }
+ else if (mode == Gfx::ENG_TEX_MAPPING_1X)
+ {
+ for (int i = 0; i < nb; i++)
+ {
+ p4->vertices[i].texCoord.x = p4->vertices[i].coord.x * au + bu;
+ }
+ }
+ else if (mode == Gfx::ENG_TEX_MAPPING_1Y)
+ {
+ for (int i = 0; i < nb; i++)
+ {
+ p4->vertices[i].texCoord.y = p4->vertices[i].coord.y * au + bu;
+ }
+ }
+ else if (mode == Gfx::ENG_TEX_MAPPING_1Z)
+ {
+ for (int i = 0; i < nb; i++)
+ {
+ p4->vertices[i].texCoord.x = p4->vertices[i].coord.z * au + bu;
+ }
+ }
+
+ return true;
+}
+
+bool Gfx::CEngine::TrackTextureMapping(int objRank, const Gfx::Material& mat, int state,
+ const std::string& tex1Name, const std::string& tex2Name,
+ float min, float max, Gfx::EngineTextureMapping mode,
+ float pos, float factor, float tl, float ts, float tt)
+{
+ // TODO track texture mapping: pretty complex code, so leaving it for now
+ GetLogger()->Trace("CEngine::TrackTextureMapping(): stub!\n");
+ return true;
+}
+
+
+bool Gfx::CEngine::CreateShadow(int objRank)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ // Already allocated?
+ if (m_objects[objRank].shadowRank != -1) return true;
+
+ int index = 0;
+ for ( ; index < static_cast<int>( m_shadows.size() ); index++)
+ {
+ if (! m_shadows[index].used)
+ {
+ m_shadows[index].LoadDefault();
+ break;
+ }
+ }
+
+ m_shadows.push_back(Gfx::EngineShadow());
+
+ m_shadows[index].used = true;
+ m_shadows[index].objRank = objRank;
+ m_shadows[index].height = 0.0f;
+
+ m_objects[objRank].shadowRank = index;
+
+ return true;
+}
+
+void Gfx::CEngine::DeleteShadow(int objRank)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
return;
- std::map<Gfx::Texture, std::string>::iterator revIt = m_revTexNameMap.find((*it).second);
+ int i = m_objects[objRank].shadowRank;
+ if (i == -1)
+ return;
- m_device->DestroyTexture((*it).second);
+ m_shadows[i].used = false;
+ m_shadows[i].objRank = -1;
- m_revTexNameMap.erase(revIt);
- m_texNameMap.erase(it);
+ m_objects[objRank].shadowRank = -1;
}
-void Gfx::CEngine::SetTexture(const std::string &name, int stage)
+bool Gfx::CEngine::SetObjectShadowHide(int objRank, bool hide)
{
- std::map<std::string, Gfx::Texture>::iterator it = m_texNameMap.find(name);
- if (it != m_texNameMap.end())
- m_device->SetTexture(stage, (*it).second);
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ int i = m_objects[objRank].shadowRank;
+ if (i == -1)
+ return false;
- // TODO if not present...
+ m_shadows[i].hide = hide;
+ return true;
}
-void Gfx::CEngine::SetMaterial(const Gfx::Material &mat)
+bool Gfx::CEngine::SetObjectShadowType(int objRank, Gfx::EngineShadowType type)
{
- m_device->SetMaterial(mat);
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ int i = m_objects[objRank].shadowRank;
+ if (i == -1)
+ return false;
+
+ m_shadows[i].type = type;
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectShadowPos(int objRank, const Math::Vector& pos)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ int i = m_objects[objRank].shadowRank;
+ if (i == -1)
+ return false;
+
+ m_shadows[i].pos = pos;
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectShadowNormal(int objRank, const Math::Vector& normal)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ int i = m_objects[objRank].shadowRank;
+ if (i == -1)
+ return false;
+
+ m_shadows[i].normal = normal;
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectShadowAngle(int objRank, float angle)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ int i = m_objects[objRank].shadowRank;
+ if (i == -1)
+ return false;
+
+ m_shadows[i].angle = angle;
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectShadowRadius(int objRank, float radius)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ int i = m_objects[objRank].shadowRank;
+ if (i == -1)
+ return false;
+
+ m_shadows[i].radius = radius;
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectShadowIntensity(int objRank, float intensity)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ int i = m_objects[objRank].shadowRank;
+ if (i == -1)
+ return false;
+
+ m_shadows[i].intensity = intensity;
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectShadowHeight(int objRank, float height)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return false;
+
+ int i = m_objects[objRank].shadowRank;
+ if (i == -1)
+ return false;
+
+ m_shadows[i].height = height;
+ return true;
+}
+
+float Gfx::CEngine::GetObjectShadowRadius(int objRank)
+{
+ if ( objRank < 0 || objRank >= static_cast<int>( m_objects.size() ) )
+ return 0.0f;
+
+ int i = m_objects[objRank].shadowRank;
+ if (i == -1)
+ return 0.0f;
+
+ return m_shadows[i].radius;
+}
+
+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 )
+ {
+ m_highlightRank[i++] = *rankList++;
+ }
+ m_highlightRank[i] = -1; // terminator
+}
+
+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;
+
+ for (int i = 0; i < 8; i++)
+ {
+ 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;
+ }
+ }
+
+ if ( min.x == 1000000.0f ||
+ min.y == 1000000.0f ||
+ max.x == -1000000.0f ||
+ max.y == -1000000.0f ) return false;
+
+ return true;
+}
+
+void Gfx::CEngine::FlushGroundSpot()
+{
+ m_groundSpots.clear();
+ m_firstGroundSpot = true;
+
+ // TODO: blank all shadow textures
}
-void Gfx::CEngine::SetState(int state, Gfx::Color color)
+int Gfx::CEngine::CreateGroundSpot()
{
- if ( state == m_lastState && color == m_lastColor )
+ int index = 0;
+ for ( ; index < static_cast<int>( m_groundSpots.size() ); index++)
+ {
+ if (! m_groundSpots[index].used)
+ {
+ m_groundSpots[index].LoadDefault();
+ break;
+ }
+ }
+
+ m_groundSpots.push_back(Gfx::EngineGroundSpot());
+
+ m_groundSpots[index].used = true;
+ m_groundSpots[index].smooth = 1.0f;
+
+ return index;
+}
+
+void Gfx::CEngine::DeleteGroundSpot(int rank)
+{
+ m_groundSpots[rank].used = false;
+ m_groundSpots[rank].pos = Math::Vector(0.0f, 0.0f, 0.0f);
+}
+
+bool Gfx::CEngine::SetObjectGroundSpotPos(int rank, const Math::Vector& pos)
+{
+ if ( rank < 0 || rank >= static_cast<int>( m_groundSpots.size() ) )
+ return 0.0f;
+
+ m_groundSpots[rank].pos = pos;
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectGroundSpotRadius(int rank, float radius)
+{
+ if ( rank < 0 || rank >= static_cast<int>( m_groundSpots.size() ) )
+ return 0.0f;
+
+ m_groundSpots[rank].radius = radius;
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectGroundSpotColor(int rank, const Gfx::Color& color)
+{
+ if ( rank < 0 || rank >= static_cast<int>( m_groundSpots.size() ) )
+ return 0.0f;
+
+ m_groundSpots[rank].color = color;
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectGroundSpotMinMax(int rank, float min, float max)
+{
+ if ( rank < 0 || rank >= static_cast<int>( m_groundSpots.size() ) )
+ return 0.0f;
+
+ m_groundSpots[rank].min = min;
+ m_groundSpots[rank].max = max;
+ return true;
+}
+
+bool Gfx::CEngine::SetObjectGroundSpotSmooth(int rank, float smooth)
+{
+ if ( rank < 0 || rank >= static_cast<int>( m_groundSpots.size() ) )
+ return 0.0f;
+
+ m_groundSpots[rank].smooth = smooth;
+ return true;
+}
+
+void Gfx::CEngine::CreateGroundMark(Math::Vector pos, float radius,
+ float delay1, float delay2, float delay3,
+ int dx, int dy, char* table)
+{
+ m_groundMark.LoadDefault();
+
+ m_groundMark.phase = Gfx::ENG_GR_MARK_PHASE_INC;
+ m_groundMark.delay[0] = delay1;
+ m_groundMark.delay[1] = delay2;
+ m_groundMark.delay[2] = delay3;
+ m_groundMark.pos = pos;
+ m_groundMark.radius = radius;
+ m_groundMark.intensity = 0.0f;
+ m_groundMark.dx = dx;
+ m_groundMark.dy = dy;
+ m_groundMark.table = table;
+}
+
+void Gfx::CEngine::DeleteGroundMark(int rank)
+{
+ m_groundMark.LoadDefault();
+}
+
+void Gfx::CEngine::ComputeDistance()
+{
+ // TODO: s_resol???
+
+ for (int i = 0; i < static_cast<int>( m_objects.size() ); i++)
+ {
+ if (! m_objects[i].used)
+ continue;
+
+ Math::Vector v;
+ v.x = m_eyePt.x - m_objects[i].transform.Get(1, 4);
+ v.y = m_eyePt.y - m_objects[i].transform.Get(2, 4);
+ v.z = m_eyePt.z - m_objects[i].transform.Get(3, 4);
+ m_objects[i].distance = v.Length();
+ }
+}
+
+void Gfx::CEngine::UpdateGeometry()
+{
+ if (! m_updateGeometry)
+ return;
+
+ for (int i = 0; i < static_cast<int>( m_objects.size() ); i++)
+ {
+ m_objects[i].bboxMin.x = 0;
+ m_objects[i].bboxMin.y = 0;
+ m_objects[i].bboxMin.z = 0;
+ m_objects[i].bboxMax.x = 0;
+ m_objects[i].bboxMax.y = 0;
+ m_objects[i].bboxMax.z = 0;
+ m_objects[i].radius = 0;
+ }
+
+ for (int l1 = 0; l1 < static_cast<int>( m_objectTree.size() ); l1++)
+ {
+ Gfx::EngineObjLevel1& p1 = m_objectTree[l1];
+ if (! p1.used) continue;
+
+ for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
+ {
+ Gfx::EngineObjLevel2& p2 = p1.next[l2];
+ if (! p2.used) continue;
+
+ for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
+ {
+ Gfx::EngineObjLevel3& p3 = p2.next[l1];
+ if (! p3.used) continue;
+
+ for (int l4 = 0; l4 < static_cast<int>( p3.next.size() ); l4++)
+ {
+ Gfx::EngineObjLevel4& p4 = p3.next[l4];
+ if (! p4.used) continue;
+
+ int objRank = p2.objRank;
+
+ for (int i = 0; i < static_cast<int>( p4.vertices.size() ); i++)
+ {
+ m_objects[objRank].bboxMin.x = Math::Min(p4.vertices[i].coord.x, m_objects[objRank].bboxMin.x);
+ m_objects[objRank].bboxMin.y = Math::Min(p4.vertices[i].coord.y, m_objects[objRank].bboxMin.y);
+ m_objects[objRank].bboxMin.z = Math::Min(p4.vertices[i].coord.z, m_objects[objRank].bboxMin.z);
+ m_objects[objRank].bboxMax.x = Math::Max(p4.vertices[i].coord.x, m_objects[objRank].bboxMax.x);
+ m_objects[objRank].bboxMax.y = Math::Max(p4.vertices[i].coord.y, m_objects[objRank].bboxMax.y);
+ m_objects[objRank].bboxMax.z = Math::Max(p4.vertices[i].coord.z, m_objects[objRank].bboxMax.z);
+ }
+
+ m_objects[objRank].radius = Math::Max(m_objects[objRank].bboxMin.Length(),
+ m_objects[objRank].bboxMax.Length());
+ }
+ }
+ }
+ }
+
+ m_updateGeometry = false;
+}
+
+void Gfx::CEngine::Update()
+{
+ ComputeDistance();
+ UpdateGeometry();
+}
+
+bool Gfx::CEngine::DetectBBox(int objRank, Math::Point mouse)
+{
+ Math::Point min, max;
+ min.x = 1000000.0f;
+ min.y = 1000000.0f;
+ max.x = -1000000.0f;
+ max.y = -1000000.0f;
+
+ for (int i = 0; i < 8; i++)
+ {
+ 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;
+ }
+ }
+
+ return ( mouse.x >= min.x &&
+ mouse.x <= max.x &&
+ mouse.y >= min.y &&
+ mouse.y <= max.y );
+}
+
+int Gfx::CEngine::DetectObject(Math::Point mouse)
+{
+ float min = 1000000.0f;
+ int nearest = -1;
+
+ for (int l1 = 0; l1 < static_cast<int>( m_objectTree.size() ); l1++)
+ {
+ Gfx::EngineObjLevel1& p1 = m_objectTree[l1];
+ if (! p1.used) continue;
+
+ for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
+ {
+ Gfx::EngineObjLevel2& p2 = p1.next[l2];
+ if (! p2.used) continue;
+
+ if (m_objects[p2.objRank].type == Gfx::ENG_OBJTYPE_TERRAIN) continue;
+
+ if (! DetectBBox(p2.objRank, mouse)) continue;
+
+ for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
+ {
+ Gfx::EngineObjLevel3& p3 = p2.next[l1];
+ if (! p3.used) continue;
+
+ if (p3.min != 0.0f) continue; // LOD B or C?
+
+ for (int l4 = 0; l4 < static_cast<int>( p3.next.size() ); l4++)
+ {
+ Gfx::EngineObjLevel4& p4 = p3.next[l4];
+ if (! p4.used) continue;
+
+ if (p4.type == Gfx::ENG_TRIANGLE_TYPE_TRIANGLES)
+ {
+ for (int i = 0; i < static_cast<int>( p4.vertices.size() ); i += 3)
+ {
+ float dist = 0.0f;
+ if (DetectTriangle(mouse, &p4.vertices[i], p2.objRank, dist) && dist < min)
+ {
+ min = dist;
+ nearest = p2.objRank;
+ }
+ }
+ }
+ else if (p4.type == Gfx::ENG_TRIANGLE_TYPE_SURFACE)
+ {
+ for (int i = 0; i < static_cast<int>( p4.vertices.size() ) - 2; i += 1)
+ {
+ float dist = 0.0f;
+ if (DetectTriangle(mouse, &p4.vertices[i], p2.objRank, dist) && dist < min)
+ {
+ min = dist;
+ nearest = p2.objRank;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return nearest;
+}
+
+bool Gfx::CEngine::DetectTriangle(Math::Point mouse, Gfx::VertexTex2* triangle, int objRank, float& dist)
+{
+ Math::Vector p2D[3], p3D;
+
+ for (int i = 0; i < 3; i++)
+ {
+ p3D.x = triangle[i].coord.x;
+ p3D.y = triangle[i].coord.y;
+ p3D.z = triangle[i].coord.z;
+
+ if (! TransformPoint(p2D[i], objRank, p3D))
+ return false;
+ }
+
+ if ( mouse.x < p2D[0].x &&
+ mouse.x < p2D[1].x &&
+ mouse.x < p2D[2].x ) return false;
+ if ( mouse.x > p2D[0].x &&
+ mouse.x > p2D[1].x &&
+ mouse.x > p2D[2].x ) return false;
+ if ( mouse.y < p2D[0].y &&
+ mouse.y < p2D[1].y &&
+ mouse.y < p2D[2].y ) return false;
+ if ( mouse.y > p2D[0].y &&
+ mouse.y > p2D[1].y &&
+ mouse.y > p2D[2].y ) return false;
+
+ Math::Point a, b, c;
+ a.x = p2D[0].x;
+ a.y = p2D[0].y;
+ b.x = p2D[1].x;
+ b.y = p2D[1].y;
+ c.x = p2D[2].x;
+ c.y = p2D[2].y;
+
+ if (! Math::IsInsideTriangle(a, b, c, mouse))
+ return false;
+
+ dist = (p2D[0].z + p2D[1].z + p2D[2].z) / 3.0f;
+ return true;
+}
+
+bool Gfx::CEngine::IsVisible(int objRank)
+{
+ // TODO: use ComputeSphereVisiblity() after tested OK
+ 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 +1843,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->SetRenderState(Gfx::RENDER_STATE_DEPTH_WRITE, true);
+ m_device->SetRenderState(Gfx::RENDER_STATE_FOG, true);
- /*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_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 +2016,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 +2043,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,171 +2080,668 @@ 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 tex = m_device->CreateTexture(&img, params);
+
+ if (! tex.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 tex;
+ }
- if (event.key.key == KEY(F1))
+ m_texNameMap[texName] = tex;
+ m_revTexNameMap[tex] = texName;
+
+ return tex;
+}
+
+Gfx::Texture Gfx::CEngine::LoadTexture(const std::string& name)
+{
+ return LoadTexture(name, m_defaultTexParams);
+}
+
+Gfx::Texture Gfx::CEngine::LoadTexture(const std::string& name, const Gfx::TextureCreateParams& params)
+{
+ if (m_texBlacklist.find(name) != m_texBlacklist.end())
+ return Gfx::Texture();
+
+ std::map<std::string, Gfx::Texture>::iterator it = m_texNameMap.find(name);
+ if (it != m_texNameMap.end())
+ return (*it).second;
+
+ Gfx::Texture tex = CreateTexture(name, params);
+ return tex;
+}
+
+bool Gfx::CEngine::LoadAllTextures()
+{
+ LoadTexture("text.png");
+ m_miceTexture = 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_backgroundQuarter) // image into 4 pieces?
+ {
+ if (! m_backgroundName.empty())
{
- m_mouseVisible = !m_mouseVisible;
- m_app->SetSystemMouseVisible(! m_app->GetSystemMouseVisibile());
+ for (int i = 0; i < 4; i++)
+ m_backgroundQuarterTexs[i] = LoadTexture(m_backgroundQuarterNames[i]);
}
- 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 );
+ for (int i = 0; i < 4; i++)
+ m_backgroundQuarterTexs[i].SetInvalid();
}
}
+ else
+ {
+ if (! m_backgroundName.empty())
+ m_backgroundFullTex = LoadTexture(m_backgroundName);
+ else
+ m_backgroundFullTex.SetInvalid();
+ }
- // By default, pass on all events
- return true;
+ if (! m_foregroundName.empty())
+ m_foregroundTex = LoadTexture(m_foregroundName);
+ else
+ m_foregroundTex.SetInvalid();
+
+ m_planet->LoadTexture();
+
+ bool ok = true;
+
+ for (int l1 = 0; l1 < static_cast<int>( m_objectTree.size() ); l1++)
+ {
+ Gfx::EngineObjLevel1& p1 = m_objectTree[l1];
+ if (! p1.used) continue;
+
+ if (! p1.tex1Name.empty())
+ {
+ if (! LoadTexture(p1.tex1Name).Valid())
+ ok = false;
+ }
+
+ if (! p1.tex2Name.empty())
+ {
+ if (! LoadTexture(p1.tex2Name).Valid())
+ ok = false;
+ }
+ }
+
+ return ok;
}
-bool Gfx::CEngine::Render()
+void Gfx::CEngine::DeleteTexture(const std::string& texName)
{
- m_statisticTriangle = 0;
+ auto it = m_texNameMap.find(texName);
+ if (it == m_texNameMap.end())
+ return;
- m_lastState = -1;
- SetState(Gfx::ENG_RSTATE_NORMAL);
+ auto revIt = m_revTexNameMap.find((*it).second);
- m_device->BeginScene();
+ m_device->DestroyTexture((*it).second);
+
+ m_revTexNameMap.erase(revIt);
+ m_texNameMap.erase(it);
+}
+
+void Gfx::CEngine::DeleteTexture(const Gfx::Texture& tex)
+{
+ if (! tex.Valid())
+ return;
- SetUp3DView();
+ auto revIt = m_revTexNameMap.find(tex);
+ if (revIt == m_revTexNameMap.end())
+ return;
- if (! Draw3DScene() )
- return false;
+ m_device->DestroyTexture(tex);
- SetUpInterfaceView();
+ auto it = m_texNameMap.find((*revIt).second);
- if (! DrawInterface() )
+ m_revTexNameMap.erase(revIt);
+ m_texNameMap.erase(it);
+}
+
+bool Gfx::CEngine::SetTexture(const std::string& name, int stage)
+{
+ auto it = m_texNameMap.find(name);
+ if (it != m_texNameMap.end())
+ {
+ m_device->SetTexture(stage, (*it).second);
+ return true;
+ }
+
+ if (! LoadTexture(name).Valid())
+ {
+ m_device->SetTexture(stage, 0); // invalid texture
return false;
+ }
- m_device->EndScene();
+ it = m_texNameMap.find(name);
+ if (it != m_texNameMap.end())
+ {
+ m_device->SetTexture(stage, (*it).second);
+ return true;
+ }
- return true;
+ m_device->SetTexture(stage, 0); // invalid texture
+ return false; // should not happen normally
}
-void Gfx::CEngine::SetUp3DView()
+void Gfx::CEngine::SetTexture(const Gfx::Texture& tex, int stage)
{
- // TODO
+ m_device->SetTexture(stage, tex);
}
-bool Gfx::CEngine::Draw3DScene()
+void Gfx::CEngine::SetLimitLOD(int rank, float limit)
{
- // TODO
- return true;
+ m_limitLOD[rank] = limit;
}
-void Gfx::CEngine::SetUpInterfaceView()
+float Gfx::CEngine::GetLimitLOD(int rank, bool last)
{
- m_device->SetTransform(Gfx::TRANSFORM_WORLD, m_matWorldInterface);
- m_device->SetTransform(Gfx::TRANSFORM_VIEW, m_matViewInterface);
- m_device->SetTransform(Gfx::TRANSFORM_PROJECTION, m_matProjInterface);
+ 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;
}
-bool Gfx::CEngine::DrawInterface()
+void Gfx::CEngine::SetShadow(bool mode)
{
- Gfx::VertexCol vertices[3] =
+ 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];
+}
+
+std::string QuarterName(const std::string& name, int quarter)
+{
+ size_t pos = name.find('.');
+ if (pos == std::string::npos)
+ return name;
+
+ return name.substr(0, pos) + std::string(1, static_cast<char>('a' + quarter)) + name.substr(pos);
+}
+
+void Gfx::CEngine::SetBackground(const std::string& name, Gfx::Color up, Gfx::Color down,
+ Gfx::Color cloudUp, Gfx::Color cloudDown,
+ bool full, bool quarter)
+{
+ if (m_backgroundFullTex.Valid())
{
- 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))
- };
+ DeleteTexture(m_backgroundFullTex);
+ m_backgroundFullTex.SetInvalid();
+ }
- m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLES, vertices, 3);
+ for (int i = 0; i < 4; i++)
+ {
+ DeleteTexture(m_backgroundQuarterTexs[i]);
+ m_backgroundQuarterTexs[i].SetInvalid();
+ }
- DrawMouse();
+ m_backgroundName = name;
+ m_backgroundColorUp = up;
+ m_backgroundColorDown = down;
+ m_backgroundCloudUp = cloudUp;
+ m_backgroundCloudDown = cloudDown;
+ m_backgroundFull = full;
+ m_backgroundQuarter = quarter;
- return true;
+ if (! m_backgroundName.empty())
+ {
+ if (m_backgroundQuarter)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ m_backgroundQuarterNames[i] = QuarterName(name, i);
+ m_backgroundQuarterTexs[i] = LoadTexture(m_backgroundQuarterNames[i]);
+ }
+ }
+ else
+ {
+ m_backgroundFullTex = LoadTexture(m_backgroundName);
+ }
+ }
}
-void Gfx::CEngine::DrawMouse()
+void Gfx::CEngine::GetBackground(std::string& name, Gfx::Color& up, Gfx::Color& down,
+ Gfx::Color& cloudUp, Gfx::Color& cloudDown,
+ bool &full, bool &quarter)
{
- if (! m_mouseVisible)
- return;
+ name = m_backgroundName;
+ up = m_backgroundColorUp;
+ down = m_backgroundColorDown;
+ cloudUp = m_backgroundCloudUp;
+ cloudDown = m_backgroundCloudDown;
+ full = m_backgroundFull;
+ quarter = m_backgroundQuarter;
+}
- if (m_app->GetSystemMouseVisibile())
- return;
+void Gfx::CEngine::SetForegroundName(const std::string& name)
+{
+ if (m_foregroundTex.Valid())
+ {
+ DeleteTexture(m_foregroundTex);
+ m_foregroundTex.SetInvalid();
+ }
- Gfx::Material material;
- material.diffuse = Gfx::Color(1.0f, 1.0f, 1.0f);
- material.ambient = Gfx::Color(0.5f, 0.5f, 0.5f);
+ m_foregroundName = name;
- m_device->SetMaterial(material);
- m_device->SetTexture(0, m_miceTexture);
+ if (! m_foregroundName.empty())
+ m_foregroundTex = LoadTexture(m_foregroundName);
+}
- int index = static_cast<int>(m_mouseType);
+void Gfx::CEngine::SetOverFront(bool front)
+{
+ m_overFront = front;
+}
- Math::Point pos = m_mousePos;
- pos.x = m_mousePos.x - (m_mice[index].hotPoint.x * m_mouseSize.x) / 32.0f;
- pos.y = m_mousePos.y - ((32.0f - m_mice[index].hotPoint.y) * m_mouseSize.y) / 32.0f;
+void Gfx::CEngine::SetOverColor(const Gfx::Color& color, int mode)
+{
+ m_overColor = color;
+ m_overMode = mode;
+}
- Math::Point shadowPos;
- shadowPos.x = pos.x + (4.0f/800.0f);
- shadowPos.y = pos.y - (3.0f/600.0f);
+void Gfx::CEngine::SetParticleDensity(float value)
+{
+ if (value < 0.0f) value = 0.0f;
+ if (value > 2.0f) value = 2.0f;
+ m_particleDensity = value;
+}
- SetState(Gfx::ENG_RSTATE_TCOLOR_WHITE);
- DrawMouseSprite(shadowPos, m_mouseSize, m_mice[index].iconShadow);
+float Gfx::CEngine::GetParticleDensity()
+{
+ return m_particleDensity;
+}
- SetState(m_mice[index].mode1);
- DrawMouseSprite(pos, m_mouseSize, m_mice[index].icon1);
+float Gfx::CEngine::ParticleAdapt(float factor)
+{
+ if (m_particleDensity == 0.0f)
+ return 1000000.0f;
- SetState(m_mice[index].mode2);
- DrawMouseSprite(pos, m_mouseSize, m_mice[index].icon2);
+ return factor / m_particleDensity;
}
-void Gfx::CEngine::DrawMouseSprite(Math::Point pos, Math::Point size, int icon)
+void Gfx::CEngine::SetClippingDistance(float value)
{
- if (icon == -1)
- return;
+ if (value < 0.5f) value = 0.5f;
+ if (value > 2.0f) value = 2.0f;
+ m_clippingDistance = value;
+}
- Math::Point p1 = pos;
- Math::Point p2 = p1 + size;
+float Gfx::CEngine::GetClippingDistance()
+{
+ return m_clippingDistance;
+}
- float u1 = (32.0f / 256.0f) * (icon % 8);
- float v1 = (32.0f / 256.0f) * (icon / 8);
- float u2 = u1 + (32.0f / 256.0f);
- float v2 = v1 + (32.0f / 256.0f);
+void Gfx::CEngine::SetObjectDetail(float value)
+{
+ if ( value < 0.0f ) value = 0.0f;
+ if ( value > 2.0f ) value = 2.0f;
+ m_objectDetail = value;
+}
- float dp = 0.5f / 256.0f;
- u1 += dp;
- v1 += dp;
- u2 -= dp;
- v2 -= dp;
+float Gfx::CEngine::GetObjectDetail()
+{
+ return m_objectDetail;
+}
- Math::Vector normal(0.0f, 0.0f, -1.0f);
+void Gfx::CEngine::SetGadgetQuantity(float value)
+{
+ if (value < 0.0f) value = 0.0f;
+ if (value > 1.0f) value = 1.0f;
- Gfx::Vertex vertex[4] =
+ 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)
{
- 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, p2.y, 0.0f), normal, Math::Point(u2, v1))
- };
+ m_textureQuality = value;
+ LoadAllTextures();
+ }
+}
- m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, vertex, 4);
- AddStatisticTriangle(2);
+int Gfx::CEngine::GetTextureQuality()
+{
+ return m_textureQuality;
}
-bool Gfx::CEngine::GetPause()
+void Gfx::CEngine::SetTotoMode(bool present)
{
- return m_pause;
+ m_totoMode = present;
}
-Math::Vector Gfx::CEngine::GetLookatPt()
+bool Gfx::CEngine::GetTotoMode()
{
- return m_lookatPt;
+ return m_totoMode;
}
-Math::Vector Gfx::CEngine::GetEyePt()
+void Gfx::CEngine::SetLensMode(bool present)
{
- return m_eyePt;
+ 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)
@@ -726,18 +2774,1080 @@ Gfx::EngineMouseType Gfx::CEngine::GetMouseType()
return m_mouseType;
}
-void Gfx::CEngine::AddStatisticTriangle(int count)
+const Math::Matrix& Gfx::CEngine::GetMatView()
{
- m_statisticTriangle += count;
+ return m_matView;
}
-void Gfx::CEngine::SetShowStat(bool show)
+Math::Vector Gfx::CEngine::GetEyePt()
{
- m_showStats = show;
+ return m_eyePt;
}
-bool Gfx::CEngine::GetShowStat()
+Math::Vector Gfx::CEngine::GetLookatPt()
{
- return m_showStats;
+ 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;
+ m_lastColor = 999;
+ m_lastMaterial = Gfx::Material();
+
+ m_lightMan->UpdateLights();
+
+ Gfx::Color color;
+ if (m_skyMode && m_cloud->GetLevel() != 0.0f) // clouds?
+ color = m_backgroundCloudDown;
+ else
+ color = m_backgroundColorDown;
+
+ m_device->SetClearColor(color);
+
+ // Begin the scene
+ m_device->BeginScene();
+
+ if (m_drawWorld)
+ Draw3DScene();
+
+ DrawInterface();
+
+ // End the scene
+ m_device->EndScene();
+}
+
+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->tex1Name, 0);
+ SetTexture(p2->tex2Name, 1);
+ for ( l2=0 ; l2<p2->totalUsed ; l2++ )
+ {
+ p3 = p2->table[l2];
+ if ( p3 == 0 ) continue;
+ objRank = p3->objRank;
+ if ( m_objects[objRank].type != TYPETERRAIN ) continue;
+ if ( !m_objects[objRank].bDrawWorld ) continue;
+
+ {
+ D3DMATRIX mat = MAT_TO_D3DMAT(m_objects[objRank].transform);
+ m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &mat);
+ }
+
+ if ( !IsVisible(objRank) ) continue;
+ m_light->LightUpdate(m_objects[objRank].type);
+ for ( l3=0 ; l3<p3->totalUsed ; l3++ )
+ {
+ p4 = p3->table[l3];
+ if ( p4 == 0 ) continue;
+ if ( m_objects[objRank].distance < p4->min ||
+ m_objects[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->tex1Name, 0);
+ SetTexture(p2->tex2Name, 1);
+ for ( l2=0 ; l2<p2->totalUsed ; l2++ )
+ {
+ p3 = p2->table[l2];
+ if ( p3 == 0 ) continue;
+ objRank = p3->objRank;
+ if ( m_bShadow && m_objects[objRank].type == TYPETERRAIN ) continue;
+ if ( !m_objects[objRank].bDrawWorld ) continue;
+
+ {
+ D3DMATRIX mat = MAT_TO_D3DMAT(m_objects[objRank].transform);
+ m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &mat);
+ }
+
+ if ( !IsVisible(objRank) ) continue;
+ m_light->LightUpdate(m_objects[objRank].type);
+ for ( l3=0 ; l3<p3->totalUsed ; l3++ )
+ {
+ p4 = p3->table[l3];
+ if ( p4 == 0 ) continue;
+ if ( m_objects[objRank].distance < p4->min ||
+ m_objects[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_objects[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->tex1Name, 0);
+ SetTexture(p2->tex2Name, 1);
+ for ( l2=0 ; l2<p2->totalUsed ; l2++ )
+ {
+ p3 = p2->table[l2];
+ if ( p3 == 0 ) continue;
+ objRank = p3->objRank;
+ if ( m_bShadow && m_objects[objRank].type == TYPETERRAIN ) continue;
+ if ( !m_objects[objRank].bDrawWorld ) continue;
+
+ {
+ D3DMATRIX mat = MAT_TO_D3DMAT(m_objects[objRank].transform);
+ m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &mat);
+ }
+
+ if ( !IsVisible(objRank) ) continue;
+ m_light->LightUpdate(m_objects[objRank].type);
+ for ( l3=0 ; l3<p3->totalUsed ; l3++ )
+ {
+ p4 = p3->table[l3];
+ if ( p4 == 0 ) continue;
+ if ( m_objects[objRank].distance < p4->min ||
+ m_objects[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_objects[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::DrawInterface()
+{
+ 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->tex1Name, 0);
+ SetTexture(p2->tex2Name, 1);
+ for ( l2=0 ; l2<p2->totalUsed ; l2++ )
+ {
+ p3 = p2->table[l2];
+ if ( p3 == 0 ) continue;
+ objRank = p3->objRank;
+ if ( !m_objects[objRank].bDrawFront ) continue;
+
+ {
+ D3DMATRIX mat = MAT_TO_D3DMAT(m_objects[objRank].transform);
+ m_pD3DDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &mat);
+ }
+
+ if ( !IsVisible(objRank) ) continue;
+ m_light->LightUpdate(m_objects[objRank].type);
+ for ( l3=0 ; l3<p3->totalUsed ; l3++ )
+ {
+ p4 = p3->table[l3];
+ if ( p4 == 0 ) continue;
+ if ( m_objects[objRank].distance < p4->min ||
+ m_objects[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();
+}
+
+void Gfx::CEngine::UpdateGroundSpotTextures()
+{
+ // TODO the original code modifying the textures is very complex, so stub for now
+ GetLogger()->Trace("CEngine::UpdateGroundSpotTextures(): stub!\n");
+}
+
+void Gfx::CEngine::DrawShadow()
+{
+ 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);
+}
+
+// Status: PART_TESTED
+void Gfx::CEngine::DrawBackgroundImageQuarter(Math::Point p1, Math::Point p2, const Gfx::Texture &tex)
+{
+ 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
+ {
+ 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(tex);
+ 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_TRIANGLE_STRIP, vertex, 4);
+ AddStatisticTriangle(2);
}
+// Status: TESTED, VERIFIED
+void Gfx::CEngine::DrawBackgroundImage()
+{
+ Math::Point p1, p2;
+ std::string name;
+
+ if (m_backgroundQuarter)
+ {
+ p1.x = 0.0f;
+ p1.y = 0.5f;
+ p2.x = 0.5f;
+ p2.y = 1.0f;
+ DrawBackgroundImageQuarter(p1, p2, m_backgroundQuarterTexs[0]);
+
+ p1.x = 0.5f;
+ p1.y = 0.5f;
+ p2.x = 1.0f;
+ p2.y = 1.0f;
+ DrawBackgroundImageQuarter(p1, p2, m_backgroundQuarterTexs[1]);
+
+ p1.x = 0.0f;
+ p1.y = 0.0f;
+ p2.x = 0.5f;
+ p2.y = 0.5f;
+ DrawBackgroundImageQuarter(p1, p2, m_backgroundQuarterTexs[2]);
+
+ p1.x = 0.5f;
+ p1.y = 0.0f;
+ p2.x = 1.0f;
+ p2.y = 0.5f;
+ DrawBackgroundImageQuarter(p1, p2, m_backgroundQuarterTexs[3]);
+ }
+ else
+ {
+ p1.x = 0.0f;
+ p1.y = 0.0f;
+ p2.x = 1.0f;
+ p2.y = 1.0f;
+ DrawBackgroundImageQuarter(p1, p2, m_backgroundFullTex);
+ }
+}
+
+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_foregroundTex);
+ 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)
+ return;
+
+ if (m_app->GetSystemMouseVisibile())
+ return;
+
+ Gfx::Material material;
+ material.diffuse = Gfx::Color(1.0f, 1.0f, 1.0f);
+ material.ambient = Gfx::Color(0.5f, 0.5f, 0.5f);
+
+ m_device->SetMaterial(material);
+ m_device->SetTexture(0, m_miceTexture);
+
+ int index = static_cast<int>(m_mouseType);
+
+ Math::Point pos = m_mousePos;
+ pos.x = m_mousePos.x - (m_mice[index].hotPoint.x * m_mouseSize.x) / 32.0f;
+ pos.y = m_mousePos.y - ((32.0f - m_mice[index].hotPoint.y) * m_mouseSize.y) / 32.0f;
+
+ Math::Point shadowPos;
+ shadowPos.x = pos.x + (4.0f/800.0f);
+ shadowPos.y = pos.y - (3.0f/600.0f);
+
+ SetState(Gfx::ENG_RSTATE_TCOLOR_WHITE);
+ DrawMouseSprite(shadowPos, m_mouseSize, m_mice[index].iconShadow);
+
+ SetState(m_mice[index].mode1);
+ DrawMouseSprite(pos, m_mouseSize, m_mice[index].icon1);
+
+ SetState(m_mice[index].mode2);
+ 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)
+ return;
+
+ Math::Point p1 = pos;
+ Math::Point p2 = p1 + size;
+
+ float u1 = (32.0f / 256.0f) * (icon % 8);
+ float v1 = (32.0f / 256.0f) * (icon / 8);
+ float u2 = u1 + (32.0f / 256.0f);
+ float v2 = v1 + (32.0f / 256.0f);
+
+ float dp = 0.5f / 256.0f;
+ u1 += dp;
+ v1 += dp;
+ u2 -= dp;
+ v2 -= dp;
+
+ Math::Vector normal(0.0f, 0.0f, -1.0f);
+
+ Gfx::Vertex vertex[4] =
+ {
+ Gfx::Vertex(Math::Vector(p1.x, p1.y, 0.0f), normal, Math::Point(u1, 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);
+}
diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h
index 25c5e5d..3f21e0d 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 {
@@ -57,14 +60,69 @@ class CTerrain;
/**
+ \enum EngineRenderState
+ \brief Render state of graphics engine
+
+ States are used for settings certain modes, for instance texturing and blending.
+ The enum is a bitmask and some of the states can be OR'd together. */
+enum EngineRenderState
+{
+ //! Normal opaque materials
+ ENG_RSTATE_NORMAL = 0,
+ //! The transparent texture (black = no)
+ ENG_RSTATE_TTEXTURE_BLACK = (1<<0),
+ //! The transparent texture (white = no)
+ ENG_RSTATE_TTEXTURE_WHITE = (1<<1),
+ //! The transparent diffuse color
+ ENG_RSTATE_TDIFFUSE = (1<<2),
+ //! Texture wrap
+ ENG_RSTATE_WRAP = (1<<3),
+ //! Texture borders with solid color
+ ENG_RSTATE_CLAMP = (1<<4),
+ //! Light texture (ambient max)
+ ENG_RSTATE_LIGHT = (1<<5),
+ //! Double black texturing
+ ENG_RSTATE_DUAL_BLACK = (1<<6),
+ //! Double white texturing
+ ENG_RSTATE_DUAL_WHITE = (1<<7),
+ //! Part 1 (no change in. MOD!)
+ ENG_RSTATE_PART1 = (1<<8),
+ //! Part 2
+ ENG_RSTATE_PART2 = (1<<9),
+ //! Part 3
+ ENG_RSTATE_PART3 = (1<<10),
+ //! Part 4
+ ENG_RSTATE_PART4 = (1<<11),
+ //! Double-sided face
+ ENG_RSTATE_2FACE = (1<<12),
+ //! Image using alpha channel
+ ENG_RSTATE_ALPHA = (1<<13),
+ //! Always use 2nd floor texturing
+ ENG_RSTATE_SECOND = (1<<14),
+ //! Causes the fog
+ ENG_RSTATE_FOG = (1<<15),
+ //! The transparent color (black = no)
+ ENG_RSTATE_TCOLOR_BLACK = (1<<16),
+ //! The transparent color (white = no)
+ 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)
+};
+
+
+/**
\enum EngineTriangleType
\brief Type of triangles drawn for engine objects */
enum EngineTriangleType
{
//! Triangles
- ENG_TRIANGLE_TYPE_6T = 1,
+ ENG_TRIANGLE_TYPE_TRIANGLES = 1,
//! Surfaces
- ENG_TRIANGLE_TYPE_6S = 2
+ ENG_TRIANGLE_TYPE_SURFACE = 2
};
/**
@@ -76,16 +134,16 @@ struct EngineTriangle
Gfx::VertexTex2 triangle[3];
//! Material
Gfx::Material material;
- //! Render state (TODO: ?)
+ //! Render state
int state;
//! 1st texture
- Gfx::Texture tex1;
+ std::string tex1Name;
//! 2nd texture
- Gfx::Texture tex2;
+ std::string tex2Name;
EngineTriangle()
{
- state = 0;
+ state = Gfx::ENG_RSTATE_NORMAL;
}
};
@@ -115,6 +173,8 @@ enum EngineObjectType
\brief Object drawn by the graphics engine */
struct EngineObject
{
+ //! If true, object is valid in objects vector
+ bool used;
//! If true, the object is drawn
bool visible;
//! If true, object is behind the 2D interface
@@ -127,7 +187,7 @@ struct EngineObject
Gfx::EngineObjectType type;
//! Transformation matrix
Math::Matrix transform;
- //! Distance view - origin (TODO: ?)
+ //! Distance to object from eye point
float distance;
//! Bounding box min (origin 0,0,0 always included)
Math::Vector bboxMin;
@@ -140,15 +200,27 @@ struct EngineObject
//! Transparency of the object [0, 1]
float transparency;
+ //! Calls LoadDefault()
EngineObject()
{
+ LoadDefault();
+ }
+
+ //! Loads default values
+ inline void LoadDefault()
+ {
+ used = false;
visible = false;
drawWorld = false;
drawFront = false;
totalTriangles = 0;
+ type = Gfx::ENG_OBJTYPE_NULL;
+ transform.LoadIdentity();
+ bboxMax.LoadZero();
+ bboxMin.LoadZero();
distance = 0.0f;
radius = 0.0f;
- shadowRank = 0;
+ shadowRank = -1;
transparency = 0.0f;
}
};
@@ -157,31 +229,22 @@ struct EngineObjLevel1;
struct EngineObjLevel2;
struct EngineObjLevel3;
struct EngineObjLevel4;
-struct EngineObjLevel5;
-
-/**
- \struct EngineObjLevel5
- \brief Tier 5 of object tree */
-struct EngineObjLevel5
-{
- Gfx::Material material;
- int state;
- Gfx::EngineTriangleType type;
- std::vector<Gfx::VertexTex2> vertices;
-
- EngineObjLevel5();
-};
/**
\struct EngineObjLevel4
\brief Tier 4 of object tree */
struct EngineObjLevel4
{
- int reserved;
- std::vector<Gfx::EngineObjLevel5> up;
- Gfx::EngineObjLevel3* down;
+ bool used;
+ Gfx::EngineTriangleType type;
+ Gfx::Material material;
+ int state;
+ std::vector<Gfx::VertexTex2> vertices;
- EngineObjLevel4();
+ EngineObjLevel4(bool used = false,
+ Gfx::EngineTriangleType type = Gfx::ENG_TRIANGLE_TYPE_TRIANGLES,
+ const Gfx::Material& material = Gfx::Material(),
+ int state = Gfx::ENG_RSTATE_NORMAL);
};
/**
@@ -189,12 +252,12 @@ struct EngineObjLevel4
\brief Tier 3 of object tree */
struct EngineObjLevel3
{
+ bool used;
float min;
float max;
- std::vector<Gfx::EngineObjLevel4> up;
- Gfx::EngineObjLevel2* down;
+ std::vector<Gfx::EngineObjLevel4> next;
- EngineObjLevel3();
+ EngineObjLevel3(bool used = false, float min = 0.0f, float max = 0.0f);
};
/**
@@ -202,11 +265,11 @@ struct EngineObjLevel3
\brief Tier 2 of object tree */
struct EngineObjLevel2
{
+ bool used;
int objRank;
- std::vector<Gfx::EngineObjLevel3> up;
- Gfx::EngineObjLevel1* down;
+ std::vector<Gfx::EngineObjLevel3> next;
- EngineObjLevel2();
+ EngineObjLevel2(bool used = false, int objRank = -1);
};
/**
@@ -214,11 +277,15 @@ struct EngineObjLevel2
\brief Tier 1 of object tree */
struct EngineObjLevel1
{
+ bool used;
+ std::string tex1Name;
Gfx::Texture tex1;
+ std::string tex2Name;
Gfx::Texture tex2;
- std::vector<Gfx::EngineObjLevel2> up;
+ std::vector<Gfx::EngineObjLevel2> next;
- EngineObjLevel1();
+ EngineObjLevel1(bool used = false, const std::string& tex1Name = "",
+ const std::string& tex2Name = "");
};
/**
@@ -237,6 +304,8 @@ enum EngineShadowType
\brief Shadow drawn by the graphics engine */
struct EngineShadow
{
+ //! If true, shadow is valid
+ bool used;
//! If true, shadow is invisible (object being carried for example)
bool hide;
//! Rank of the associated object
@@ -258,8 +327,17 @@ struct EngineShadow
EngineShadow()
{
+ LoadDefault();
+ }
+
+ void LoadDefault()
+ {
+ used = false;
hide = false;
objRank = 0;
+ type = Gfx::ENG_SHADOW_NORM;
+ pos.LoadZero();
+ normal.LoadZero();
angle = radius = intensity = height = 0.0f;
}
};
@@ -269,6 +347,8 @@ struct EngineShadow
\brief A spot (large shadow) drawn on the ground by the graphics engine */
struct EngineGroundSpot
{
+ //! If true, ground spot is valid
+ bool used;
//! Color of the shadow
Gfx::Color color;
//! Min altitude
@@ -288,6 +368,15 @@ struct EngineGroundSpot
EngineGroundSpot()
{
+ LoadDefault();
+ }
+
+ void LoadDefault()
+ {
+ used = false;
+ color = Gfx::Color();
+ pos.LoadZero();
+ drawPos.LoadZero();
min = max = smooth = radius = drawRadius = 0.0f;
}
};
@@ -297,12 +386,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
};
/**
@@ -339,11 +430,19 @@ struct EngineGroundMark
EngineGroundMark()
{
+ LoadDefault();
+ }
+
+ void LoadDefault()
+ {
draw = false;
+ phase = ENG_GR_MARK_PHASE_NULL;
+ pos = Math::Vector();
+ drawPos = Math::Vector();
delay[0] = delay[1] = delay[2] = 0.0f;
fix = radius = intensity = drawRadius = drawIntensity = 0.0f;
dx = dy = 0;
- table = NULL;
+ table = nullptr;
}
};
@@ -363,55 +462,6 @@ enum EngineTextureMapping
/**
- \enum EngineRenderState
- \brief Render state of graphics engine
-
- States are used for settings certain modes, for instance texturing and blending.
- The enum is a bitmask and some of the states can be OR'd together. */
-enum EngineRenderState
-{
- //! Normal opaque materials
- ENG_RSTATE_NORMAL = 0,
- //! The transparent texture (black = no)
- ENG_RSTATE_TTEXTURE_BLACK = (1<<0),
- //! The transparent texture (white = no)
- ENG_RSTATE_TTEXTURE_WHITE = (1<<1),
- //! The transparent diffuse color
- ENG_RSTATE_TDIFFUSE = (1<<2),
- //! Texture wrap
- ENG_RSTATE_WRAP = (1<<3),
- //! Texture borders with solid color
- ENG_RSTATE_CLAMP = (1<<4),
- //! Light texture (ambient max)
- ENG_RSTATE_LIGHT = (1<<5),
- //! Double black texturing
- ENG_RSTATE_DUAL_BLACK = (1<<6),
- //! Double white texturing
- ENG_RSTATE_DUAL_WHITE = (1<<7),
- //! Part 1 (no change in. MOD!)
- ENG_RSTATE_PART1 = (1<<8),
- //! Part 2
- ENG_RSTATE_PART2 = (1<<9),
- //! Part 3
- ENG_RSTATE_PART3 = (1<<10),
- //! Part 4
- ENG_RSTATE_PART4 = (1<<11),
- //! Double-sided face
- ENG_RSTATE_2FACE = (1<<12),
- //! Image using alpha channel
- ENG_RSTATE_ALPHA = (1<<13),
- //! Always use 2nd floor texturing
- ENG_RSTATE_SECOND = (1<<14),
- //! Causes the fog
- ENG_RSTATE_FOG = (1<<15),
- //! The transparent color (black = no)
- ENG_RSTATE_TCOLOR_BLACK = (1<<16),
- //! The transparent color (white = no)
- ENG_RSTATE_TCOLOR_WHITE = (1<<17)
-};
-
-
-/**
\enum EngineMouseType
\brief Type of mouse cursor displayed in-game */
enum EngineMouseType
@@ -501,7 +551,7 @@ struct EngineMouse
The 3D scene is composed of objects which are basically collections of triangles forming
a surface or simply independent triangles in space. Objects are stored in the engine
- as a tree structure which is composed of 5 tiers (EngineObjLevel1, EngineObjLevel2 and so on).
+ as a tree structure which is composed of 4 tiers (EngineObjLevel1, EngineObjLevel2 and so on).
Each tier stores some data about object triangle, like textures or materials used.
Additional information on objects stored are in EngineObject structure.
Each object is uniquely identified by its rank.
@@ -511,354 +561,592 @@ 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);
+
+ //! 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);
- //! Renders a single frame
- bool Render();
+ //! Writes a screenshot containing the current frame
+ bool WriteScreenShot(const std::string& fileName, int width, int height);
- bool WriteProfile();
+ //! 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();
+ //! Creates a new object and returns its rank
int CreateObject();
+ //! Deletes all objects, shadows and ground spots
void FlushObject();
+ //! Deletes the given object
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,
- 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,
- int state, std::string texName1, std::string texName2,
+
+ //@{
+ //! Management of engine object type
+ bool SetObjectType(int objRank, Gfx::EngineObjectType type);
+ Gfx::EngineObjectType GetObjectType(int objRank);
+ //@}
+
+ //@{
+ //! Management of object transform
+ bool SetObjectTransform(int objRank, const Math::Matrix& transform);
+ bool GetObjectTransform(int objRank, Math::Matrix& transform);
+ //@}
+
+ //! Sets drawWorld for given object
+ bool SetObjectDrawWorld(int objRank, bool draw);
+ //! Sets drawFront for given object
+ bool SetObjectDrawFront(int objRank, bool draw);
+
+ //! Sets the transparency level for given object
+ bool SetObjectTransparency(int objRank, float value);
+
+ //! Returns the bounding box for an object
+ bool GetObjectBBox(int objRank, Math::Vector& min, Math::Vector& max);
+
+ //! Returns the total number of triangles of given object
+ int GetObjectTotalTriangles(int objRank);
+
+ //! Adds triangles to given object with the specified params
+ bool AddTriangles(int objRank, const std::vector<Gfx::VertexTex2>& vertices,
+ const Gfx::Material& material, int state,
+ std::string tex1Name, std::string tex2Name,
+ float min, float max, bool globalUpdate);
+
+ //! Adds a surface to given object with the specified params
+ bool AddSurface(int objRank, const std::vector<Gfx::VertexTex2>& vertices,
+ const Gfx::Material& material, int state,
+ std::string tex1Name, std::string tex2Name,
float min, float max, bool globalUpdate);
- bool AddQuick(int objRank, Gfx::EngineObjLevel5* buffer,
- std::string texName1, std::string texName2,
+
+ //! Adds a tier 4 engine object directly
+ bool AddQuick(int objRank, const Gfx::EngineObjLevel4& buffer,
+ std::string tex1Name, std::string tex2Name,
float min, float max, bool globalUpdate);
- Gfx::EngineObjLevel5* SearchTriangle(int objRank, const Gfx::Material &mat,
- int state, std::string texName1, std::string texName2,
- float min, float max);
+ //! Returns the first found tier 4 engine object for the given params or nullptr if not found
+ Gfx::EngineObjLevel4* FindTriangles(int objRank, const Gfx::Material& material,
+ int state, std::string tex1Name, std::string tex2Name,
+ float min, float max);
+
+ //! Returns a partial list of triangles for given object
+ int GetPartialTriangles(int objRank, float min, float max, float percent, int maxCount,
+ std::vector<Gfx::EngineTriangle>& triangles);
+
+ //! Updates LOD after parameter or resolution change
void ChangeLOD();
- bool ChangeSecondTexture(int objRank, char* 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,
+
+ //! Changes the 2nd texure for given object
+ bool ChangeSecondTexture(int objRank, const std::string& tex2Name);
+
+ //! Changes (recalculates) texture mapping for given object
+ bool ChangeTextureMapping(int objRank, const Gfx::Material& mat, int state,
+ const std::string& tex1Name, const std::string& tex2Name,
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,
+
+ //! Changes texture mapping for robot tracks
+ bool TrackTextureMapping(int objRank, const Gfx::Material& mat, int state,
+ const std::string& tex1Name, const std::string& tex2Name,
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 SetObjectType(int objRank, Gfx::EngineObjectType type);
- Gfx::EngineObjectType GetObjectType(int objRank);
- bool SetObjectTransparency(int objRank, float value);
- bool ShadowCreate(int objRank);
- void ShadowDelete(int objRank);
+
+ //! Creates a shadow for the given object
+ bool CreateShadow(int objRank);
+ //! Deletes the shadow for given object
+ void DeleteShadow(int objRank);
+
+ //@{
+ //! Management of different shadow params
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& normal);
bool SetObjectShadowAngle(int objRank, float angle);
bool SetObjectShadowRadius(int objRank, float radius);
bool SetObjectShadowIntensity(int objRank, float intensity);
- bool SetObjectShadowHeight(int objRank, float h);
+ bool SetObjectShadowHeight(int objRank, float height);
float GetObjectShadowRadius(int objRank);
-
- void GroundSpotFlush();
- int GroundSpotCreate();
- void GroundSpotDelete(int rank);
- bool SetObjectGroundSpotPos(int rank, const Math::Vector &pos);
+ //@}
+
+ //! Lists the ranks of objects and subobjects selected
+ void SetHighlightRank(int* rankList);
+ //! Returns the highlighted rectangle
+ bool GetHighlight(Math::Point& p1, Math::Point& p2);
+
+ //! Deletes all ground spots
+ void FlushGroundSpot();
+ //! Creates a new ground spot and returns its rank
+ int CreateGroundSpot();
+ //! Deletes the given ground spot
+ void DeleteGroundSpot(int rank);
+
+ //@{
+ //! Management of different ground spot params
+ 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);
+ //@}
- int GroundMarkCreate(Math::Vector pos, float radius,
+ //! Creates the ground mark with the given params
+ void CreateGroundMark(Math::Vector pos, float radius,
float delay1, float delay2, float delay3,
int dx, int dy, char* table);
- bool GroundMarkDelete(int rank);
+ //! Deletes the ground mark
+ void DeleteGroundMark(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 *************** */
- bool LoadTexture(const std::string &name, int stage = 0);
+ //! 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);
+
+ //! Loads texture, creating it if not already present
+ Gfx::Texture LoadTexture(const std::string& name);
+ //! Loads texture, creating it with given params if not already present
+ Gfx::Texture LoadTexture(const std::string& name, const Gfx::TextureCreateParams& params);
+ //! Loads all necessary textures
bool LoadAllTextures();
+ //! Sets texture for given stage; if not present in cache, the texture is loaded
+ /** If loading fails, returns false. */
+ bool SetTexture(const std::string& name, int stage = 0);
+ //! Sets texture for given stage
+ void SetTexture(const Gfx::Texture& tex, int stage = 0);
+
+ //! Deletes the given texture, unloading it and removing from cache
+ void DeleteTexture(const std::string& name);
+ //! Deletes the given texture, unloading it and removing from cache
+ void DeleteTexture(const Gfx::Texture& tex);
+
+ //@{
+ //! 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);
- 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);
+ //! Resets the projection matrix after changes
+ void UpdateMatProj();
-protected:
+ //! Updates the scene after a change of parameters
+ void ApplyChange();
- void SetUp3DView();
- bool Draw3DScene();
+protected:
+ //! Prepares the interface for 3D scene
+ void Draw3DScene();
+ //! Draws the user interface over the scene
+ void DrawInterface();
- 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 Gfx::Texture &tex);
+ //! 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);*/
+ //! Creates new tier 1 object
+ Gfx::EngineObjLevel1& AddLevel1(const std::string& tex1Name, const std::string& tex2Name);
+ //! Creates a new tier 2 object
+ Gfx::EngineObjLevel2& AddLevel2(Gfx::EngineObjLevel1 &p1, int objRank);
+ //! Creates a new tier 3 object
+ Gfx::EngineObjLevel3& AddLevel3(Gfx::EngineObjLevel2 &p2, float min, float max);
+ //! Creates a new tier 4 object
+ Gfx::EngineObjLevel4& AddLevel4(Gfx::EngineObjLevel3 &p3, Gfx::EngineTriangleType type,
+ const Gfx::Material& mat, int state);
+ //! Create texture and add it to cache
+ Gfx::Texture CreateTexture(const std::string &texName, const Gfx::TextureCreateParams &params);
+
+ //! 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 geometric parameters of objects (bounding box and radius)
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 +1157,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 +1217,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;
@@ -936,21 +1226,25 @@ protected:
bool m_fog;
bool m_firstGroundSpot;
int m_secondTexNum;
+ bool m_backgroundFull;
+ bool m_backgroundQuarter;
std::string m_backgroundName;
+ std::string m_backgroundQuarterNames[4];
+ Gfx::Texture m_backgroundFullTex;
+ Gfx::Texture m_backgroundQuarterTexs[4];
Gfx::Color m_backgroundColorUp;
Gfx::Color m_backgroundColorDown;
Gfx::Color m_backgroundCloudUp;
Gfx::Color m_backgroundCloudDown;
- bool m_backgroundFull;
- bool m_backgroundQuarter;
bool m_overFront;
Gfx::Color m_overColor;
int m_overMode;
- std::string m_frontsizeName;
+ std::string m_foregroundName;
+ Gfx::Texture m_foregroundTex;
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 +1263,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..b6b111a 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()->Trace("CLightning::CLightning() stub!\n");
+ // TODO!
+}
+
+Gfx::CLightning::~CLightning()
+{
+ GetLogger()->Trace("CLightning::~CLightning() stub!\n");
+ // TODO!
+}
+
+void Gfx::CLightning::Flush()
+{
+ GetLogger()->Trace("CLightning::Flush() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CLightning::EventProcess(const Event &event)
+{
+ GetLogger()->Trace("CLightning::EventProcess() stub!\n");
+ // TODO!
+ return true;
+}
+
+bool Gfx::CLightning::Create(float sleep, float delay, float magnetic)
+{
+ GetLogger()->Trace("CLightning::Create() stub!\n");
+ // TODO!
+ return true;
+}
+
+bool Gfx::CLightning::GetStatus(float &sleep, float &delay, float &magnetic, float &progress)
+{
+ GetLogger()->Trace("CLightning::GetStatus() stub!\n");
+ // TODO!
+ return true;
+}
+
+bool Gfx::CLightning::SetStatus(float sleep, float delay, float magnetic, float progress)
+{
+ GetLogger()->Trace("CLightning::SetStatus() stub!\n");
+ // TODO!
+ return true;
+}
+
+void Gfx::CLightning::Draw()
+{
+ GetLogger()->Trace("CLightning::Draw() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CLightning::EventFrame(const Event &event)
+{
+ GetLogger()->Trace("CLightning::EventFrame() stub!\n");
+ // TODO!
+ return true;
+}
+
+CObject* Gfx::CLightning::SearchObject(Math::Vector pos)
+{
+ GetLogger()->Trace("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..37a3975 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()->Trace("CParticle::CParticle() stub!\n");
+ // TODO!
+}
+
+Gfx::CParticle::~CParticle()
+{
+ GetLogger()->Trace("CParticle::~CParticle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetDevice(Gfx::CDevice* device)
+{
+ GetLogger()->Trace("CParticle::SetDevice() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::FlushParticle()
+{
+ GetLogger()->Trace("CParticle::FlushParticle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::FlushParticle(int sheet)
+{
+ GetLogger()->Trace("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()->Trace("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()->Trace("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()->Trace("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()->Trace("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()->Trace("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()->Trace("CParticle::CreateWheelTrace() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DeleteParticle(Gfx::ParticleType type)
+{
+ GetLogger()->Trace("CParticle::DeleteParticle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DeleteParticle(int channel)
+{
+ GetLogger()->Trace("CParticle::DeleteParticle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetObjectLink(int channel, CObject *object)
+{
+ GetLogger()->Trace("CParticle::SetObjectLink() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetObjectFather(int channel, CObject *object)
+{
+ GetLogger()->Trace("CParticle::SetObjectFather() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetPosition(int channel, Math::Vector pos)
+{
+ GetLogger()->Trace("CParticle::SetPosition() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetDimension(int channel, Math::Point dim)
+{
+ GetLogger()->Trace("CParticle::SetDimension() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetZoom(int channel, float zoom)
+{
+ GetLogger()->Trace("CParticle::SetZoom() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetAngle(int channel, float angle)
+{
+ GetLogger()->Trace("CParticle::SetAngle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetIntensity(int channel, float intensity)
+{
+ GetLogger()->Trace("CParticle::SetIntensity() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetParam(int channel, Math::Vector pos, Math::Point dim, float zoom, float angle, float intensity)
+{
+ GetLogger()->Trace("CParticle::SetParam() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::SetPhase(int channel, Gfx::ParticlePhase phase, float duration)
+{
+ GetLogger()->Trace("CParticle::SetPhase() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CParticle::GetPosition(int channel, Math::Vector &pos)
+{
+ GetLogger()->Trace("CParticle::GetPosition() stub!\n");
+ // TODO!
+ return true;
+}
+
+Gfx::Color Gfx::CParticle::GetFogColor(Math::Vector pos)
+{
+ GetLogger()->Trace("CParticle::GetFogColor() stub!\n");
+ // TODO!
+ return Gfx::Color();
+}
+
+void Gfx::CParticle::SetFrameUpdate(int sheet, bool update)
+{
+ GetLogger()->Trace("CParticle::SetFrameUpdate() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::FrameParticle(float rTime)
+{
+ GetLogger()->Trace("CParticle::FrameParticle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticle(int sheet)
+{
+ GetLogger()->Trace("CParticle::DrawParticle() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CParticle::WriteWheelTrace(char *filename, int width, int height, Math::Vector dl, Math::Vector ur)
+{
+ GetLogger()->Trace("CParticle::WriteWheelTrace() stub!\n");
+ // TODO!
+ return true;
+}
+
+void Gfx::CParticle::DeleteRank(int rank)
+{
+ GetLogger()->Trace("CParticle::DeleteRank() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CParticle::CheckChannel(int &channel)
+{
+ GetLogger()->Trace("CParticle::CheckChannel() stub!\n");
+ // TODO!
+ return true;
+}
+
+void Gfx::CParticle::DrawParticleTriangle(int i)
+{
+ GetLogger()->Trace("CParticle::DrawParticleTriangle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleNorm(int i)
+{
+ GetLogger()->Trace("CParticle::DrawParticleNorm() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleFlat(int i)
+{
+ GetLogger()->Trace("CParticle::DrawParticleFlat() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleFog(int i)
+{
+ GetLogger()->Trace("CParticle::DrawParticleFog() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleRay(int i)
+{
+ GetLogger()->Trace("CParticle::DrawParticleRay() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleSphere(int i)
+{
+ GetLogger()->Trace("CParticle::DrawParticleSphere() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleCylinder(int i)
+{
+ GetLogger()->Trace("CParticle::DrawParticleCylinder() stub!\n");
+ // TODO!
+}
+
+void Gfx::CParticle::DrawParticleWheel(int i)
+{
+ GetLogger()->Trace("CParticle::DrawParticleWheel() stub!\n");
+ // TODO!
+}
+
+CObject* Gfx::CParticle::SearchObjectGun(Math::Vector old, Math::Vector pos, Gfx::ParticleType type, CObject *father)
+{
+ GetLogger()->Trace("CParticle::SearchObjectGun() stub!\n");
+ // TODO!
+ return nullptr;
+}
+
+CObject* Gfx::CParticle::SearchObjectRay(Math::Vector pos, Math::Vector goal, Gfx::ParticleType type, CObject *father)
+{
+ GetLogger()->Trace("CParticle::SearchObjectRay() stub!\n");
+ // TODO!
+ return nullptr;
+}
+
+void Gfx::CParticle::Play(Sound sound, Math::Vector pos, float amplitude)
+{
+ GetLogger()->Trace("CParticle::Play() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CParticle::TrackMove(int i, Math::Vector pos, float progress)
+{
+ GetLogger()->Trace("CParticle::TrackMove() stub!\n");
+ // TODO!
+ return true;
+}
+
+void Gfx::CParticle::TrackDraw(int i, Gfx::ParticleType type)
+{
+ GetLogger()->Trace("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..e859cd7 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,71 @@ 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;
+
+ // TODO: make all textures transparent?
+ //! 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..c97131e 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()->Trace("CParticle::CPyro() stub!\n");
+ // TODO!
+}
+
+Gfx::CPyro::~CPyro()
+{
+ GetLogger()->Trace("CPyro::~CPyro() stub!");
+ // TODO!
+}
+
+void Gfx::CPyro::DeleteObject(bool all)
+{
+ GetLogger()->Trace("CPyro::DeleteObject() stub!");
+ // TODO!
+}
+
+bool Gfx::CPyro::Create(Gfx::PyroType type, CObject* pObj, float force)
+{
+ GetLogger()->Trace("CPyro::Create() stub!");
+ // TODO!
+ return true;
+}
+
+bool Gfx::CPyro::EventProcess(const Event &event)
+{
+ GetLogger()->Trace("CPyro::EventProcess() stub!\n");
+ // TODO!
+ return true;
+}
+
+Error Gfx::CPyro::IsEnded()
+{
+ GetLogger()->Trace("CPyro::IsEnded() stub!\n");
+ // TODO!
+ return ERR_OK;
+}
+
+void Gfx::CPyro::CutObjectLink(CObject* pObj)
+{
+ GetLogger()->Trace("CPyro::CutObjectLink() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::DisplayError(PyroType type, CObject* pObj)
+{
+ GetLogger()->Trace("CPyro::DisplayError() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CPyro::CreateLight(Math::Vector pos, float height)
+{
+ GetLogger()->Trace("CPyro::CreateLight() stub!\n");
+ // TODO!
+ return true;
+}
+
+void Gfx::CPyro::DeleteObject(bool primary, bool secondary)
+{
+ GetLogger()->Trace("CPyro::DeleteObject() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::CreateTriangle(CObject* pObj, ObjectType oType, int part)
+{
+ GetLogger()->Trace("CPyro::CreateTriangle() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::ExploStart()
+{
+ GetLogger()->Trace("CPyro::ExploStart() stub!\n");
+ // TODO!
+}
+void Gfx::CPyro::ExploTerminate()
+{
+ GetLogger()->Trace("CPyro::ExploTerminate() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::BurnStart()
+{
+ GetLogger()->Trace("CPyro::BurnStart() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::BurnAddPart(int part, Math::Vector pos, Math::Vector angle)
+{
+ GetLogger()->Trace("CPyro::BurnAddPart() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::BurnProgress()
+{
+ GetLogger()->Trace("CPyro::BurnProgress() stub!\n");
+ // TODO!
+}
+
+bool Gfx::CPyro::BurnIsKeepPart(int part)
+{
+ GetLogger()->Trace("CPyro::BurnIsKeepPart() stub!\n");
+ // TODO!
+ return true;
+}
+
+void Gfx::CPyro::BurnTerminate()
+{
+ GetLogger()->Trace("CPyro::BurnTerminate() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::FallStart()
+{
+ GetLogger()->Trace("CPyro::FallStart() stub!\n");
+ // TODO!
+}
+
+CObject* Gfx::CPyro::FallSearchBeeExplo()
+{
+ GetLogger()->Trace("CPyro::FallSearchBeeExplo() stub!\n");
+ // TODO!
+ return nullptr;
+}
+
+void Gfx::CPyro::FallProgress(float rTime)
+{
+ GetLogger()->Trace("CPyro::FallProgress() stub!\n");
+ // TODO!
+}
+
+Error Gfx::CPyro::FallIsEnded()
+{
+ GetLogger()->Trace("CPyro::FallIsEnded() stub!\n");
+ // TODO!
+ return ERR_OK;
+}
+
+void Gfx::CPyro::LightOperFlush()
+{
+ GetLogger()->Trace("CPyro::LightOperFlush() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::LightOperAdd(float progress, float intensity, float r, float g, float b)
+{
+ GetLogger()->Trace("CPyro::LightOperAdd() stub!\n");
+ // TODO!
+}
+
+void Gfx::CPyro::LightOperFrame(float rTime)
+{
+ GetLogger()->Trace("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..6b26281 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_levelMats.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_levelMats.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_levelMats.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::EngineObjLevel4 buffer;
+ buffer.vertices.reserve(total);
+
+ buffer.type = Gfx::ENG_TRIANGLE_TYPE_SURFACE;
+ 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_levelMats.size() ); i++)
+ {
+ if (id == m_levelMats[i].id)
+ return &m_levelMats[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_levelDots[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_levelMats or -1 if there is not.
+ m_levelMats[i].id gives the identifier. */
+int Gfx::CTerrain::LevelTestMat(char *mat)
+{
+ for (int i = 0; i < static_cast<int>( m_levelMats.size() ); i++)
+ {
+ if ( m_levelMats[i].mat[0] == mat[0] &&
+ m_levelMats[i].mat[1] == mat[1] &&
+ m_levelMats[i].mat[2] == mat[2] &&
+ m_levelMats[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_levelMats[ii].id; // looking for a id compatible with mat
+ }
+
+ // Changes the point
+ m_levelDots[x+y*m_levelDotSize].id = id;
+ m_levelDots[x+y*m_levelDotSize].mat[0] = mat[0];
+ m_levelDots[x+y*m_levelDotSize].mat[1] = mat[1];
+ m_levelDots[x+y*m_levelDotSize].mat[2] = mat[2];
+ m_levelDots[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_levelDots[i].mat[0] != mat[2])
+ {
+ m_levelDots[i].mat[0] = mat[2];
+ int ii = LevelTestMat(m_levelDots[i].mat);
+ if (ii != -1)
+ m_levelDots[i].id = m_levelMats[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_levelDots[i].mat[1] != mat[3])
+ {
+ m_levelDots[i].mat[1] = mat[3];
+ int ii = LevelTestMat(m_levelDots[i].mat);
+ if (ii != -1)
+ m_levelDots[i].id = m_levelMats[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_levelDots[i].mat[2] != mat[0])
+ {
+ m_levelDots[i].mat[2] = mat[0];
+ int ii = LevelTestMat(m_levelDots[i].mat);
+ if (ii != -1)
+ m_levelDots[i].id = m_levelMats[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_levelDots[i].mat[3] != mat[1] )
+ {
+ m_levelDots[i].mat[3] = mat[1];
+ int ii = LevelTestMat(m_levelDots[i].mat);
+ if (ii != -1)
+ m_levelDots[i].id = m_levelMats[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_levelDots[(x+0)+(y-1)*m_levelDotSize].mat[1];
+ test[2] = m_levelDots[(x+0)+(y-1)*m_levelDotSize].mat[2];
+ test[3] = m_levelDots[(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_levelDots[(x-1)+(y+0)*m_levelDotSize].mat[0];
+ test[1] = mat[3];
+ test[2] = m_levelDots[(x-1)+(y+0)*m_levelDotSize].mat[2];
+ test[3] = m_levelDots[(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_levelDots[(x+0)+(y+1)*m_levelDotSize].mat[0];
+ test[1] = m_levelDots[(x+0)+(y+1)*m_levelDotSize].mat[1];
+ test[2] = mat[0];
+ test[3] = m_levelDots[(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_levelDots[(x+1)+(y+0)*m_levelDotSize].mat[0];
+ test[1] = m_levelDots[(x+1)+(y+0)*m_levelDotSize].mat[1];
+ test[2] = m_levelDots[(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_levelDots[i].id = id;
+
+ for (int j = 0; j < 4; j++)
+ m_levelDots[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_levelDots.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_levelDots);
+
+ for (int i = 0; i < m_levelDotSize * m_levelDotSize; i++)
+ {
+ for (int j = 0; j < 4; j++)
+ m_levelDots[i].mat[j] = 0;
+ }
+}
+
+void Gfx::CTerrain::LevelCloseTable()
+{
+ m_levelDots.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_levelDots.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_levelDots[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->CreateGroundMark(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..0c7e3cf 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_levelMats;
+ std::vector<Gfx::DotLevel> m_levelDots;
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..19ef57b 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..2a7c6d2 100644
--- a/src/graphics/engine/water.cpp
+++ b/src/graphics/engine/water.cpp
@@ -19,5 +19,641 @@
#include "graphics/engine/water.h"
+#include "common/iman.h"
+#include "common/logger.h"
+#include "graphics/engine/engine.h"
+#include "graphics/engine/terrain.h"
+#include "object/object.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_lines.reserve(WATERLINE_PREALLOCATE_COUNT);
+
+ std::vector<Gfx::WaterVapor>(VAPOR_SIZE).swap(m_vapors);
+}
+
+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_vapors.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_vapors.clear();
+}
+
+bool Gfx::CWater::VaporCreate(Gfx::ParticleType type, Math::Vector pos, float delay)
+{
+ for (int i = 0; i < static_cast<int>( m_vapors.size() ); i++)
+ {
+ if (! m_vapors[i].used)
+ {
+ m_vapors[i].used = true;
+ m_vapors[i].type = type;
+ m_vapors[i].pos = pos;
+ m_vapors[i].delay = delay;
+ m_vapors[i].time = 0.0f;
+ m_vapors[i].last = 0.0f;
+
+ if (m_vapors[i].type == PARTIFIRE)
+ m_sound->Play(SOUND_BLUP, pos, 1.0f, 1.0f-Math::Rand()*0.5f);
+
+ if (m_vapors[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_vapors[i].time += rTime;
+
+ if (m_sound == nullptr)
+ m_sound = static_cast<CSoundInterface*>(m_iMan->SearchInstance(CLASS_SOUND));
+
+ if (m_vapors[i].time <= m_vapors[i].delay)
+ {
+ if (m_time-m_vapors[i].last >= m_engine->ParticleAdapt(0.02f))
+ {
+ m_vapors[i].last = m_time;
+
+ if (m_vapors[i].type == PARTIFIRE)
+ {
+ for (int j = 0; j < 10; j++)
+ {
+ Math::Vector pos = m_vapors[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_vapors[i].type == PARTIFLAME)
+ {
+ Math::Vector pos = m_vapors[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_vapors[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_vapors[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()
+{
+ GetLogger()->Trace("CWater::DrawBack(): stub!\n");
+ /* 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()
+{
+ GetLogger()->Trace("CWater::DrawSurf(): stub!\n");
+ /* 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_lines.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_lines.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)
+{
+ 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..f20f992 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_lines;
+ std::vector<WaterVapor> m_vapors;
- 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..3526b13 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,22 @@ 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
}
- result.valid = true;
- result.size.w = data->surface->w;
- result.size.h = data->surface->h;
+ return CreateTexture(data, params);
+}
+
+Gfx::Texture Gfx::CGLDevice::CreateTexture(ImageData *data, const Gfx::TextureCreateParams &params)
+{
+ Gfx::Texture result;
+
+ result.size.x = data->surface->w;
+ result.size.y = data->surface->h;
// Use & enable 1st texture stage
glActiveTexture(GL_TEXTURE0);
@@ -461,7 +462,7 @@ Gfx::Texture Gfx::CGLDevice::CreateTexture(CImage *image, const Gfx::TextureCrea
// Restore the previous state of 1st stage
- if (m_currentTextures[0].valid)
+ if (m_currentTextures[0].Valid())
glBindTexture(GL_TEXTURE_2D, m_currentTextures[0].id);
else
glBindTexture(GL_TEXTURE_2D, 0);
@@ -516,7 +517,7 @@ void Gfx::CGLDevice::SetTexture(int index, const Gfx::Texture &texture)
m_currentTextures[index] = texture; // remember the change
- if (! texture.valid)
+ if (! texture.Valid())
{
glBindTexture(GL_TEXTURE_2D, 0); // unbind texture
}
@@ -531,6 +532,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)
@@ -576,7 +595,7 @@ void Gfx::CGLDevice::SetTextureStageParams(int index, const Gfx::TextureStagePar
m_textureStageParams[index] = params;
// Don't actually do anything if texture not set
- if (! m_currentTextures[index].valid)
+ if (! m_currentTextures[index].Valid())
return;
// Enable the given stage
@@ -1159,17 +1178,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 7a8c3df..83eca52 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